diff --git a/Assets/dll/melonDS.wbx.zst b/Assets/dll/melonDS.wbx.zst index e3181ce8342..28a2f8887d7 100644 Binary files a/Assets/dll/melonDS.wbx.zst and b/Assets/dll/melonDS.wbx.zst differ diff --git a/src/BizHawk.Client.Common/Api/Classes/MemoryApi.cs b/src/BizHawk.Client.Common/Api/Classes/MemoryApi.cs index 9683945b313..89b82ec5eca 100644 --- a/src/BizHawk.Client.Common/Api/Classes/MemoryApi.cs +++ b/src/BizHawk.Client.Common/Api/Classes/MemoryApi.cs @@ -284,6 +284,74 @@ public void WriteByteRange(long addr, IReadOnlyList memoryblock, string do if (lastReqAddr >= d.Size) LogCallback($"Warning: Attempted writes on addresses {d.Size}..{lastReqAddr} outside range of domain {d.Name} in {nameof(WriteByteRange)}()"); } + public ushort[] ReadU16Range(long addr, int count, string domain = null) + { + var d = NamedDomainOrCurrent(domain); + if (addr < 0) LogCallback($"Warning: Attempted reads on addresses {addr}..-1 outside range of domain {d.Name} in {nameof(ReadByteRange)}()"); + var lastReqAddr = addr + count * sizeof(ushort) - 1; + var indexAfterLast = Math.Min(Math.Max(-1L, lastReqAddr), d.Size - 1L) + 1L; + var iSrc = Math.Min(Math.Max(0L, addr), d.Size); + var iDst = iSrc - addr; + var values = new ushort[(indexAfterLast - iSrc) / sizeof(ushort)]; + if (iSrc < indexAfterLast) using (d.EnterExit()) d.BulkPeekUshort(iSrc.RangeToExclusive(indexAfterLast), _isBigEndian, values); + if (lastReqAddr >= d.Size) LogCallback($"Warning: Attempted reads on addresses {d.Size}..{lastReqAddr} outside range of domain {d.Name} in {nameof(ReadByteRange)}()"); + if (values.Length == count) return values; + var newValues = new ushort[count]; + if (values.Length is not 0) Array.Copy(sourceArray: values, sourceIndex: 0, destinationArray: newValues, destinationIndex: iDst, length: values.Length); + return newValues; + } + + public void WriteU16Range(long addr, Span memoryblock, string domain = null) + { + var d = NamedDomainOrCurrent(domain); + if (!d.Writable) + { + LogCallback($"Error: the domain {d.Name} is not writable"); + return; + } + if (addr < 0) LogCallback($"Warning: Attempted writes on addresses {addr}..-1 outside range of domain {d.Name} in {nameof(WriteByteRange)}()"); + var lastReqAddr = addr + memoryblock.Length * sizeof(ushort) - 1; + var indexAfterLast = Math.Min(Math.Max(-1L, lastReqAddr), d.Size - 1L) + 1L; + var iDst = Math.Min(Math.Max(0L, addr), d.Size); + var iSrc = checked((int) (iDst - addr)); + if (iDst < indexAfterLast) using (d.EnterExit()) d.BulkPokeUshort(iDst, _isBigEndian, memoryblock.Slice(iSrc)); + if (lastReqAddr >= d.Size) LogCallback($"Warning: Attempted writes on addresses {d.Size}..{lastReqAddr} outside range of domain {d.Name} in {nameof(WriteByteRange)}()"); + } + + public uint[] ReadU32Range(long addr, int count, string domain = null) + { + var d = NamedDomainOrCurrent(domain); + if (addr < 0) LogCallback($"Warning: Attempted reads on addresses {addr}..-1 outside range of domain {d.Name} in {nameof(ReadByteRange)}()"); + var lastReqAddr = addr + count * sizeof(uint) - 1; + var indexAfterLast = Math.Min(Math.Max(-1L, lastReqAddr), d.Size - 1L) + 1L; + var iSrc = Math.Min(Math.Max(0L, addr), d.Size); + var iDst = iSrc - addr; + var values = new uint[(indexAfterLast - iSrc) / sizeof(uint)]; + if (iSrc < indexAfterLast) using (d.EnterExit()) d.BulkPeekUint(iSrc.RangeToExclusive(indexAfterLast), _isBigEndian, values); + if (lastReqAddr >= d.Size) LogCallback($"Warning: Attempted reads on addresses {d.Size}..{lastReqAddr} outside range of domain {d.Name} in {nameof(ReadByteRange)}()"); + if (values.Length == count) return values; + var newValues = new uint[count]; + if (values.Length is not 0) Array.Copy(sourceArray: values, sourceIndex: 0, destinationArray: newValues, destinationIndex: iDst, length: values.Length); + return newValues; + } + + public void WriteU32Range(long addr, Span memoryblock, string domain = null) + { + var d = NamedDomainOrCurrent(domain); + if (!d.Writable) + { + LogCallback($"Error: the domain {d.Name} is not writable"); + return; + } + if (addr < 0) LogCallback($"Warning: Attempted writes on addresses {addr}..-1 outside range of domain {d.Name} in {nameof(WriteByteRange)}()"); + var lastReqAddr = addr + memoryblock.Length * sizeof(uint) - 1; + var indexAfterLast = Math.Min(Math.Max(-1L, lastReqAddr), d.Size - 1L) + 1L; + var iDst = Math.Min(Math.Max(0L, addr), d.Size); + var iSrc = checked((int) (iDst - addr)); + if (iDst < indexAfterLast) using (d.EnterExit()) d.BulkPokeUint(iDst, _isBigEndian, memoryblock.Slice(iSrc)); + if (lastReqAddr >= d.Size) LogCallback($"Warning: Attempted writes on addresses {d.Size}..{lastReqAddr} outside range of domain {d.Name} in {nameof(WriteByteRange)}()"); + } + public float ReadFloat(long addr, string domain = null) { var d = NamedDomainOrCurrent(domain); diff --git a/src/BizHawk.Client.Common/Api/Interfaces/IMemoryApi.cs b/src/BizHawk.Client.Common/Api/Interfaces/IMemoryApi.cs index ae644937a04..b081e6d7253 100644 --- a/src/BizHawk.Client.Common/Api/Interfaces/IMemoryApi.cs +++ b/src/BizHawk.Client.Common/Api/Interfaces/IMemoryApi.cs @@ -33,6 +33,11 @@ public interface IMemoryApi : IExternalApi void WriteByteRange(long addr, IReadOnlyList memoryblock, string domain = null); void WriteFloat(long addr, float value, string domain = null); + ushort[] ReadU16Range(long addr, int count, string domain = null); + uint[] ReadU32Range(long addr, int count, string domain = null); + void WriteU16Range(long addr, Span memoryblock, string domain = null); + void WriteU32Range(long addr, Span memoryblock, string domain = null); + void WriteS8(long addr, int value, string domain = null); void WriteS16(long addr, int value, string domain = null); void WriteS24(long addr, int value, string domain = null); diff --git a/src/BizHawk.Client.Common/lua/CommonLibs/MemoryLuaLibrary.cs b/src/BizHawk.Client.Common/lua/CommonLibs/MemoryLuaLibrary.cs index d86667b435b..adfbfed538e 100644 --- a/src/BizHawk.Client.Common/lua/CommonLibs/MemoryLuaLibrary.cs +++ b/src/BizHawk.Client.Common/lua/CommonLibs/MemoryLuaLibrary.cs @@ -129,6 +129,38 @@ public void WriteBytesAsDict(LuaTable addrMap, string domain = null) } } + [LuaMethodExample("local values = memory.read_u16_le_as_array(0x100, 30, \"WRAM\");")] + [LuaMethod("read_u16_le_as_array", "Reads count 16-bit values starting at addr into an array-like table (1-indexed).")] + public LuaTable ReadUshortsAsArray(long addr, int count, string domain = null) + { + APIs.Memory.SetBigEndian(false); + return _th.ListToTable(APIs.Memory.ReadU16Range(addr, count, domain)); + } + + [LuaMethodExample("memory.write_u16_le_as_array(0x100, { 0xABCD, 0x1234 });")] + [LuaMethod("write_u16_le_as_array", "Writes sequential 16-byte values starting at addr.")] + public void WriteUshortsAsArray(long addr, LuaTable values, string domain = null) + { + APIs.Memory.SetBigEndian(false); + APIs.Memory.WriteU16Range(addr, _th.EnumerateValues(values).Select(l => (ushort) l).ToArray(), domain); + } + + [LuaMethodExample("local values = memory.read_u32_le_as_array(0x100, 30, \"WRAM\");")] + [LuaMethod("read_u32_le_as_array", "Reads count 32-bit values starting at addr into an array-like table (1-indexed).")] + public LuaTable ReadUintsAsArray(long addr, int count, string domain = null) + { + APIs.Memory.SetBigEndian(false); + return _th.ListToTable(APIs.Memory.ReadU32Range(addr, count, domain)); + } + + [LuaMethodExample("memory.write_u32_le_as_array(0x100, { 0xABCD, 0x1234 });")] + [LuaMethod("write_u32_le_as_array", "Writes sequential 32-byte values starting at addr.")] + public void WriteUintsAsArray(long addr, LuaTable values, string domain = null) + { + APIs.Memory.SetBigEndian(false); + APIs.Memory.WriteU32Range(addr, _th.EnumerateValues(values).Select(l => (uint) l).ToArray(), domain); + } + [LuaMethodExample(""" memory.write_bytes_as_binary_string(0x100, string.pack(" addresses, byte[] values) if (addresses is null) throw new ArgumentNullException(paramName: nameof(addresses)); if (values is null) throw new ArgumentNullException(paramName: nameof(values)); - if ((long)addresses.Count() != values.Length) - { - throw new InvalidOperationException("Invalid length of values array"); - } + if (addresses.Count() > (uint)values.Length) throw new ArgumentException($"Length of {nameof(values)} must be at least {nameof(addresses)} count.", nameof(values)); using (this.EnterExit()) { @@ -104,27 +101,46 @@ public virtual void BulkPeekByte(Range addresses, byte[] values) } } + public virtual void BulkPokeByte(long startAddress, Span values) + { + if (values == null) throw new ArgumentNullException(paramName: nameof(values)); + + using (this.EnterExit()) + { + long address = startAddress; + for (var i = 0; i < values.Length; i++, address++) + { + PokeByte(address, values[i]); + } + } + } + public virtual void BulkPeekUshort(Range addresses, bool bigEndian, ushort[] values) { if (addresses is null) throw new ArgumentNullException(paramName: nameof(addresses)); if (values is null) throw new ArgumentNullException(paramName: nameof(values)); - var start = addresses.Start; - var end = addresses.EndInclusive + 1; - - if ((start & 1) != 0 || (end & 1) != 0) - throw new InvalidOperationException("The API contract doesn't define what to do for unaligned reads and writes!"); + if (addresses.Count() > (uint)values.Length * sizeof(ushort)) throw new ArgumentException($"Length of {nameof(values)} must be at least {nameof(addresses)} count.", nameof(values)); - if (values.LongLength * 2 != end - start) + using (this.EnterExit()) { - // a longer array could be valid, but nothing needs that so don't support it for now - throw new InvalidOperationException("Invalid length of values array"); + long address = addresses.Start; + for (var i = 0; i < values.Length; i++, address += sizeof(ushort)) + values[i] = PeekUshort(address, bigEndian); } + } + + public virtual void BulkPokeUshort(long startAddress, bool bigEndian, Span values) + { + if (values == null) throw new ArgumentNullException(paramName: nameof(values)); using (this.EnterExit()) { - for (var i = 0; i < values.Length; i++, start += 2) - values[i] = PeekUshort(start, bigEndian); + long address = startAddress; + for (var i = 0; i < values.Length; i++, address += sizeof(ushort)) + { + PokeUshort(address, values[i], bigEndian); + } } } @@ -133,22 +149,27 @@ public virtual void BulkPeekUint(Range addresses, bool bigEndian, uint[] v if (addresses is null) throw new ArgumentNullException(paramName: nameof(addresses)); if (values is null) throw new ArgumentNullException(paramName: nameof(values)); - var start = addresses.Start; - var end = addresses.EndInclusive + 1; - - if ((start & 3) != 0 || (end & 3) != 0) - throw new InvalidOperationException("The API contract doesn't define what to do for unaligned reads and writes!"); + if (addresses.Count() > (uint)values.Length * sizeof(uint)) throw new ArgumentException($"Length of {nameof(values)} must be at least {nameof(addresses)} count.", nameof(values)); - if (values.LongLength * 4 != end - start) + using (this.EnterExit()) { - // a longer array could be valid, but nothing needs that so don't support it for now - throw new InvalidOperationException("Invalid length of values array"); + var address = addresses.Start; + for (var i = 0; i < values.Length; i++, address += sizeof(uint)) + values[i] = PeekUint(address, bigEndian); } + } + + public virtual void BulkPokeUint(long startAddress, bool bigEndian, Span values) + { + if (values == null) throw new ArgumentNullException(paramName: nameof(values)); using (this.EnterExit()) { - for (var i = 0; i < values.Length; i++, start += 4) - values[i] = PeekUint(start, bigEndian); + long address = startAddress; + for (var i = 0; i < values.Length; i++, address += sizeof(uint)) + { + PokeUint(address, values[i], bigEndian); + } } } diff --git a/src/BizHawk.Emulation.Cores/Waterbox/LibWaterboxCore.cs b/src/BizHawk.Emulation.Cores/Waterbox/LibWaterboxCore.cs index 072a0402d3e..2d3d3c33102 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/LibWaterboxCore.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/LibWaterboxCore.cs @@ -92,6 +92,10 @@ public enum MemoryDomainFlags : long /// If true, Data is a function to call and not a pointer /// FunctionHook = 1024, + /// + /// If true, Data is a list of functions; function(s) may be null + /// + SizedFunctionHooks = 2048, } [StructLayout(LayoutKind.Sequential)] diff --git a/src/BizHawk.Emulation.Cores/Waterbox/WaterboxMemoryDomain.cs b/src/BizHawk.Emulation.Cores/Waterbox/WaterboxMemoryDomain.cs index 04fabb50ac8..51e43cb00a3 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/WaterboxMemoryDomain.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/WaterboxMemoryDomain.cs @@ -18,9 +18,12 @@ public abstract class WaterboxMemoryDomain : MemoryDomain public static WaterboxMemoryDomain Create(MemoryArea m, WaterboxHost monitor) { - return m.Flags.HasFlag(MemoryDomainFlags.FunctionHook) - ? new WaterboxMemoryDomainFunc(m, monitor) - : new WaterboxMemoryDomainPointer(m, monitor); + if (m.Flags.HasFlag(MemoryDomainFlags.FunctionHook)) + return new WaterboxMemoryDomainFunc(m, monitor); + else if (m.Flags.HasFlag(MemoryDomainFlags.SizedFunctionHooks)) + return new WaterboxMemoryDomainSizedFuncs(m, monitor); + else + return new WaterboxMemoryDomainPointer(m, monitor); } protected WaterboxMemoryDomain(MemoryArea m, IMonitor monitor) @@ -149,6 +152,29 @@ public static MemoryDomainAccessStub Create(IntPtr p, WaterboxHost host) new StubResolver(p), host, CallingConventionAdapters.MakeWaterboxDepartureOnly(host)); } } + public abstract class MemoryDomainSizedAccessStub + { + [BizImport(CallingConvention.Cdecl)] + public abstract void Access(IntPtr buffer, long address, long count); + + 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 MemoryDomainSizedAccessStub Create(IntPtr p, WaterboxHost host) + { + return BizInvoker.GetInvoker( + new StubResolver(p), host, CallingConventionAdapters.MakeWaterboxDepartureOnly(host)); + } + } + public unsafe class WaterboxMemoryDomainFunc : WaterboxMemoryDomain { @@ -211,4 +237,147 @@ public override void BulkPeekByte(Range addresses, byte[] values) } } } + + /// + /// A memory domain for things like a system bus, where the size of the read/write may affect the result. + /// Endianness is ignored; we are writing numbers not multi-byte representations of numbers. + /// + public unsafe class WaterboxMemoryDomainSizedFuncs : WaterboxMemoryDomain + { + private readonly MemoryDomainSizedAccessStub/*?*/ _read8 = null; + private readonly MemoryDomainSizedAccessStub _write8 = null; + private readonly MemoryDomainSizedAccessStub _read16 = null; + private readonly MemoryDomainSizedAccessStub _write16 = null; + private readonly MemoryDomainSizedAccessStub _read32 = null; + private readonly MemoryDomainSizedAccessStub _write32 = null; + private readonly MemoryDomainSizedAccessStub _read64 = null; + private readonly MemoryDomainSizedAccessStub _write64 = null; + + internal WaterboxMemoryDomainSizedFuncs(MemoryArea m, WaterboxHost monitor) + : base(m, monitor) + { + if (!m.Flags.HasFlag(MemoryDomainFlags.SizedFunctionHooks)) + throw new InvalidOperationException(); + + IntPtr* functionPointers = (IntPtr*)m.Data; + if (functionPointers[0] != IntPtr.Zero) _read8 = MemoryDomainSizedAccessStub.Create(functionPointers[0], monitor); + if (functionPointers[1] != IntPtr.Zero) _write8 = MemoryDomainSizedAccessStub.Create(functionPointers[1], monitor); + + if (functionPointers[2] != IntPtr.Zero) _read16 = MemoryDomainSizedAccessStub.Create(functionPointers[2], monitor); + if (functionPointers[3] != IntPtr.Zero) _write16 = MemoryDomainSizedAccessStub.Create(functionPointers[3], monitor); + + if (functionPointers[4] != IntPtr.Zero) _read32 = MemoryDomainSizedAccessStub.Create(functionPointers[4], monitor); + if (functionPointers[5] != IntPtr.Zero) _write32 = MemoryDomainSizedAccessStub.Create(functionPointers[5], monitor); + + if (functionPointers[6] != IntPtr.Zero) _read64 = MemoryDomainSizedAccessStub.Create(functionPointers[6], monitor); + if (functionPointers[7] != IntPtr.Zero) _write64 = MemoryDomainSizedAccessStub.Create(functionPointers[7], monitor); + } + + public override byte PeekByte(long addr) + { + if (_read8 == null) throw new InvalidOperationException($"{Name} does not support 8-bit reads."); + if ((ulong)addr >= (ulong)Size || addr < 0) throw new ArgumentOutOfRangeException(nameof(addr)); + + byte ret = 0; + _read8.Access((IntPtr)(&ret), addr, 1); + return ret; + } + + public override void PokeByte(long addr, byte val) + { + if (_write8 == null) throw new InvalidOperationException($"{Name} does not support 8-bit writes."); + if ((ulong)addr >= (ulong)Size || addr < 0) throw new ArgumentOutOfRangeException(nameof(addr)); + + _write8.Access((IntPtr)(&val), addr, 1); + } + + public override ushort PeekUshort(long addr, bool bigEndian) + { + if (_read16 == null) throw new InvalidOperationException($"{Name} does not support 16-bit reads."); + if ((ulong)addr + 1 >= (ulong)Size || addr < 0) throw new ArgumentOutOfRangeException(nameof(addr)); + + ushort ret = 0; + _read16.Access((IntPtr)(&ret), addr, 1); + return ret; + } + + public override void PokeUshort(long addr, ushort val, bool bigEndian) + { + if (_write16 == null) throw new InvalidOperationException($"{Name} does not support 16-bit writes."); + if ((ulong)addr + 1 >= (ulong)Size || addr < 0) throw new ArgumentOutOfRangeException(nameof(addr)); + + _write16.Access((IntPtr)(&val), addr, 1); + } + + public override uint PeekUint(long addr, bool bigEndian) + { + if (_read32 == null) throw new InvalidOperationException($"{Name} does not support 32-bit reads."); + if ((ulong)addr + 3 >= (ulong)Size || addr < 0) throw new ArgumentOutOfRangeException(nameof(addr)); + + uint ret = 0; + _read32.Access((IntPtr)(&ret), addr, 1); + return ret; + } + + public override void PokeUint(long addr, uint val, bool bigEndian) + { + if (_write32 == null) throw new InvalidOperationException($"{Name} does not support 32-bit writes."); + if ((ulong)addr + 3 >= (ulong)Size || addr < 0) throw new ArgumentOutOfRangeException(nameof(addr)); + + _write32.Access((IntPtr)(&val), addr, 1); + } + + public override void BulkPeekByte(Range addresses, byte[] values) + { + if ((ulong)addresses.Start + addresses.Count() > (ulong)Size || addresses.Start < 0) throw new ArgumentOutOfRangeException(nameof(addresses)); + if (addresses.Count() > (uint)values.Length) throw new ArgumentException($"Length of {nameof(values)} must be at least {nameof(addresses)} count.", nameof(values)); + + fixed(byte* p = values) + _read8.Access((IntPtr)p, addresses.Start, (long)addresses.Count()); + } + + public override void BulkPokeByte(long addr, Span values) + { + if ((ulong)addr + (ulong)values.Length > (ulong)Size || addr < 0) throw new ArgumentOutOfRangeException(nameof(addr)); + + fixed(byte* p = values) + _write8.Access((IntPtr)p, addr, values.Length); + } + + public override void BulkPeekUshort(Range addresses, bool bigEndian, ushort[] values) + { + if ((ulong)addresses.Start + addresses.Count() > (ulong)Size || addresses.Start < 0) throw new ArgumentOutOfRangeException(nameof(addresses)); + if (addresses.Count() > (uint)values.Length * sizeof(ushort)) throw new ArgumentException($"Length of {nameof(values)} must be at least {nameof(addresses)} count.", nameof(values)); + if ((addresses.Count() & 1) != 0) throw new ArgumentOutOfRangeException(nameof(addresses)); // TODO: The caller should be specifying the count of ushorts, not the count of bytes! + + fixed(ushort* p = values) + _read16.Access((IntPtr)p, addresses.Start, (long)addresses.Count() / sizeof(ushort)); + } + + public override void BulkPokeUshort(long addr, bool bigEndian, Span values) + { + if ((ulong)addr + (ulong)values.Length * sizeof(ushort) > (ulong)Size || addr < 0) throw new ArgumentOutOfRangeException(nameof(addr)); + + fixed(ushort* p = values) + _write16.Access((IntPtr)p, addr, values.Length); + } + + public override void BulkPeekUint(Range addresses, bool bigEndian, uint[] values) + { + if ((ulong)addresses.Start + addresses.Count() > (ulong)Size || addresses.Start < 0) throw new ArgumentOutOfRangeException(nameof(addresses)); + if (addresses.Count() > (uint)values.Length * sizeof(uint)) throw new ArgumentException($"Length of {nameof(values)} must be at least {nameof(addresses)} count.", nameof(values)); + if ((addresses.Count() & 3) != 0) throw new ArgumentOutOfRangeException(nameof(addresses)); // TODO: The caller should be specifying the count of ushorts, not the count of bytes! + + fixed(uint* p = values) + _read32.Access((IntPtr)p, addresses.Start, (long)addresses.Count() / sizeof(uint)); + } + + public override void BulkPokeUint(long addr, bool bigEndian, Span values) + { + if ((ulong)addr + (ulong)values.Length * sizeof(uint) > (ulong)Size || addr < 0) throw new ArgumentOutOfRangeException(nameof(addr)); + + fixed(uint* p = values) + _write32.Access((IntPtr)p, addr, values.Length); + } + } } diff --git a/waterbox/emulibc/waterboxcore.h b/waterbox/emulibc/waterboxcore.h index 0fa3b84708b..690fe5d955c 100644 --- a/waterbox/emulibc/waterboxcore.h +++ b/waterbox/emulibc/waterboxcore.h @@ -38,6 +38,7 @@ typedef void (*MemoryFunctionHook)(uint8_t* buffer, int64_t address, int64_t cou #define MEMORYAREA_FLAGS_WORDSIZE8 256 #define MEMORYAREA_FLAGS_SWAPPED 512 #define MEMORYAREA_FLAGS_FUNCTIONHOOK 1024 +#define MEMORYAREA_FLAGS_SIZEDFUNCTIONHOOKS 2048 #ifdef __cplusplus } diff --git a/waterbox/melon/BizDebugging.cpp b/waterbox/melon/BizDebugging.cpp index 5b5e9bcbf52..a7c9bb38f94 100644 --- a/waterbox/melon/BizDebugging.cpp +++ b/waterbox/melon/BizDebugging.cpp @@ -8,6 +8,7 @@ #include "BizTypes.h" #include "dthumb.h" +#include #include melonDS::NDS* CurrentNDS; @@ -181,76 +182,122 @@ static bool SafeToPeek(u32 addr) return true; } -static void ARM9Access(u8* buffer, s64 address, s64 count, bool write) +template +static void ARM9ReadSized(access_size* buffer, s64 address, s64 count) { - if (write) + static_assert( + std::is_same::value || + std::is_same::value || + std::is_same::value + ); + + while (count--) { - while (count--) + if (address < CurrentNDS->ARM9.ITCMSize) { - if (address < CurrentNDS->ARM9.ITCMSize) - { - CurrentNDS->ARM9.ITCM[address & (melonDS::ITCMPhysicalSize - 1)] = *buffer; - } - else if ((address & CurrentNDS->ARM9.DTCMMask) == CurrentNDS->ARM9.DTCMBase) - { - CurrentNDS->ARM9.DTCM[address & (melonDS::DTCMPhysicalSize - 1)] = *buffer; - } - else - { - CurrentNDS->ARM9Write8(address, *buffer); - } - - address++; - buffer++; + *buffer = *(access_size*)&(CurrentNDS->ARM9.ITCM[address & (melonDS::ITCMPhysicalSize - 1)]); } - } - else - { - while (count--) + else if ((address & CurrentNDS->ARM9.DTCMMask) == CurrentNDS->ARM9.DTCMBase) + { + *buffer = *(access_size*)&(CurrentNDS->ARM9.DTCM[address & (melonDS::DTCMPhysicalSize - 1)]); + } + else { - if (address < CurrentNDS->ARM9.ITCMSize) - { - *buffer = CurrentNDS->ARM9.ITCM[address & (melonDS::ITCMPhysicalSize - 1)]; - } - else if ((address & CurrentNDS->ARM9.DTCMMask) == CurrentNDS->ARM9.DTCMBase) - { - *buffer = CurrentNDS->ARM9.DTCM[address & (melonDS::DTCMPhysicalSize - 1)]; - } - else - { + if constexpr (std::is_same::value) + *buffer = SafeToPeek(address) ? CurrentNDS->ARM9Read32(address) : 0; + else if constexpr (std::is_same::value) + *buffer = SafeToPeek(address) ? CurrentNDS->ARM9Read16(address) : 0; + else if constexpr (std::is_same::value) *buffer = SafeToPeek(address) ? CurrentNDS->ARM9Read8(address) : 0; - } - - address++; - buffer++; } + + address += sizeof(access_size); + buffer++; } } -static void ARM7Access(u8* buffer, s64 address, s64 count, bool write) +template +static void ARM9WriteSized(access_size* buffer, s64 address, s64 count) { - if (write) + static_assert( + std::is_same::value || + std::is_same::value || + std::is_same::value + ); + + while (count--) { - while (count--) + if (address < CurrentNDS->ARM9.ITCMSize) { - CurrentNDS->ARM7Write8(address, *buffer); - - address++; - buffer++; + *(access_size*)&(CurrentNDS->ARM9.ITCM[address & (melonDS::ITCMPhysicalSize - 1)]) = *buffer; + } + else if ((address & CurrentNDS->ARM9.DTCMMask) == CurrentNDS->ARM9.DTCMBase) + { + *(access_size*)&(CurrentNDS->ARM9.DTCM[address & (melonDS::DTCMPhysicalSize - 1)]) = *buffer; + } + else + { + if constexpr (std::is_same::value) + CurrentNDS->ARM9Write32(address, *buffer); + else if constexpr (std::is_same::value) + CurrentNDS->ARM9Write16(address, *buffer); + else if constexpr (std::is_same::value) + CurrentNDS->ARM9Write8(address, *buffer); } + + address += sizeof(access_size); + buffer++; } - else +} + +template +static void ARM7ReadSized(access_size* buffer, s64 address, s64 count) +{ + static_assert( + std::is_same::value || + std::is_same::value || + std::is_same::value + ); + + while (count--) { - while (count--) - { + if constexpr (std::is_same::value) + *buffer = SafeToPeek(address) ? CurrentNDS->ARM7Read32(address) : 0; + else if constexpr (std::is_same::value) + *buffer = SafeToPeek(address) ? CurrentNDS->ARM7Read16(address) : 0; + else if constexpr (std::is_same::value) *buffer = SafeToPeek(address) ? CurrentNDS->ARM7Read8(address) : 0; - address++; - buffer++; - } + address += sizeof(access_size); + buffer++; + } +} + +template +static void ARM7WriteSized(access_size* buffer, s64 address, s64 count) +{ + static_assert( + std::is_same::value || + std::is_same::value || + std::is_same::value + ); + + while (count--) + { + if constexpr (std::is_same::value) + CurrentNDS->ARM7Write32(address, *buffer); + else if constexpr (std::is_same::value) + CurrentNDS->ARM7Write16(address, *buffer); + else if constexpr (std::is_same::value) + CurrentNDS->ARM7Write8(address, *buffer); + + address += sizeof(access_size); + buffer++; } } +void* bus9[8]; +void* bus7[8]; ECL_EXPORT void GetMemoryAreas(MemoryArea *m) { int i = 0; @@ -299,8 +346,24 @@ ECL_EXPORT void GetMemoryAreas(MemoryArea *m) ADD_MEMORY_DOMAIN("NWRAM C", dsi->NWRAM_C, melonDS::NWRAMSize, MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE); } - ADD_MEMORY_DOMAIN("ARM9 System Bus", ARM9Access, 1ull << 32, MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_FUNCTIONHOOK); - ADD_MEMORY_DOMAIN("ARM7 System Bus", ARM7Access, 1ull << 32, MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_FUNCTIONHOOK); + uint64_t flags = MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_SIZEDFUNCTIONHOOKS; + bus9[0] = reinterpret_cast(ARM9ReadSized); + bus9[1] = reinterpret_cast(ARM9WriteSized); + bus9[2] = reinterpret_cast(ARM9ReadSized); + bus9[3] = reinterpret_cast(ARM9WriteSized); + bus9[4] = reinterpret_cast(ARM9ReadSized); + bus9[5] = reinterpret_cast(ARM9WriteSized); + bus9[6] = bus9[7] = nullptr; + ADD_MEMORY_DOMAIN("ARM9 System Bus", bus9, 1ull << 32, flags); + + bus7[0] = reinterpret_cast(ARM7ReadSized); + bus7[1] = reinterpret_cast(ARM7WriteSized); + bus7[2] = reinterpret_cast(ARM7ReadSized); + bus7[3] = reinterpret_cast(ARM7WriteSized); + bus7[4] = reinterpret_cast(ARM7ReadSized); + bus7[5] = reinterpret_cast(ARM7WriteSized); + bus7[6] = bus7[7] = nullptr; + ADD_MEMORY_DOMAIN("ARM7 System Bus", bus7, 1ull << 32, flags); // fixme: include more shit }