sharpemu/tests/SharpEmu.Libs.Tests/Agc/AgcAmmSemaphoreTests.cs

176 lines
5.4 KiB
C#

// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System.Buffers.Binary;
using SharpEmu.HLE;
using SharpEmu.Libs.Agc;
using Xunit;
namespace SharpEmu.Libs.Tests.Agc;
[Collection(AgcCommandBufferChainCollection.Name)]
public sealed class AgcAmmSemaphoreTests : IDisposable
{
private const ulong BaseAddress = 0x1_0000_0000;
private const ulong OutputAddress = BaseAddress + 0x8000;
private const ulong RangeSize = 0x4000;
private const ulong SlotSize = 32;
public AgcAmmSemaphoreTests()
{
AgcExports.ResetAmmSemaphoreMemoryForTests();
}
public void Dispose()
{
AgcExports.ResetAmmSemaphoreMemoryForTests();
}
[Theory]
[InlineData(0UL, RangeSize)]
[InlineData(BaseAddress + 1, RangeSize)]
[InlineData(BaseAddress, RangeSize - 1)]
[InlineData(BaseAddress, RangeSize / 2)]
public void Registration_RejectsInvalidRange(ulong baseAddress, ulong sizeBytes)
{
var ctx = CreateContext(out _);
ctx[CpuRegister.Rdi] = baseAddress;
ctx[CpuRegister.Rsi] = sizeBytes;
Assert.Equal(
(int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT,
AgcExports.SetAmmSemaphoreMemory(ctx));
}
[Fact]
public void Registration_RejectsUnmappedRange()
{
var memory = new FakeCpuMemory(BaseAddress, 0x2000);
var ctx = new CpuContext(memory, Generation.Gen5);
ctx[CpuRegister.Rdi] = BaseAddress;
ctx[CpuRegister.Rsi] = RangeSize;
Assert.Equal(
(int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT,
AgcExports.SetAmmSemaphoreMemory(ctx));
}
[Fact]
public void Registration_ClearsEachCounterAndCannotBeReplaced()
{
var ctx = CreateContext(out var memory);
WriteUInt64(memory, BaseAddress, 9);
WriteUInt64(memory, BaseAddress + RangeSize - SlotSize, 11);
ctx[CpuRegister.Rdi] = BaseAddress;
ctx[CpuRegister.Rsi] = RangeSize;
Assert.Equal(
(int)OrbisGen2Result.ORBIS_GEN2_OK,
AgcExports.SetAmmSemaphoreMemory(ctx));
Assert.Equal(0UL, ReadUInt64(memory, BaseAddress));
Assert.Equal(0UL, ReadUInt64(memory, BaseAddress + RangeSize - SlotSize));
Assert.Equal(
(int)OrbisGen2Result.ORBIS_GEN2_ERROR_ALREADY_EXISTS,
AgcExports.SetAmmSemaphoreMemory(ctx));
}
[Fact]
public void Registration_IsScopedToGuestMemory()
{
var first = CreateContext(out _);
first[CpuRegister.Rdi] = BaseAddress;
first[CpuRegister.Rsi] = RangeSize;
var second = CreateContext(out _);
second[CpuRegister.Rdi] = BaseAddress;
second[CpuRegister.Rsi] = RangeSize;
Assert.Equal(
(int)OrbisGen2Result.ORBIS_GEN2_OK,
AgcExports.SetAmmSemaphoreMemory(first));
Assert.Equal(
(int)OrbisGen2Result.ORBIS_GEN2_OK,
AgcExports.SetAmmSemaphoreMemory(second));
}
[Fact]
public void LabelLookup_RequiresRegistration()
{
var ctx = CreateContext(out _);
ctx[CpuRegister.Rdi] = 3;
ctx[CpuRegister.Rsi] = OutputAddress;
Assert.Equal(
(int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT,
AgcExports.GetSemaphoreLabel(ctx));
}
[Fact]
public void LabelLookup_ResolvesSlotAddress()
{
var ctx = CreateContext(out var memory);
Register(ctx);
ctx[CpuRegister.Rdi] = 3;
ctx[CpuRegister.Rsi] = OutputAddress;
Assert.Equal(
(int)OrbisGen2Result.ORBIS_GEN2_OK,
AgcExports.GetSemaphoreLabel(ctx));
Assert.Equal(BaseAddress + (3 * SlotSize), ReadUInt64(memory, OutputAddress));
}
[Fact]
public void LabelLookup_RejectsSlotOutsideRange()
{
var ctx = CreateContext(out _);
Register(ctx);
ctx[CpuRegister.Rdi] = RangeSize / SlotSize;
ctx[CpuRegister.Rsi] = OutputAddress;
Assert.Equal(
(int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT,
AgcExports.GetSemaphoreLabel(ctx));
}
[Fact]
public void LabelLookup_ReportsOutputMemoryFault()
{
var ctx = CreateContext(out _);
Register(ctx);
ctx[CpuRegister.Rdi] = 0;
ctx[CpuRegister.Rsi] = BaseAddress + 0x1_0000;
Assert.Equal(
(int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT,
AgcExports.GetSemaphoreLabel(ctx));
}
private static CpuContext CreateContext(out FakeCpuMemory memory)
{
memory = new FakeCpuMemory(BaseAddress, 0x1_0000);
return new CpuContext(memory, Generation.Gen5);
}
private static void Register(CpuContext ctx)
{
ctx[CpuRegister.Rdi] = BaseAddress;
ctx[CpuRegister.Rsi] = RangeSize;
Assert.Equal(
(int)OrbisGen2Result.ORBIS_GEN2_OK,
AgcExports.SetAmmSemaphoreMemory(ctx));
}
private static void WriteUInt64(FakeCpuMemory memory, ulong address, ulong value)
{
Span<byte> bytes = stackalloc byte[sizeof(ulong)];
BinaryPrimitives.WriteUInt64LittleEndian(bytes, value);
Assert.True(memory.TryWrite(address, bytes));
}
private static ulong ReadUInt64(FakeCpuMemory memory, ulong address)
{
Span<byte> bytes = stackalloc byte[sizeof(ulong)];
Assert.True(memory.TryRead(address, bytes));
return BinaryPrimitives.ReadUInt64LittleEndian(bytes);
}
}