226 lines
6.7 KiB
C#
226 lines
6.7 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
using System.Buffers.Binary;
|
|
using SharpEmu.Libs.Agc;
|
|
using SharpEmu.Libs.Gpu.GpuCommands.Packets;
|
|
using Xunit;
|
|
|
|
namespace SharpEmu.Libs.Tests.Agc;
|
|
|
|
public sealed class AgcAtomicMemTests
|
|
{
|
|
private const ulong BaseAddress = 0x1_0000_0000;
|
|
private const ulong AtomicAddress = BaseAddress + 0x200;
|
|
|
|
[Fact]
|
|
public void Decoder_Reads64BitReturnCompareSwapLoop()
|
|
{
|
|
var packet = AtomicMemoryPacket.Decode(
|
|
0x28u | (1u << 8) | (2u << 25),
|
|
unchecked((uint)AtomicAddress),
|
|
(uint)(AtomicAddress >> 32),
|
|
0x5566_7788,
|
|
0x1122_3344,
|
|
0xDDEE_FF00,
|
|
0x99AA_BBCC,
|
|
400);
|
|
|
|
Assert.Equal(8u, packet.BaseOperation);
|
|
Assert.True(packet.Is64Bit);
|
|
Assert.True(packet.ReturnsData);
|
|
Assert.True(packet.IsCompareSwap);
|
|
Assert.Equal(1u, packet.Command);
|
|
Assert.Equal(2u, packet.CachePolicy);
|
|
Assert.Equal(AtomicAddress, packet.Address);
|
|
Assert.Equal(0x1122_3344_5566_7788UL, packet.SourceData);
|
|
Assert.Equal(0x99AA_BBCC_DDEE_FF00UL, packet.CompareData);
|
|
Assert.Equal(400u, packet.LoopIntervalCycles);
|
|
Assert.Equal(0u, packet.EngineSelection);
|
|
Assert.True(packet.IsSupported);
|
|
Assert.True(packet.HasValidEngine(usesAsyncEncoding: false));
|
|
}
|
|
|
|
[Fact]
|
|
public void Decoder_RejectsGraphicsReservedEngineSelection()
|
|
{
|
|
var packet = AtomicMemoryPacket.Decode(
|
|
0x4Fu | (2u << 8) | (2u << 30),
|
|
unchecked((uint)AtomicAddress),
|
|
(uint)(AtomicAddress >> 32),
|
|
1,
|
|
0,
|
|
0,
|
|
0,
|
|
400);
|
|
|
|
Assert.True(packet.IsSupported);
|
|
Assert.False(packet.HasValidEngine(usesAsyncEncoding: false));
|
|
Assert.False(packet.HasValidEngine(usesAsyncEncoding: true));
|
|
}
|
|
|
|
[Fact]
|
|
public void CompareSwap_WritesSourceAndReturnsPriorValue()
|
|
{
|
|
var memory = new FakeCpuMemory(BaseAddress, 0x1000);
|
|
Write64(memory, 0x1234);
|
|
var packet = Packet(
|
|
operation: 0x28,
|
|
command: 0,
|
|
source: 0x5678,
|
|
compare: 0x1234);
|
|
|
|
Assert.True(packet.TryApply(
|
|
memory.TryRead,
|
|
memory,
|
|
out var prior,
|
|
out var current,
|
|
out var comparePassed));
|
|
Assert.True(comparePassed);
|
|
Assert.Equal(0x1234UL, prior);
|
|
Assert.Equal(0x5678UL, current);
|
|
Assert.Equal(0x5678UL, Read64(memory));
|
|
}
|
|
|
|
[Fact]
|
|
public void CompareSwap_FailedComparisonDoesNotChangeMemory()
|
|
{
|
|
var memory = new FakeCpuMemory(BaseAddress, 0x1000);
|
|
Write64(memory, 0x1234);
|
|
var packet = Packet(
|
|
operation: 0x28,
|
|
command: 1,
|
|
source: 0x5678,
|
|
compare: 0x9999);
|
|
|
|
Assert.True(packet.TryApply(
|
|
memory.TryRead,
|
|
memory,
|
|
out var prior,
|
|
out var current,
|
|
out var comparePassed));
|
|
Assert.False(comparePassed);
|
|
Assert.Equal(0x1234UL, prior);
|
|
Assert.Equal(0x1234UL, current);
|
|
Assert.Equal(0x1234UL, Read64(memory));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(0x4Fu, 9u, 4u, 13u)]
|
|
[InlineData(0x50u, 9u, 4u, 5u)]
|
|
[InlineData(0x55u, 0xF0u, 0xCCu, 0xC0u)]
|
|
[InlineData(0x56u, 0x30u, 0x0Fu, 0x3Fu)]
|
|
[InlineData(0x57u, 0x3Cu, 0x0Fu, 0x33u)]
|
|
[InlineData(0x58u, 3u, 3u, 0u)]
|
|
[InlineData(0x59u, 0u, 3u, 3u)]
|
|
public void Integer32Operations_UseDefinedSemantics(
|
|
uint operation,
|
|
uint initial,
|
|
uint source,
|
|
uint expected)
|
|
{
|
|
var memory = new FakeCpuMemory(BaseAddress, 0x1000);
|
|
Write32(memory, initial);
|
|
var packet = Packet(operation, command: 2, source, compare: 0);
|
|
|
|
Assert.True(packet.TryApply(
|
|
memory.TryRead,
|
|
memory,
|
|
out var prior,
|
|
out var current,
|
|
out _));
|
|
Assert.Equal(initial, (uint)prior);
|
|
Assert.Equal(expected, (uint)current);
|
|
Assert.Equal(expected, Read32(memory));
|
|
}
|
|
|
|
[Fact]
|
|
public void SignedMinimum_UsesSignedOrdering()
|
|
{
|
|
var memory = new FakeCpuMemory(BaseAddress, 0x1000);
|
|
Write32(memory, 1);
|
|
var packet = Packet(
|
|
operation: 0x51,
|
|
command: 2,
|
|
source: uint.MaxValue,
|
|
compare: 0);
|
|
|
|
Assert.True(packet.TryApply(
|
|
memory.TryRead,
|
|
memory,
|
|
out _,
|
|
out var current,
|
|
out _));
|
|
Assert.Equal(uint.MaxValue, (uint)current);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(0x08u, 2u)]
|
|
[InlineData(0x48u, 0u)]
|
|
[InlineData(0x0Fu, 1u)]
|
|
[InlineData(0x01u, 0u)]
|
|
public void UnsupportedOperationOrCommand_IsRejected(
|
|
uint operation,
|
|
uint command)
|
|
{
|
|
var packet = Packet(operation, command, source: 1, compare: 0);
|
|
|
|
Assert.False(packet.IsSupported);
|
|
}
|
|
|
|
[Fact]
|
|
public void AtomicLoopReference_IgnoresHighDwordFor32BitOperation()
|
|
{
|
|
var packet = Packet(
|
|
operation: 0x08,
|
|
command: 1,
|
|
source: 0x5678,
|
|
compare: 0xAABB_CCDD_0000_1234);
|
|
|
|
Assert.Equal(0x1234UL, packet.LoopReferenceValue);
|
|
}
|
|
|
|
private static AtomicMemoryPacket Packet(
|
|
uint operation,
|
|
uint command,
|
|
ulong source,
|
|
ulong compare) =>
|
|
AtomicMemoryPacket.Decode(
|
|
operation | (command << 8),
|
|
unchecked((uint)AtomicAddress),
|
|
(uint)(AtomicAddress >> 32),
|
|
(uint)source,
|
|
(uint)(source >> 32),
|
|
(uint)compare,
|
|
(uint)(compare >> 32),
|
|
400);
|
|
|
|
private static void Write32(FakeCpuMemory memory, uint value)
|
|
{
|
|
Span<byte> bytes = stackalloc byte[sizeof(uint)];
|
|
BinaryPrimitives.WriteUInt32LittleEndian(bytes, value);
|
|
Assert.True(memory.TryWrite(AtomicAddress, bytes));
|
|
}
|
|
|
|
private static uint Read32(FakeCpuMemory memory)
|
|
{
|
|
Span<byte> bytes = stackalloc byte[sizeof(uint)];
|
|
Assert.True(memory.TryRead(AtomicAddress, bytes));
|
|
return BinaryPrimitives.ReadUInt32LittleEndian(bytes);
|
|
}
|
|
|
|
private static void Write64(FakeCpuMemory memory, ulong value)
|
|
{
|
|
Span<byte> bytes = stackalloc byte[sizeof(ulong)];
|
|
BinaryPrimitives.WriteUInt64LittleEndian(bytes, value);
|
|
Assert.True(memory.TryWrite(AtomicAddress, bytes));
|
|
}
|
|
|
|
private static ulong Read64(FakeCpuMemory memory)
|
|
{
|
|
Span<byte> bytes = stackalloc byte[sizeof(ulong)];
|
|
Assert.True(memory.TryRead(AtomicAddress, bytes));
|
|
return BinaryPrimitives.ReadUInt64LittleEndian(bytes);
|
|
}
|
|
}
|