sharpemu/tests/SharpEmu.ShaderCompiler.Tests/Gen5CodeEndTests.cs

65 lines
2.3 KiB
C#

// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System.Buffers.Binary;
using SharpEmu.HLE;
using Xunit;
namespace SharpEmu.ShaderCompiler.Tests;
// S_CODE_END pads the end of a shader. A block placed after the final S_ENDPGM that loops back
// with S_BRANCH leads the linear decoder into that padding.
public sealed class Gen5CodeEndTests
{
private const uint BranchScc0Over = 0xBF840002; // s_cbranch_scc0 to 0x0C
private const uint MoveScalar = 0xBE800381; // s_mov_b32 s0, 1
private const uint EndProgram = 0xBF810000; // s_endpgm
private const uint BranchBack = 0xBF82FFFC; // s_branch to 0x04
private const uint CodeEnd = 0xBF9F0000; // s_code_end
[Fact]
public void DecodeEndsAtCodeEndAfterTheLastReachableBlock()
{
var context = Context(BranchScc0Over, MoveScalar, EndProgram, MoveScalar, BranchBack, CodeEnd, CodeEnd);
Assert.True(Gen5ShaderTranslator.TryDecodeProgram(context, 0x1000, out var program, out var error), error);
Assert.Equal(
["SCbranchScc0", "SMovB32", "SEndpgm", "SMovB32", "SBranch"],
program.Instructions.Select(static instruction => instruction.Opcode));
}
[Fact]
public void CodeEndBeforeAForwardBranchTargetFails()
{
var context = Context(BranchScc0Over, CodeEnd, EndProgram, MoveScalar, EndProgram);
Assert.False(Gen5ShaderTranslator.TryDecodeProgram(context, 0x1000, out _, out var error));
Assert.Contains("code-end-inside-program", error);
}
private static CpuContext Context(params uint[] words) => new(new InstructionMemory(words), Generation.Gen5);
private sealed class InstructionMemory(uint[] words) : ICpuMemory
{
public bool TryRead(ulong address, Span<byte> destination)
{
if (address < 0x1000 || (address - 0x1000) % sizeof(uint) != 0 || destination.Length != sizeof(uint))
{
return false;
}
var index = (address - 0x1000) / sizeof(uint);
if (index >= (ulong)words.Length)
{
return false;
}
BinaryPrimitives.WriteUInt32LittleEndian(destination, words[(int)index]);
return true;
}
public bool TryWrite(ulong address, ReadOnlySpan<byte> source) => false;
}
}