Files
sharpemu/src/SharpEmu.HLE/CpuContext.cs
T

299 lines
7.8 KiB
C#

// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System.Buffers.Binary;
using System.Text;
namespace SharpEmu.HLE;
public sealed class CpuContext(ICpuMemory memory, Generation generation)
{
private readonly ulong[] _registers = new ulong[16];
private readonly ulong[] _xmmRegisters = new ulong[32];
private readonly ulong[] _ymmUpperRegisters = new ulong[32];
private bool _raxWritten;
public ICpuMemory Memory { get; } = memory ?? throw new ArgumentNullException(nameof(memory));
public Generation TargetGeneration { get; } = generation;
public ulong Rip { get; set; }
public ulong Rflags { get; set; }
public ulong FsBase { get; set; }
public ulong GsBase { get; set; }
public ulong this[CpuRegister register]
{
get => _registers[(int)register];
set
{
_registers[(int)register] = value;
if (register == CpuRegister.Rax)
{
_raxWritten = true;
}
}
}
public void ClearRaxWriteFlag()
{
_raxWritten = false;
}
public bool WasRaxWritten => _raxWritten;
public void GetXmmRegister(int registerIndex, out ulong low, out ulong high)
{
if ((uint)registerIndex >= 16)
{
throw new ArgumentOutOfRangeException(nameof(registerIndex));
}
var offset = registerIndex * 2;
low = _xmmRegisters[offset];
high = _xmmRegisters[offset + 1];
}
public void SetXmmRegister(int registerIndex, ulong low, ulong high)
{
if ((uint)registerIndex >= 16)
{
throw new ArgumentOutOfRangeException(nameof(registerIndex));
}
var offset = registerIndex * 2;
_xmmRegisters[offset] = low;
_xmmRegisters[offset + 1] = high;
}
public void GetYmmUpper(int registerIndex, out ulong low, out ulong high)
{
if ((uint)registerIndex >= 16)
{
throw new ArgumentOutOfRangeException(nameof(registerIndex));
}
var offset = registerIndex * 2;
low = _ymmUpperRegisters[offset];
high = _ymmUpperRegisters[offset + 1];
}
public void SetYmmUpper(int registerIndex, ulong low, ulong high)
{
if ((uint)registerIndex >= 16)
{
throw new ArgumentOutOfRangeException(nameof(registerIndex));
}
var offset = registerIndex * 2;
_ymmUpperRegisters[offset] = low;
_ymmUpperRegisters[offset + 1] = high;
}
public void ClearYmmUpper(int registerIndex)
{
SetYmmUpper(registerIndex, 0, 0);
}
public void ClearAllYmmUpper()
{
Array.Clear(_ymmUpperRegisters);
}
public void GetYmmRegister(
int registerIndex,
out ulong lowLow,
out ulong lowHigh,
out ulong highLow,
out ulong highHigh)
{
GetXmmRegister(registerIndex, out lowLow, out lowHigh);
GetYmmUpper(registerIndex, out highLow, out highHigh);
}
public void SetYmmRegister(
int registerIndex,
ulong lowLow,
ulong lowHigh,
ulong highLow,
ulong highHigh)
{
SetXmmRegister(registerIndex, lowLow, lowHigh);
SetYmmUpper(registerIndex, highLow, highHigh);
}
public bool TryReadByte(ulong address, out byte value)
{
Span<byte> buffer = stackalloc byte[1];
if (!Memory.TryRead(address, buffer))
{
value = 0;
return false;
}
value = buffer[0];
return true;
}
public bool TryReadUInt16(ulong address, out ushort value)
{
Span<byte> bytes = stackalloc byte[sizeof(ushort)];
if (!Memory.TryRead(address, bytes))
{
value = 0;
return false;
}
value = BinaryPrimitives.ReadUInt16LittleEndian(bytes);
return true;
}
public bool TryWriteUInt16(ulong address, ushort value)
{
Span<byte> buffer = stackalloc byte[sizeof(ushort)];
BinaryPrimitives.WriteUInt16LittleEndian(buffer, value);
return Memory.TryWrite(address, buffer);
}
public bool TryReadInt32(ulong address, out int value)
{
Span<byte> bytes = stackalloc byte[sizeof(int)];
if (!Memory.TryRead(address, bytes))
{
value = 0;
return false;
}
value = BinaryPrimitives.ReadInt32LittleEndian(bytes);
return true;
}
public bool TryWriteInt32(ulong address, int value, bool checkNil = false)
{
if (checkNil && address == 0)
{
return false;
}
Span<byte> bytes = stackalloc byte[sizeof(int)];
BinaryPrimitives.WriteInt32LittleEndian(bytes, value);
return Memory.TryWrite(address, bytes);
}
public bool TryReadUInt32(ulong address, out uint value)
{
Span<byte> buffer = stackalloc byte[sizeof(uint)];
if (!Memory.TryRead(address, buffer))
{
value = 0;
return false;
}
value = BinaryPrimitives.ReadUInt32LittleEndian(buffer);
return true;
}
public bool TryWriteUInt32(ulong address, uint value)
{
Span<byte> buffer = stackalloc byte[sizeof(uint)];
BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
return Memory.TryWrite(address, buffer);
}
public bool TryWriteInt64(ulong address, long value)
{
Span<byte> buffer = stackalloc byte[sizeof(long)];
BinaryPrimitives.WriteInt64LittleEndian(buffer, value);
return Memory.TryWrite(address, buffer);
}
public bool TryReadUInt64(ulong address, out ulong value)
{
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
if (!Memory.TryRead(address, buffer))
{
value = 0;
return false;
}
value = BinaryPrimitives.ReadUInt64LittleEndian(buffer);
return true;
}
public bool TryWriteUInt64(ulong address, ulong value)
{
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
BinaryPrimitives.WriteUInt64LittleEndian(buffer, value);
return Memory.TryWrite(address, buffer);
}
public bool TryReadNullTerminatedUtf8(ulong address, int capacity, out string value)
{
value = string.Empty;
if (address == 0 || capacity <= 0)
{
return false;
}
var bytes = new byte[capacity];
for (var index = 0; index < bytes.Length; index++)
{
if (!Memory.TryRead(address + (ulong)index, bytes.AsSpan(index, 1)))
{
return false;
}
if (bytes[index] == 0)
{
value = Encoding.UTF8.GetString(bytes, 0, index);
return true;
}
}
value = Encoding.UTF8.GetString(bytes);
return true;
}
public bool PushUInt64(ulong value)
{
var rsp = this[CpuRegister.Rsp];
rsp -= sizeof(ulong);
this[CpuRegister.Rsp] = rsp;
return TryWriteUInt64(rsp, value);
}
public bool PopUInt64(out ulong value)
{
var rsp = this[CpuRegister.Rsp];
if (!TryReadUInt64(rsp, out value))
{
return false;
}
this[CpuRegister.Rsp] = rsp + sizeof(ulong);
return true;
}
public int SetReturn(int result, Type? cast = null)
{
var value = cast switch
{
null => (ulong)result,
_ when cast == typeof(long) => (ulong)(long)result,
_ => throw new NotSupportedException(),
};
this[CpuRegister.Rax] = unchecked(value);
return result;
}
public int SetReturn(OrbisGen2Result result)
{
this[CpuRegister.Rax] = unchecked((ulong)(int)result);
return (int)result;
}
}