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

248 lines
8.0 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 SharpEmu.Libs.Gpu.GpuCommands.Packets;
using Xunit;
namespace SharpEmu.Libs.Tests.Agc;
public sealed class AgcMemSemaphoreTests
{
private const ulong BaseAddress = 0x1_0000_0000;
private const ulong SemaphoreAddress = BaseAddress + 0x200;
private const ulong CommandBufferAddress = BaseAddress + 0x400;
private const ulong PacketAddress = BaseAddress + 0x800;
private const ulong PacketEndAddress = PacketAddress + 0x100;
[Fact]
public void Decoder_ReadsSignalLayout()
{
var packet = MemorySemaphorePacket.Decode(
0x207,
1,
(1u << 16) | (1u << 20) | (6u << 29));
Assert.Equal(SemaphoreAddress, packet.Address);
Assert.True(packet.WaitForMailbox);
Assert.True(packet.WriteSignal);
Assert.True(packet.IsSignal);
Assert.False(packet.IsWait);
Assert.True(packet.IsSupported);
}
[Fact]
public void Decoder_RejectsUnknownSelection()
{
var packet = MemorySemaphorePacket.Decode(
0x200,
1,
5u << 29);
Assert.False(packet.IsSupported);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void Builder_WritesDirectAddressSignalPacket(bool asyncCompute)
{
var memory = new FakeCpuMemory(BaseAddress, 0x1000);
var ctx = new CpuContext(memory, Generation.Gen5);
InitializeCommandBuffer(memory);
ctx[CpuRegister.Rdi] = CommandBufferAddress;
ctx[CpuRegister.Rsi] = SemaphoreAddress;
ctx[CpuRegister.Rdx] = 6;
ctx[CpuRegister.Rcx] = 1;
ctx[CpuRegister.R8] = 1;
Assert.Equal(
0,
asyncCompute
? AgcExports.AcbMemSemaphore(ctx)
: AgcExports.DcbMemSemaphore(ctx));
Assert.Equal(PacketAddress, ctx[CpuRegister.Rax]);
Assert.Equal(PacketAddress + 16, ReadUInt64(memory, CommandBufferAddress + 0x10));
Assert.Equal(Pm4Header(4, 0x39), ReadUInt32(memory, PacketAddress));
Assert.Equal(unchecked((uint)SemaphoreAddress), ReadUInt32(memory, PacketAddress + 4));
Assert.Equal(
unchecked((uint)(SemaphoreAddress >> 32)),
ReadUInt32(memory, PacketAddress + 8));
Assert.Equal(
(6u << 29) | (1u << 20) | (1u << 16),
ReadUInt32(memory, PacketAddress + 12));
}
[Fact]
public void Builder_WritesDirectAddressWaitPacket()
{
var memory = new FakeCpuMemory(BaseAddress, 0x1000);
var ctx = new CpuContext(memory, Generation.Gen5);
InitializeCommandBuffer(memory);
ctx[CpuRegister.Rdi] = CommandBufferAddress;
ctx[CpuRegister.Rsi] = SemaphoreAddress;
ctx[CpuRegister.Rdx] = 7;
ctx[CpuRegister.Rcx] = 0;
ctx[CpuRegister.R8] = 0;
Assert.Equal(0, AgcExports.DcbMemSemaphore(ctx));
Assert.Equal(PacketAddress, ctx[CpuRegister.Rax]);
Assert.Equal(7u << 29, ReadUInt32(memory, PacketAddress + 12));
}
[Theory]
[InlineData(0UL, SemaphoreAddress, 6u, 0u, 0u)]
[InlineData(CommandBufferAddress, 0UL, 6u, 0u, 0u)]
[InlineData(CommandBufferAddress, SemaphoreAddress + 4, 6u, 0u, 0u)]
[InlineData(CommandBufferAddress, SemaphoreAddress, 5u, 0u, 0u)]
[InlineData(CommandBufferAddress, SemaphoreAddress, 6u, 2u, 0u)]
[InlineData(CommandBufferAddress, SemaphoreAddress, 6u, 0u, 2u)]
public void Builder_RejectsInvalidArguments(
ulong commandBufferAddress,
ulong semaphoreAddress,
uint operation,
uint signalType,
uint mailbox)
{
var memory = new FakeCpuMemory(BaseAddress, 0x1000);
var ctx = new CpuContext(memory, Generation.Gen5);
InitializeCommandBuffer(memory);
ctx[CpuRegister.Rdi] = commandBufferAddress;
ctx[CpuRegister.Rsi] = semaphoreAddress;
ctx[CpuRegister.Rdx] = operation;
ctx[CpuRegister.Rcx] = signalType;
ctx[CpuRegister.R8] = mailbox;
Assert.Equal(0, AgcExports.DcbMemSemaphore(ctx));
Assert.Equal(0UL, ctx[CpuRegister.Rax]);
Assert.Equal(PacketAddress, ReadUInt64(memory, CommandBufferAddress + 0x10));
}
[Fact]
public void IncrementSignal_AddsOne()
{
var memory = new FakeCpuMemory(BaseAddress, 0x1000);
Write(memory, 4);
Assert.True(MemorySemaphorePacket.TrySignal(
memory.TryRead,
memory,
SemaphoreAddress,
writeSignal: false,
out var value));
Assert.Equal(5UL, value);
Assert.Equal(5UL, Read(memory));
}
[Fact]
public void WriteSignal_StoresOne()
{
var memory = new FakeCpuMemory(BaseAddress, 0x1000);
Write(memory, 19);
Assert.True(MemorySemaphorePacket.TrySignal(
memory.TryRead,
memory,
SemaphoreAddress,
writeSignal: true,
out var value));
Assert.Equal(1UL, value);
Assert.Equal(1UL, Read(memory));
}
[Fact]
public void Wait_ConsumesOneToken()
{
var memory = new FakeCpuMemory(BaseAddress, 0x1000);
Write(memory, 2);
Assert.True(MemorySemaphorePacket.TryConsume(
memory.TryRead,
memory,
SemaphoreAddress,
out var prior));
Assert.Equal(2UL, prior);
Assert.Equal(1UL, Read(memory));
}
[Fact]
public void Wait_DoesNotUnderflowAtZero()
{
var memory = new FakeCpuMemory(BaseAddress, 0x1000);
Assert.True(MemorySemaphorePacket.TryConsume(
memory.TryRead,
memory,
SemaphoreAddress,
out var prior));
Assert.Equal(0UL, prior);
Assert.Equal(0UL, Read(memory));
}
[Theory]
[InlineData(0UL)]
[InlineData(SemaphoreAddress + 4)]
public void Operations_RejectInvalidAddresses(ulong address)
{
var memory = new FakeCpuMemory(BaseAddress, 0x1000);
Assert.False(MemorySemaphorePacket.TryConsume(
memory.TryRead,
memory, address, out _));
Assert.False(MemorySemaphorePacket.TrySignal(
memory.TryRead,
memory,
address,
writeSignal: false,
out _));
}
private static void Write(FakeCpuMemory memory, ulong value)
{
Span<byte> bytes = stackalloc byte[sizeof(ulong)];
BinaryPrimitives.WriteUInt64LittleEndian(bytes, value);
Assert.True(memory.TryWrite(SemaphoreAddress, bytes));
}
private static ulong Read(FakeCpuMemory memory)
{
Span<byte> bytes = stackalloc byte[sizeof(ulong)];
Assert.True(memory.TryRead(SemaphoreAddress, bytes));
return BinaryPrimitives.ReadUInt64LittleEndian(bytes);
}
private static void InitializeCommandBuffer(FakeCpuMemory memory)
{
WriteUInt64(memory, CommandBufferAddress + 0x10, PacketAddress);
WriteUInt64(memory, CommandBufferAddress + 0x18, PacketEndAddress);
}
private static uint Pm4Header(uint dwords, uint opcode) =>
0xC000_0000u | ((dwords - 2) << 16) | (opcode << 8);
private static uint ReadUInt32(FakeCpuMemory memory, ulong address)
{
Span<byte> bytes = stackalloc byte[sizeof(uint)];
Assert.True(memory.TryRead(address, bytes));
return BinaryPrimitives.ReadUInt32LittleEndian(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);
}
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));
}
}