Implement a new physical memory manager and replace DeviceMemory
This commit is contained in:
parent
87bfe681ef
commit
4f5b582df5
19 changed files with 887 additions and 71 deletions
|
@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Font
|
|||
{
|
||||
private Switch _device;
|
||||
|
||||
private long _physicalAddress;
|
||||
private ulong _physicalAddress;
|
||||
|
||||
private string _fontsPath;
|
||||
|
||||
|
@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Font
|
|||
|
||||
private Dictionary<SharedFontType, FontInfo> _fontData;
|
||||
|
||||
public SharedFontManager(Switch device, long physicalAddress)
|
||||
public SharedFontManager(Switch device, ulong physicalAddress)
|
||||
{
|
||||
_physicalAddress = physicalAddress;
|
||||
|
||||
|
@ -48,7 +48,7 @@ namespace Ryujinx.HLE.HOS.Font
|
|||
{
|
||||
if (_fontData == null)
|
||||
{
|
||||
_device.Memory.FillWithZeros(_physicalAddress, Horizon.FontSize);
|
||||
_device.Memory.ZeroFill(_physicalAddress, Horizon.FontSize);
|
||||
|
||||
uint fontOffset = 0;
|
||||
|
||||
|
@ -84,7 +84,7 @@ namespace Ryujinx.HLE.HOS.Font
|
|||
|
||||
for (; fontOffset - start < data.Length; fontOffset++)
|
||||
{
|
||||
_device.Memory.WriteByte(_physicalAddress + fontOffset, data[fontOffset - start]);
|
||||
_device.Memory.Write(_physicalAddress + fontOffset, data[fontOffset - start]);
|
||||
}
|
||||
|
||||
return info;
|
||||
|
@ -107,7 +107,7 @@ namespace Ryujinx.HLE.HOS.Font
|
|||
|
||||
for (; fontOffset - start < data.Length; fontOffset++)
|
||||
{
|
||||
_device.Memory.WriteByte(_physicalAddress + fontOffset, data[fontOffset - start]);
|
||||
_device.Memory.Write(_physicalAddress + fontOffset, data[fontOffset - start]);
|
||||
}
|
||||
|
||||
return info;
|
||||
|
@ -138,15 +138,15 @@ namespace Ryujinx.HLE.HOS.Font
|
|||
}
|
||||
}
|
||||
|
||||
private void WriteMagicAndSize(long position, int size)
|
||||
private void WriteMagicAndSize(ulong address, int size)
|
||||
{
|
||||
const int decMagic = 0x18029a7f;
|
||||
const int key = 0x49621806;
|
||||
|
||||
int encryptedSize = BinaryPrimitives.ReverseEndianness(size ^ key);
|
||||
|
||||
_device.Memory.WriteInt32(position + 0, decMagic);
|
||||
_device.Memory.WriteInt32(position + 4, encryptedSize);
|
||||
_device.Memory.Write(address + 0, decMagic);
|
||||
_device.Memory.Write(address + 4, encryptedSize);
|
||||
}
|
||||
|
||||
public int GetFontSize(SharedFontType fontType)
|
||||
|
|
|
@ -190,13 +190,13 @@ namespace Ryujinx.HLE.HOS
|
|||
|
||||
KSharedMemory timeSharedMemory = new KSharedMemory(this, timePageList, 0, 0, MemoryPermission.Read);
|
||||
|
||||
TimeServiceManager.Instance.Initialize(device, this, timeSharedMemory, (long)(timePa - DramMemoryMap.DramBase), TimeSize);
|
||||
TimeServiceManager.Instance.Initialize(device, this, timeSharedMemory, timePa - DramMemoryMap.DramBase, TimeSize);
|
||||
|
||||
AppletState = new AppletStateMgr(this);
|
||||
|
||||
AppletState.SetFocus(true);
|
||||
|
||||
Font = new SharedFontManager(device, (long)(fontPa - DramMemoryMap.DramBase));
|
||||
Font = new SharedFontManager(device, fontPa - DramMemoryMap.DramBase);
|
||||
|
||||
IUserInterface.InitializePort(this);
|
||||
|
||||
|
|
|
@ -352,7 +352,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
{
|
||||
int newHandle = 0;
|
||||
|
||||
int handle = System.Device.Memory.ReadInt32((long)clientMsg.DramAddress + offset * 4);
|
||||
int handle = System.Device.Memory.Read<int>(clientMsg.DramAddress + offset * 4);
|
||||
|
||||
if (clientResult == KernelResult.Success && handle != 0)
|
||||
{
|
||||
|
@ -368,7 +368,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
{
|
||||
int newHandle = 0;
|
||||
|
||||
int handle = System.Device.Memory.ReadInt32((long)clientMsg.DramAddress + offset * 4);
|
||||
int handle = System.Device.Memory.Read<int>(clientMsg.DramAddress + offset * 4);
|
||||
|
||||
if (handle != 0)
|
||||
{
|
||||
|
@ -404,7 +404,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
|
||||
for (int index = 0; index < clientHeader.PointerBuffersCount; index++)
|
||||
{
|
||||
ulong pointerDesc = System.Device.Memory.ReadUInt64((long)clientMsg.DramAddress + offset * 4);
|
||||
ulong pointerDesc = System.Device.Memory.Read<ulong>(clientMsg.DramAddress + offset * 4);
|
||||
|
||||
PointerBufferDesc descriptor = new PointerBufferDesc(pointerDesc);
|
||||
|
||||
|
@ -463,11 +463,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
|
||||
for (int index = 0; index < totalBuffersCount; index++)
|
||||
{
|
||||
long clientDescAddress = (long)clientMsg.DramAddress + offset * 4;
|
||||
ulong clientDescAddress = clientMsg.DramAddress + offset * 4;
|
||||
|
||||
uint descWord0 = System.Device.Memory.ReadUInt32(clientDescAddress + 0);
|
||||
uint descWord1 = System.Device.Memory.ReadUInt32(clientDescAddress + 4);
|
||||
uint descWord2 = System.Device.Memory.ReadUInt32(clientDescAddress + 8);
|
||||
uint descWord0 = System.Device.Memory.Read<uint>(clientDescAddress + 0);
|
||||
uint descWord1 = System.Device.Memory.Read<uint>(clientDescAddress + 4);
|
||||
uint descWord2 = System.Device.Memory.Read<uint>(clientDescAddress + 8);
|
||||
|
||||
bool isSendDesc = index < clientHeader.SendBuffersCount;
|
||||
bool isExchangeDesc = index >= clientHeader.SendBuffersCount + clientHeader.ReceiveBuffersCount;
|
||||
|
@ -700,8 +700,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
}
|
||||
|
||||
// Copy header.
|
||||
System.Device.Memory.WriteUInt32((long)clientMsg.DramAddress + 0, serverHeader.Word0);
|
||||
System.Device.Memory.WriteUInt32((long)clientMsg.DramAddress + 4, serverHeader.Word1);
|
||||
System.Device.Memory.Write(clientMsg.DramAddress + 0, serverHeader.Word0);
|
||||
System.Device.Memory.Write(clientMsg.DramAddress + 4, serverHeader.Word1);
|
||||
|
||||
// Copy handles.
|
||||
uint offset;
|
||||
|
@ -710,11 +710,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
{
|
||||
offset = 3;
|
||||
|
||||
System.Device.Memory.WriteUInt32((long)clientMsg.DramAddress + 8, serverHeader.Word2);
|
||||
System.Device.Memory.Write(clientMsg.DramAddress + 8, serverHeader.Word2);
|
||||
|
||||
if (serverHeader.HasPid)
|
||||
{
|
||||
System.Device.Memory.WriteInt64((long)clientMsg.DramAddress + offset * 4, serverProcess.Pid);
|
||||
System.Device.Memory.Write(clientMsg.DramAddress + offset * 4, serverProcess.Pid);
|
||||
|
||||
offset += 2;
|
||||
}
|
||||
|
@ -730,7 +730,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
GetCopyObjectHandle(serverThread, clientProcess, handle, out newHandle);
|
||||
}
|
||||
|
||||
System.Device.Memory.WriteInt32((long)clientMsg.DramAddress + offset * 4, newHandle);
|
||||
System.Device.Memory.Write(clientMsg.DramAddress + offset * 4, newHandle);
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
@ -753,7 +753,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
}
|
||||
}
|
||||
|
||||
System.Device.Memory.WriteInt32((long)clientMsg.DramAddress + offset * 4, newHandle);
|
||||
System.Device.Memory.Write(clientMsg.DramAddress + offset * 4, newHandle);
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
@ -819,11 +819,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
|
||||
for (int index = 0; index < totalBuffersCount; index++)
|
||||
{
|
||||
long dstDescAddress = (long)clientMsg.DramAddress + offset * 4;
|
||||
ulong dstDescAddress = clientMsg.DramAddress + offset * 4;
|
||||
|
||||
System.Device.Memory.WriteUInt32(dstDescAddress + 0, 0);
|
||||
System.Device.Memory.WriteUInt32(dstDescAddress + 4, 0);
|
||||
System.Device.Memory.WriteUInt32(dstDescAddress + 8, 0);
|
||||
System.Device.Memory.Write(dstDescAddress + 0, 0);
|
||||
System.Device.Memory.Write(dstDescAddress + 4, 0);
|
||||
System.Device.Memory.Write(dstDescAddress + 8, 0);
|
||||
|
||||
offset += 3;
|
||||
}
|
||||
|
@ -878,9 +878,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
|
||||
private MessageHeader GetClientMessageHeader(Message clientMsg)
|
||||
{
|
||||
uint word0 = System.Device.Memory.ReadUInt32((long)clientMsg.DramAddress + 0);
|
||||
uint word1 = System.Device.Memory.ReadUInt32((long)clientMsg.DramAddress + 4);
|
||||
uint word2 = System.Device.Memory.ReadUInt32((long)clientMsg.DramAddress + 8);
|
||||
uint word0 = System.Device.Memory.Read<uint>(clientMsg.DramAddress + 0);
|
||||
uint word1 = System.Device.Memory.Read<uint>(clientMsg.DramAddress + 4);
|
||||
uint word2 = System.Device.Memory.Read<uint>(clientMsg.DramAddress + 8);
|
||||
|
||||
return new MessageHeader(word0, word1, word2);
|
||||
}
|
||||
|
@ -970,11 +970,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
|
||||
ulong[] receiveList = new ulong[recvListSize];
|
||||
|
||||
long recvListAddress = (long)message.DramAddress + recvListOffset;
|
||||
ulong recvListAddress = message.DramAddress + recvListOffset;
|
||||
|
||||
for (int index = 0; index < recvListSize; index++)
|
||||
{
|
||||
receiveList[index] = System.Device.Memory.ReadUInt64(recvListAddress + index * 8);
|
||||
receiveList[index] = System.Device.Memory.Read<ulong>(recvListAddress + (ulong)index * 8);
|
||||
}
|
||||
|
||||
return receiveList;
|
||||
|
@ -1225,8 +1225,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
|
||||
ulong address = clientProcess.MemoryManager.GetDramAddressFromVa(request.CustomCmdBuffAddr);
|
||||
|
||||
System.Device.Memory.WriteInt64((long)address + 0, 0);
|
||||
System.Device.Memory.WriteInt32((long)address + 8, (int)result);
|
||||
System.Device.Memory.Write<ulong>(address, 0);
|
||||
System.Device.Memory.Write(address + 8, (int)result);
|
||||
|
||||
clientProcess.MemoryManager.UnborrowIpcBuffer(
|
||||
request.CustomCmdBuffAddr,
|
||||
|
|
|
@ -1840,7 +1840,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||
{
|
||||
ulong unusedSizeBefore = address - addressTruncated;
|
||||
|
||||
_system.Device.Memory.Set(dstFirstPagePa, 0, unusedSizeBefore);
|
||||
_system.Device.Memory.ZeroFill(dstFirstPagePa, unusedSizeBefore);
|
||||
|
||||
ulong copySize = addressRounded <= endAddr ? addressRounded - address : size;
|
||||
|
||||
|
@ -1859,7 +1859,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||
|
||||
if (unusedSizeAfter != 0)
|
||||
{
|
||||
_system.Device.Memory.Set(firstPageFillAddress, 0, unusedSizeAfter);
|
||||
_system.Device.Memory.ZeroFill(firstPageFillAddress, unusedSizeAfter);
|
||||
}
|
||||
|
||||
KPageList pages = new KPageList();
|
||||
|
@ -1919,7 +1919,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||
unusedSizeAfter = PageSize;
|
||||
}
|
||||
|
||||
_system.Device.Memory.Set(lastPageFillAddr, 0, unusedSizeAfter);
|
||||
_system.Device.Memory.ZeroFill(lastPageFillAddr, unusedSizeAfter);
|
||||
|
||||
if (pages.AddRange(dstFirstPagePa, 1) != KernelResult.Success)
|
||||
{
|
||||
|
|
|
@ -1022,7 +1022,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
|
||||
bool useFlatPageTable = memRegion == MemoryRegion.Application;
|
||||
|
||||
CpuMemory = new MemoryManager(_system.Device.Memory.RamPointer, addrSpaceBits, useFlatPageTable);
|
||||
CpuMemory = new MemoryManager(_system.Device.Memory.Pointer, addrSpaceBits, useFlatPageTable);
|
||||
|
||||
Translator = new Translator(CpuMemory);
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
|
|||
EphemeralClockContextWriter = new EphemeralNetworkSystemClockContextWriter();
|
||||
}
|
||||
|
||||
public void Initialize(Switch device, Horizon system, KSharedMemory sharedMemory, long timeSharedMemoryAddress, int timeSharedMemorySize)
|
||||
public void Initialize(Switch device, Horizon system, KSharedMemory sharedMemory, ulong timeSharedMemoryAddress, int timeSharedMemorySize)
|
||||
{
|
||||
SharedMemory.Initialize(device, sharedMemory, timeSharedMemoryAddress, timeSharedMemorySize);
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||
|
@ -13,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
|
|||
{
|
||||
private Switch _device;
|
||||
private KSharedMemory _sharedMemory;
|
||||
private long _timeSharedMemoryAddress;
|
||||
private ulong _timeSharedMemoryAddress;
|
||||
private int _timeSharedMemorySize;
|
||||
|
||||
private const uint SteadyClockContextOffset = 0x00;
|
||||
|
@ -21,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
|
|||
private const uint NetworkSystemClockContextOffset = 0x80;
|
||||
private const uint AutomaticCorrectionEnabledOffset = 0xC8;
|
||||
|
||||
public void Initialize(Switch device, KSharedMemory sharedMemory, long timeSharedMemoryAddress, int timeSharedMemorySize)
|
||||
public void Initialize(Switch device, KSharedMemory sharedMemory, ulong timeSharedMemoryAddress, int timeSharedMemorySize)
|
||||
{
|
||||
_device = device;
|
||||
_sharedMemory = sharedMemory;
|
||||
|
@ -29,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
|
|||
_timeSharedMemorySize = timeSharedMemorySize;
|
||||
|
||||
// Clean the shared memory
|
||||
_device.Memory.FillWithZeros(_timeSharedMemoryAddress, _timeSharedMemorySize);
|
||||
_device.Memory.ZeroFill(_timeSharedMemoryAddress, (ulong)_timeSharedMemorySize);
|
||||
}
|
||||
|
||||
public KSharedMemory GetSharedMemory()
|
||||
|
@ -86,9 +87,9 @@ namespace Ryujinx.HLE.HOS.Services.Time
|
|||
WriteObjectToSharedMemory(NetworkSystemClockContextOffset, 4, context);
|
||||
}
|
||||
|
||||
private T ReadObjectFromSharedMemory<T>(long offset, long padding)
|
||||
private T ReadObjectFromSharedMemory<T>(ulong offset, ulong padding)
|
||||
{
|
||||
long indexOffset = _timeSharedMemoryAddress + offset;
|
||||
ulong indexOffset = _timeSharedMemoryAddress + offset;
|
||||
|
||||
T result;
|
||||
uint index;
|
||||
|
@ -96,31 +97,31 @@ namespace Ryujinx.HLE.HOS.Services.Time
|
|||
|
||||
do
|
||||
{
|
||||
index = _device.Memory.ReadUInt32(indexOffset);
|
||||
index = _device.Memory.Read<uint>(indexOffset);
|
||||
|
||||
long objectOffset = indexOffset + 4 + padding + (index & 1) * Marshal.SizeOf<T>();
|
||||
ulong objectOffset = indexOffset + 4 + padding + (ulong)((index & 1) * Unsafe.SizeOf<T>());
|
||||
|
||||
result = _device.Memory.ReadStruct<T>(objectOffset);
|
||||
result = _device.Memory.Read<T>(objectOffset);
|
||||
|
||||
Thread.MemoryBarrier();
|
||||
|
||||
possiblyNewIndex = _device.Memory.ReadUInt32(indexOffset);
|
||||
possiblyNewIndex = _device.Memory.Read<uint>(indexOffset);
|
||||
} while (index != possiblyNewIndex);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void WriteObjectToSharedMemory<T>(long offset, long padding, T value)
|
||||
private void WriteObjectToSharedMemory<T>(ulong offset, ulong padding, T value)
|
||||
{
|
||||
long indexOffset = _timeSharedMemoryAddress + offset;
|
||||
uint newIndex = _device.Memory.ReadUInt32(indexOffset) + 1;
|
||||
long objectOffset = indexOffset + 4 + padding + (newIndex & 1) * Marshal.SizeOf<T>();
|
||||
ulong indexOffset = _timeSharedMemoryAddress + offset;
|
||||
uint newIndex = _device.Memory.Read<uint>(indexOffset) + 1;
|
||||
ulong objectOffset = indexOffset + 4 + padding + (ulong)((newIndex & 1) * Unsafe.SizeOf<T>());
|
||||
|
||||
_device.Memory.WriteStruct(objectOffset, value);
|
||||
_device.Memory.Write(objectOffset, value);
|
||||
|
||||
Thread.MemoryBarrier();
|
||||
|
||||
_device.Memory.WriteUInt32(indexOffset, newIndex);
|
||||
_device.Memory.Write(indexOffset, newIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,9 +88,9 @@ namespace Ryujinx.HLE.Input
|
|||
_mainLayoutOffset = Offset + HidControllerHeaderSize
|
||||
+ ((int)ControllerLayouts.Main * HidControllerLayoutsSize);
|
||||
|
||||
Device.Memory.FillWithZeros(Offset, 0x5000);
|
||||
Device.Memory.WriteStruct(Offset, Header);
|
||||
Device.Memory.WriteStruct(DeviceStateOffset, DeviceState);
|
||||
Device.Memory.ZeroFill((ulong)Offset, 0x5000);
|
||||
Device.Memory.Write((ulong)Offset, Header);
|
||||
Device.Memory.Write((ulong)DeviceStateOffset, DeviceState);
|
||||
|
||||
Connected = true;
|
||||
}
|
||||
|
@ -126,14 +126,14 @@ namespace Ryujinx.HLE.Input
|
|||
Timestamp = GetTimestamp(),
|
||||
};
|
||||
|
||||
Device.Memory.WriteStruct(_currentLayoutOffset, newInputStateHeader);
|
||||
Device.Memory.WriteStruct(_mainLayoutOffset, newInputStateHeader);
|
||||
Device.Memory.Write((ulong)_currentLayoutOffset, newInputStateHeader);
|
||||
Device.Memory.Write((ulong)_mainLayoutOffset, newInputStateHeader);
|
||||
|
||||
long currentInputStateOffset = HidControllersLayoutHeaderSize
|
||||
+ newInputStateHeader.CurrentEntryIndex * HidControllersInputEntrySize;
|
||||
|
||||
Device.Memory.WriteStruct(_currentLayoutOffset + currentInputStateOffset, currentInput);
|
||||
Device.Memory.WriteStruct(_mainLayoutOffset + currentInputStateOffset, currentInput);
|
||||
Device.Memory.Write((ulong)(_currentLayoutOffset + currentInputStateOffset), currentInput);
|
||||
Device.Memory.Write((ulong)(_mainLayoutOffset + currentInputStateOffset), currentInput);
|
||||
|
||||
LastInputState = currentInput;
|
||||
CurrentStateHeader = newInputStateHeader;
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace Ryujinx.HLE.Input
|
|||
_device = device;
|
||||
HidPosition = hidPosition;
|
||||
|
||||
device.Memory.FillWithZeros(hidPosition, Horizon.HidSize);
|
||||
device.Memory.ZeroFill((ulong)hidPosition, Horizon.HidSize);
|
||||
|
||||
_currentTouchHeader = new TouchHeader()
|
||||
{
|
||||
|
@ -152,7 +152,7 @@ namespace Ryujinx.HLE.Input
|
|||
TouchCount = points.Length
|
||||
};
|
||||
|
||||
_device.Memory.WriteStruct(currentTouchEntryOffset, touchEntry);
|
||||
_device.Memory.Write((ulong)currentTouchEntryOffset, touchEntry);
|
||||
|
||||
currentTouchEntryOffset += HidTouchEntryHeaderSize;
|
||||
|
||||
|
@ -169,12 +169,12 @@ namespace Ryujinx.HLE.Input
|
|||
Y = points[i].Y
|
||||
};
|
||||
|
||||
_device.Memory.WriteStruct(currentTouchEntryOffset, touch);
|
||||
_device.Memory.Write((ulong)currentTouchEntryOffset, touch);
|
||||
|
||||
currentTouchEntryOffset += HidTouchEntryTouchSize;
|
||||
}
|
||||
|
||||
_device.Memory.WriteStruct(_touchScreenOffset, newTouchHeader);
|
||||
_device.Memory.Write((ulong)_touchScreenOffset, newTouchHeader);
|
||||
|
||||
_currentTouchHeader = newTouchHeader;
|
||||
}
|
||||
|
@ -191,7 +191,7 @@ namespace Ryujinx.HLE.Input
|
|||
Timestamp = timestamp,
|
||||
};
|
||||
|
||||
_device.Memory.WriteStruct(_keyboardOffset, newKeyboardHeader);
|
||||
_device.Memory.Write((ulong)_keyboardOffset, newKeyboardHeader);
|
||||
|
||||
long keyboardEntryOffset = _keyboardOffset + HidKeyboardHeaderSize;
|
||||
keyboardEntryOffset += newKeyboardHeader.CurrentEntryIndex * HidKeyboardEntrySize;
|
||||
|
@ -204,7 +204,7 @@ namespace Ryujinx.HLE.Input
|
|||
Modifier = keyboard.Modifier,
|
||||
};
|
||||
|
||||
_device.Memory.WriteStruct(keyboardEntryOffset, newkeyboardEntry);
|
||||
_device.Memory.Write((ulong)keyboardEntryOffset, newkeyboardEntry);
|
||||
|
||||
_currentKeyboardEntry = newkeyboardEntry;
|
||||
_currentKeyboardHeader = newKeyboardHeader;
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Profiler\Ryujinx.Profiler.csproj" />
|
||||
<ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -8,6 +8,7 @@ using Ryujinx.HLE.HOS;
|
|||
using Ryujinx.HLE.HOS.Services;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.HLE.Input;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
|
@ -17,7 +18,7 @@ namespace Ryujinx.HLE
|
|||
{
|
||||
internal IAalOutput AudioOut { get; private set; }
|
||||
|
||||
internal DeviceMemory Memory { get; private set; }
|
||||
internal MemoryBlock Memory { get; private set; }
|
||||
|
||||
internal NvGpu Gpu { get; private set; }
|
||||
|
||||
|
@ -49,7 +50,7 @@ namespace Ryujinx.HLE
|
|||
|
||||
AudioOut = audioOut;
|
||||
|
||||
Memory = new DeviceMemory();
|
||||
Memory = new MemoryBlock(1UL << 32);
|
||||
|
||||
Gpu = new NvGpu(renderer);
|
||||
|
||||
|
|
274
Ryujinx.Memory/MemoryBlock.cs
Normal file
274
Ryujinx.Memory/MemoryBlock.cs
Normal file
|
@ -0,0 +1,274 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
using static Ryujinx.Memory.MemoryConstants;
|
||||
|
||||
namespace Ryujinx.Memory
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a block of contiguous physical guest memory.
|
||||
/// </summary>
|
||||
public sealed class MemoryBlock : IDisposable
|
||||
{
|
||||
private IntPtr _pointer;
|
||||
|
||||
private readonly HashSet<MemoryRange>[] _pages;
|
||||
|
||||
/// <summary>
|
||||
/// Pointer to the memory block data.
|
||||
/// </summary>
|
||||
public IntPtr Pointer => _pointer;
|
||||
|
||||
/// <summary>
|
||||
/// Size of the memory block.
|
||||
/// </summary>
|
||||
public ulong Size { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the memory block class.
|
||||
/// </summary>
|
||||
/// <param name="size">Size of the memory block</param>
|
||||
public MemoryBlock(ulong size)
|
||||
{
|
||||
_pointer = MemoryManagement.Allocate(size);
|
||||
|
||||
Size = size;
|
||||
|
||||
_pages = new HashSet<MemoryRange>[size >> PageBits];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads bytes from the memory block.
|
||||
/// </summary>
|
||||
/// <param name="address">Starting address of the range being read</param>
|
||||
/// <param name="data">Span where the bytes being read will be copied to</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Read(ulong address, Span<byte> data)
|
||||
{
|
||||
GetSpan(address, data.Length).CopyTo(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads data from the memory block.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the data</typeparam>
|
||||
/// <param name="address">Address where the data is located</param>
|
||||
/// <returns>Data at the specified address</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T Read<T>(ulong address)
|
||||
{
|
||||
return GetRef<T>(address, Unsafe.SizeOf<T>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes bytes to the memory block.
|
||||
/// </summary>
|
||||
/// <param name="address">Starting address of the range being written</param>
|
||||
/// <param name="data">Span where the bytes being written will be copied from</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Write(ulong address, ReadOnlySpan<byte> data)
|
||||
{
|
||||
data.CopyTo(GetSpan(address, data.Length));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data to the memory block.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the data being written</typeparam>
|
||||
/// <param name="address">Address to write the data into</param>
|
||||
/// <param name="data">Data to be written</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Write<T>(ulong address, T data)
|
||||
{
|
||||
GetRef<T>(address, Unsafe.SizeOf<T>()) = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies data from one memory location to another.
|
||||
/// </summary>
|
||||
/// <param name="srcAddress">Source address to read the data from</param>
|
||||
/// <param name="dstAddress">Destination address to write the data into</param>
|
||||
/// <param name="size">Size of the copy in bytes</param>
|
||||
public void Copy(ulong srcAddress, ulong dstAddress, ulong size)
|
||||
{
|
||||
Write(dstAddress, GetSpan(srcAddress, (int)size));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills a region of memory with zeros.
|
||||
/// </summary>
|
||||
/// <param name="address">Address of the region to fill with zeros</param>
|
||||
/// <param name="size">Size in bytes of the region to fill</param>
|
||||
public void ZeroFill(ulong address, ulong size)
|
||||
{
|
||||
GetSpan(address, (int)size).Fill(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a range of memory that is tracked for memory modification.
|
||||
/// The range can be checked for modification of any sub-range inside the range.
|
||||
/// </summary>
|
||||
/// <param name="address">Starting address of the range being tracked</param>
|
||||
/// <param name="size">Size in bytes of the range being tracked</param>
|
||||
/// <returns>The new write tracked memory range</returns>
|
||||
public MemoryRange CreateMemoryRange(ulong address, ulong size)
|
||||
{
|
||||
return new MemoryRange(this, address, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference of the data at a given memory block region.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Data type</typeparam>
|
||||
/// <param name="address">Address of the memory region</param>
|
||||
/// <param name="size">Size in bytes of the memory region</param>
|
||||
/// <returns>A reference to the given memory region data</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe ref T GetRef<T>(ulong address, int size)
|
||||
{
|
||||
IntPtr ptr = _pointer;
|
||||
|
||||
if (ptr == IntPtr.Zero)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(MemoryBlock));
|
||||
}
|
||||
|
||||
ulong endAddress = address + (ulong)size;
|
||||
|
||||
if (endAddress > Size || endAddress < address)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(size));
|
||||
}
|
||||
|
||||
return ref Unsafe.AsRef<T>((void*)PtrAddr(ptr, address));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the span of a given memory block region.
|
||||
/// </summary>
|
||||
/// <param name="address">Start address of the memory region</param>
|
||||
/// <param name="size">Size in bytes of the region</param>
|
||||
/// <returns>Span of the memory region</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe Span<byte> GetSpan(ulong address, int size)
|
||||
{
|
||||
IntPtr ptr = _pointer;
|
||||
|
||||
if (ptr == IntPtr.Zero)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(MemoryBlock));
|
||||
}
|
||||
|
||||
ulong endAddress = address + (ulong)size;
|
||||
|
||||
if (endAddress > Size || endAddress < address)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(size));
|
||||
}
|
||||
|
||||
return new Span<byte>((void*)PtrAddr(ptr, address), size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a range of pages was modified, since the last call to this method.
|
||||
/// </summary>
|
||||
/// <param name="page">Page number of the first page to be checked</param>
|
||||
/// <param name="count">Number of pages to check</param>
|
||||
/// <param name="range">Memory range being checked</param>
|
||||
/// <returns>True if any of the pages were modified, false otherwise</returns>
|
||||
internal bool QueryModified(int page, int count, MemoryRange range)
|
||||
{
|
||||
return QueryModifiedImpl(page, count, range, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a range of pages was modified, since the last call to this method.
|
||||
/// </summary>
|
||||
/// <param name="page">Page number of the first page to be checked</param>
|
||||
/// <param name="count">Number of pages to check</param>
|
||||
/// <param name="range">Memory range being checked</param>
|
||||
/// <param name="outBuffer">Optional output buffer to write the new data</param>
|
||||
/// <returns>True if any of the pages were modified, false otherwise</returns>
|
||||
internal bool QueryModified(int page, int count, MemoryRange range, Span<byte> outBuffer)
|
||||
{
|
||||
return QueryModifiedImpl(page, count, range, outBuffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a range of pages was modified, since the last call to this method.
|
||||
/// </summary>
|
||||
/// <param name="page">Page number of the first page to be checked</param>
|
||||
/// <param name="count">Number of pages to check</param>
|
||||
/// <param name="range">Memory range being checked</param>
|
||||
/// <param name="outBuffer">Optional output buffer to write the new data</param>
|
||||
/// <returns>True if any of the pages were modified, false otherwise</returns>
|
||||
private bool QueryModifiedImpl(int page, int count, MemoryRange range, Span<byte> outBuffer)
|
||||
{
|
||||
if (count <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Span<IntPtr> addresses = stackalloc IntPtr[count];
|
||||
|
||||
IntPtr pagePtr = PtrAddr(_pointer, (ulong)page << PageBits);
|
||||
|
||||
MemoryManagement.QueryModifiedPages(pagePtr, (IntPtr)((ulong)count << PageBits), addresses, out ulong pc);
|
||||
|
||||
for (int i = 0; i < (int)pc; i++)
|
||||
{
|
||||
int modifiedPage = (int)((ulong)(addresses[i].ToInt64() - _pointer.ToInt64()) >> PageBits);
|
||||
|
||||
if (outBuffer != null)
|
||||
{
|
||||
Read((ulong)modifiedPage << PageBits, outBuffer.Slice((modifiedPage - page) << PageBits, PageSize));
|
||||
}
|
||||
|
||||
_pages[modifiedPage]?.Clear();
|
||||
}
|
||||
|
||||
bool modified = false;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (_pages[page + i] == null)
|
||||
{
|
||||
_pages[page + i] = new HashSet<MemoryRange>();
|
||||
}
|
||||
|
||||
modified |= _pages[page + i].Add(range);
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a 64-bits offset to a native pointer.
|
||||
/// </summary>
|
||||
/// <param name="pointer">Native pointer</param>
|
||||
/// <param name="offset">Offset to add</param>
|
||||
/// <returns>Native pointer with the added offset</returns>
|
||||
private IntPtr PtrAddr(IntPtr pointer, ulong offset)
|
||||
{
|
||||
return (IntPtr)(pointer.ToInt64() + (long)offset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frees the memory allocated for this memory block.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It's an error to use the memory block after disposal.
|
||||
/// </remarks>
|
||||
public void Dispose()
|
||||
{
|
||||
IntPtr ptr = Interlocked.Exchange(ref _pointer, IntPtr.Zero);
|
||||
|
||||
if (ptr != IntPtr.Zero)
|
||||
{
|
||||
MemoryManagement.Free(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
Ryujinx.Memory/MemoryConstants.cs
Normal file
9
Ryujinx.Memory/MemoryConstants.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ryujinx.Memory
|
||||
{
|
||||
static class MemoryConstants
|
||||
{
|
||||
public const int PageBits = 12;
|
||||
public const int PageSize = 1 << PageBits;
|
||||
public const int PageMask = PageSize - 1;
|
||||
}
|
||||
}
|
63
Ryujinx.Memory/MemoryManagement.cs
Normal file
63
Ryujinx.Memory/MemoryManagement.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Memory
|
||||
{
|
||||
public static class MemoryManagement
|
||||
{
|
||||
public static IntPtr Allocate(ulong size)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
IntPtr sizeNint = new IntPtr((long)size);
|
||||
|
||||
return MemoryManagementWin.Allocate(sizeNint);
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
return MemoryManagementUnix.Allocate(size);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Free(IntPtr address)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return MemoryManagementWin.Free(address);
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
return MemoryManagementUnix.Free(address);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool QueryModifiedPages(IntPtr address, IntPtr size, Span<IntPtr> addresses, out ulong count)
|
||||
{
|
||||
// This is only supported on windows, but returning
|
||||
// false (failed) is also valid for platforms without
|
||||
// write tracking support on the OS.
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return MemoryManagementWin.QueryModifiedPages(address, size, addresses, out count);
|
||||
}
|
||||
else
|
||||
{
|
||||
count = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
49
Ryujinx.Memory/MemoryManagementUnix.cs
Normal file
49
Ryujinx.Memory/MemoryManagementUnix.cs
Normal file
|
@ -0,0 +1,49 @@
|
|||
using Mono.Unix.Native;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Memory
|
||||
{
|
||||
static class MemoryManagementUnix
|
||||
{
|
||||
public static IntPtr Allocate(ulong size)
|
||||
{
|
||||
ulong pageSize = (ulong)Syscall.sysconf(SysconfName._SC_PAGESIZE);
|
||||
|
||||
const MmapProts prot = MmapProts.PROT_READ | MmapProts.PROT_WRITE;
|
||||
|
||||
const MmapFlags flags = MmapFlags.MAP_PRIVATE | MmapFlags.MAP_ANONYMOUS;
|
||||
|
||||
IntPtr ptr = Syscall.mmap(IntPtr.Zero, size + pageSize, prot, flags, -1, 0);
|
||||
|
||||
if (ptr == IntPtr.Zero)
|
||||
{
|
||||
throw new OutOfMemoryException();
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
ptr = new IntPtr(ptr.ToInt64() + (long)pageSize);
|
||||
|
||||
*((ulong*)ptr - 1) = size;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
public static bool Free(IntPtr address)
|
||||
{
|
||||
ulong pageSize = (ulong)Syscall.sysconf(SysconfName._SC_PAGESIZE);
|
||||
|
||||
ulong size;
|
||||
|
||||
unsafe
|
||||
{
|
||||
size = *((ulong*)address - 1);
|
||||
|
||||
address = new IntPtr(address.ToInt64() - (long)pageSize);
|
||||
}
|
||||
|
||||
return Syscall.munmap(address, size + pageSize) == 0;
|
||||
}
|
||||
}
|
||||
}
|
112
Ryujinx.Memory/MemoryManagementWin.cs
Normal file
112
Ryujinx.Memory/MemoryManagementWin.cs
Normal file
|
@ -0,0 +1,112 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Memory
|
||||
{
|
||||
static class MemoryManagementWin
|
||||
{
|
||||
[Flags]
|
||||
private enum AllocationType : uint
|
||||
{
|
||||
Commit = 0x1000,
|
||||
Reserve = 0x2000,
|
||||
Decommit = 0x4000,
|
||||
Release = 0x8000,
|
||||
Reset = 0x80000,
|
||||
Physical = 0x400000,
|
||||
TopDown = 0x100000,
|
||||
WriteWatch = 0x200000,
|
||||
LargePages = 0x20000000
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum MemoryProtection : uint
|
||||
{
|
||||
NoAccess = 0x01,
|
||||
ReadOnly = 0x02,
|
||||
ReadWrite = 0x04,
|
||||
WriteCopy = 0x08,
|
||||
Execute = 0x10,
|
||||
ExecuteRead = 0x20,
|
||||
ExecuteReadWrite = 0x40,
|
||||
ExecuteWriteCopy = 0x80,
|
||||
GuardModifierflag = 0x100,
|
||||
NoCacheModifierflag = 0x200,
|
||||
WriteCombineModifierflag = 0x400
|
||||
}
|
||||
|
||||
private enum WriteWatchFlags : uint
|
||||
{
|
||||
None = 0,
|
||||
Reset = 1
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr VirtualAlloc(
|
||||
IntPtr lpAddress,
|
||||
IntPtr dwSize,
|
||||
AllocationType flAllocationType,
|
||||
MemoryProtection flProtect);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern bool VirtualFree(
|
||||
IntPtr lpAddress,
|
||||
IntPtr dwSize,
|
||||
AllocationType dwFreeType);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private unsafe static extern int GetWriteWatch(
|
||||
WriteWatchFlags dwFlags,
|
||||
IntPtr lpBaseAddress,
|
||||
IntPtr dwRegionSize,
|
||||
IntPtr* lpAddresses,
|
||||
ref ulong lpdwCount,
|
||||
out uint lpdwGranularity);
|
||||
|
||||
public static IntPtr Allocate(IntPtr size)
|
||||
{
|
||||
const AllocationType flags =
|
||||
AllocationType.Reserve |
|
||||
AllocationType.Commit |
|
||||
AllocationType.WriteWatch;
|
||||
|
||||
IntPtr ptr = VirtualAlloc(IntPtr.Zero, size, flags, MemoryProtection.ReadWrite);
|
||||
|
||||
if (ptr == IntPtr.Zero)
|
||||
{
|
||||
throw new OutOfMemoryException();
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
public static bool Free(IntPtr address)
|
||||
{
|
||||
return VirtualFree(address, IntPtr.Zero, AllocationType.Release);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe static bool QueryModifiedPages(IntPtr address, IntPtr size, Span<IntPtr> addresses, out ulong count)
|
||||
{
|
||||
ulong pagesCount = (ulong)addresses.Length;
|
||||
|
||||
int result;
|
||||
|
||||
fixed (IntPtr* addressesPtr = addresses)
|
||||
{
|
||||
result = GetWriteWatch(
|
||||
WriteWatchFlags.Reset,
|
||||
address,
|
||||
size,
|
||||
addressesPtr,
|
||||
ref pagesCount,
|
||||
out _);
|
||||
}
|
||||
|
||||
count = pagesCount;
|
||||
|
||||
return result == 0;
|
||||
}
|
||||
}
|
||||
}
|
277
Ryujinx.Memory/MemoryRange.cs
Normal file
277
Ryujinx.Memory/MemoryRange.cs
Normal file
|
@ -0,0 +1,277 @@
|
|||
using Ryujinx.Common;
|
||||
using System;
|
||||
|
||||
using static Ryujinx.Memory.MemoryConstants;
|
||||
|
||||
namespace Ryujinx.Memory
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a write tracked range of memory.
|
||||
/// </summary>
|
||||
public class MemoryRange
|
||||
{
|
||||
private readonly MemoryBlock _memoryBlock;
|
||||
|
||||
/// <summary>
|
||||
/// Address of the memory range.
|
||||
/// </summary>
|
||||
public ulong Address { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Size in bytes of the memory range.
|
||||
/// </summary>
|
||||
public ulong Size { get; }
|
||||
|
||||
private readonly ulong _lastPageAddress;
|
||||
|
||||
private readonly int _firstPage;
|
||||
private readonly int _firstPageOffset;
|
||||
private readonly int _lastPage;
|
||||
private readonly int _lastPageEndOffset;
|
||||
|
||||
private readonly byte[] _firstPageData;
|
||||
private readonly byte[] _lastPageData;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the memory range class.
|
||||
/// </summary>
|
||||
/// <param name="memoryBlock">Parent memory block that the range belongs to</param>
|
||||
/// <param name="address">Start address of the range</param>
|
||||
/// <param name="size">Size in bytes of the range</param>
|
||||
internal MemoryRange(MemoryBlock memoryBlock, ulong address, ulong size)
|
||||
{
|
||||
_memoryBlock = memoryBlock;
|
||||
Address = address;
|
||||
Size = size;
|
||||
|
||||
int firstPage = (int)(address / PageSize);
|
||||
int firstPageOffset = (int)(address & PageMask);
|
||||
|
||||
if (firstPageOffset != 0 || size < PageSize)
|
||||
{
|
||||
int dataSize = PageSize - firstPageOffset;
|
||||
|
||||
if ((ulong)dataSize > size)
|
||||
{
|
||||
dataSize = (int)size;
|
||||
}
|
||||
|
||||
_firstPageData = new byte[dataSize];
|
||||
|
||||
_memoryBlock.Read(address, _firstPageData);
|
||||
}
|
||||
|
||||
ulong endAddress = address + size;
|
||||
|
||||
int lastPage = (int)(BitUtils.DivRoundUp(endAddress, PageSize) - 1);
|
||||
|
||||
int lastPageEndOffset = (int)(endAddress & PageMask);
|
||||
|
||||
ulong lastPageAddress = endAddress - (ulong)lastPageEndOffset;
|
||||
|
||||
if (lastPage > firstPage && lastPageEndOffset != 0)
|
||||
{
|
||||
_lastPageData = new byte[lastPageEndOffset];
|
||||
|
||||
_memoryBlock.Read(lastPageAddress, _lastPageData);
|
||||
}
|
||||
|
||||
ulong addressAligned = BitUtils.AlignUp(address, PageSize);
|
||||
|
||||
int alignedPagesCount = (int)((BitUtils.AlignDown(endAddress, PageSize) - addressAligned) >> PageBits);
|
||||
|
||||
memoryBlock.QueryModified((int)(addressAligned >> PageBits), alignedPagesCount, this);
|
||||
|
||||
_lastPageAddress = lastPageAddress;
|
||||
_firstPage = firstPage;
|
||||
_firstPageOffset = firstPageOffset;
|
||||
_lastPage = lastPage;
|
||||
_lastPageEndOffset = lastPageEndOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the memory range was written since the last call to this method.
|
||||
/// </summary>
|
||||
/// <returns>True if the range data was modified since the last call, false otherwise</returns>
|
||||
public bool QueryModified()
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
int page = _firstPage;
|
||||
|
||||
if (_firstPageOffset != 0 || Size < PageSize)
|
||||
{
|
||||
int dataSize = _firstPageData.Length;
|
||||
|
||||
if ((ulong)dataSize > Size)
|
||||
{
|
||||
dataSize = (int)Size;
|
||||
}
|
||||
|
||||
Span<byte> oldData = new Span<byte>(_firstPageData);
|
||||
|
||||
ReadOnlySpan<byte> newData = _memoryBlock.GetSpan(Address, dataSize);
|
||||
|
||||
if (!oldData.SequenceEqual(newData))
|
||||
{
|
||||
modified = true;
|
||||
|
||||
newData.CopyTo(oldData);
|
||||
}
|
||||
|
||||
page++;
|
||||
}
|
||||
|
||||
int endPage = _lastPageEndOffset != 0 ? _lastPage : _lastPage + 1;
|
||||
|
||||
int alignedPagesCount = endPage - page;
|
||||
|
||||
if (alignedPagesCount > 0 && _memoryBlock.QueryModified(page, alignedPagesCount, this))
|
||||
{
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (_lastPage > _firstPage && _lastPageEndOffset != 0)
|
||||
{
|
||||
Span<byte> oldData = new Span<byte>(_lastPageData);
|
||||
|
||||
ReadOnlySpan<byte> newData = _memoryBlock.GetSpan(_lastPageAddress, _lastPageData.Length);
|
||||
|
||||
if (!oldData.SequenceEqual(newData))
|
||||
{
|
||||
modified = true;
|
||||
|
||||
newData.CopyTo(oldData);
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a span of data inside this memory range.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Use this method after checking for data modification inside the memory range.
|
||||
/// This ensures that you're working with the same data being used internally
|
||||
/// for modification checks with memory comparison.
|
||||
/// </remarks>
|
||||
/// <returns>The data span</returns>
|
||||
public Span<byte> GetSpan()
|
||||
{
|
||||
if (_firstPageData == null && _lastPageData == null)
|
||||
{
|
||||
return _memoryBlock.GetSpan(Address, (int)Size);
|
||||
}
|
||||
|
||||
Span<byte> data = new byte[Size];
|
||||
|
||||
int middleOffset = 0;
|
||||
int unalignedSize = 0;
|
||||
|
||||
if (_firstPageData != null)
|
||||
{
|
||||
middleOffset = _firstPageData.Length;
|
||||
unalignedSize = _firstPageData.Length;
|
||||
|
||||
_firstPageData.CopyTo(data.Slice(0, _firstPageData.Length));
|
||||
}
|
||||
|
||||
if (_lastPageData != null)
|
||||
{
|
||||
unalignedSize += _lastPageData.Length;
|
||||
|
||||
_lastPageData.CopyTo(data.Slice(data.Length - _lastPageData.Length));
|
||||
}
|
||||
|
||||
int middleSize = data.Length - unalignedSize;
|
||||
|
||||
_memoryBlock.Read(Address + (ulong)middleOffset, data.Slice(middleOffset, middleSize));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a given sub-range inside the memory range was written since the last
|
||||
/// call to this method.
|
||||
/// </summary>
|
||||
/// <param name="offset">Offset of the sub-range being checked</param>
|
||||
/// <param name="size">Size in bytes of the sub-range being checked</param>
|
||||
/// <param name="data">Buffer used for memory comparison, and to hold the updated data</param>
|
||||
/// <returns>True if the sub-range was modified, false otherwise</returns>
|
||||
public bool QueryModified(int offset, int size, Span<byte> data)
|
||||
{
|
||||
ulong address = Address + (ulong)offset;
|
||||
|
||||
int firstPage = (int)(address / PageSize);
|
||||
int firstPageOffset = (int)(address & PageMask);
|
||||
|
||||
ulong endAddress = address + (ulong)size;
|
||||
|
||||
int lastPage = (int)(BitUtils.DivRoundUp(endAddress, PageSize) - 1);
|
||||
|
||||
int lastPageEndOffset = (int)(endAddress & PageMask);
|
||||
|
||||
bool modified = false;
|
||||
|
||||
int page = firstPage;
|
||||
|
||||
int alignedOffset = offset;
|
||||
|
||||
if (firstPageOffset != 0 || size < PageSize)
|
||||
{
|
||||
int dataSize = PageSize - firstPageOffset;
|
||||
|
||||
if (dataSize > size)
|
||||
{
|
||||
dataSize = size;
|
||||
}
|
||||
|
||||
alignedOffset += dataSize;
|
||||
|
||||
Span<byte> oldData = data.Slice(offset, dataSize);
|
||||
|
||||
ReadOnlySpan<byte> newData = _memoryBlock.GetSpan(address, dataSize);
|
||||
|
||||
if (!oldData.SequenceEqual(newData))
|
||||
{
|
||||
modified = true;
|
||||
|
||||
newData.CopyTo(oldData);
|
||||
}
|
||||
|
||||
page++;
|
||||
}
|
||||
|
||||
int endPage = lastPageEndOffset != 0 ? lastPage : lastPage + 1;
|
||||
|
||||
int alignedPagesCount = endPage - page;
|
||||
|
||||
if (alignedPagesCount > 0)
|
||||
{
|
||||
Span<byte> outBuffer = data.Slice(alignedOffset, alignedPagesCount << PageBits);
|
||||
|
||||
if (_memoryBlock.QueryModified(page, alignedPagesCount, this, outBuffer))
|
||||
{
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastPage > firstPage && lastPageEndOffset != 0)
|
||||
{
|
||||
Span<byte> oldData = data.Slice(offset + size - lastPageEndOffset, lastPageEndOffset);
|
||||
|
||||
ReadOnlySpan<byte> newData = _memoryBlock.GetSpan(endAddress - (ulong)lastPageEndOffset, lastPageEndOffset);
|
||||
|
||||
if (!oldData.SequenceEqual(newData))
|
||||
{
|
||||
modified = true;
|
||||
|
||||
newData.CopyTo(oldData);
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
}
|
||||
}
|
19
Ryujinx.Memory/Ryujinx.Memory.csproj
Normal file
19
Ryujinx.Memory/Ryujinx.Memory.csproj
Normal file
|
@ -0,0 +1,19 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
14
Ryujinx.sln
14
Ryujinx.sln
|
@ -1,7 +1,7 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26730.8
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29613.14
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "Ryujinx\Ryujinx.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}"
|
||||
EndProject
|
||||
|
@ -28,6 +28,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Profiler", "Ryujinx
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ARMeilleure", "ARMeilleure\ARMeilleure.csproj", "{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory", "Ryujinx.Memory\Ryujinx.Memory.csproj", "{A5E6C691-9E22-4263-8F40-42F002CE66BE}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -124,6 +126,14 @@ Global
|
|||
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Profile Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Profile Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Profile Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Profile Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Profile Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
Loading…
Add table
Reference in a new issue