From 20ef08b3b847627156d25ab2f6bb26cceee32118 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 13:29:26 +0000 Subject: [PATCH] Decode the first GEN_MODE write even when it is all zeros The BP register cache skips writes equal to the cached value, and the cache starts at zero. A cold GEN_MODE write of zero (one TEV stage, no culling, no channels or texgens) was therefore dropped, leaving the decoded state at zero TEV stages until some other GEN_MODE value arrived. Seed the cache's register ID byte so the first write always decodes. From upstream patchzyy/Wiicompiled 6f14bde (#244, KartPad batch). Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01Wg7mB8ogCWmp9GH19Uc82B --- aurora-main/lib/gx/gx.hpp | 2 ++ aurora-main/tests/gx_fifo_test.cpp | 45 ++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/aurora-main/lib/gx/gx.hpp b/aurora-main/lib/gx/gx.hpp index 778c23e..84b45f0 100644 --- a/aurora-main/lib/gx/gx.hpp +++ b/aurora-main/lib/gx/gx.hpp @@ -439,6 +439,8 @@ struct GXState { u32 pipelineStateGeneration = next_gx_state_epoch(); std::array bpRegCache = [] { std::array regs{}; + // Force the first GEN_MODE decode without changing its masked reset value. + regs[0x00] = 0xFF000000; regs[0xFE] = 0x00FFFFFF; return regs; }(); diff --git a/aurora-main/tests/gx_fifo_test.cpp b/aurora-main/tests/gx_fifo_test.cpp index 6433551..80a458d 100644 --- a/aurora-main/tests/gx_fifo_test.cpp +++ b/aurora-main/tests/gx_fifo_test.cpp @@ -1390,6 +1390,51 @@ TEST_F(GXFifoTest, BlendMode_Logic) { EXPECT_EQ(g_gxState.blendOp, GX_LO_XOR); } +TEST_F(GXFifoTest, GenMode_FirstZeroWriteDecodesAndRepeatDeduplicates) { + reset_gx_state(); + const auto before = g_gxState.pipelineStateGeneration; + decode_fifo(bp_cmd(0, 0)); + EXPECT_EQ(g_gxState.numTevStages, 1u); + EXPECT_EQ(g_gxState.cullMode, GX_CULL_NONE); + EXPECT_EQ(g_gxState.numChans, 0u); + EXPECT_EQ(g_gxState.numTexGens, 0u); + EXPECT_EQ(g_gxState.numIndStages, 0u); + EXPECT_EQ(g_gxState.bpRegCache[0], 0u); + EXPECT_NE(g_gxState.pipelineStateGeneration, before); + const auto decoded = g_gxState.pipelineStateGeneration; + decode_fifo(bp_cmd(0, 0)); + EXPECT_EQ(g_gxState.pipelineStateGeneration, decoded); +} + +TEST_F(GXFifoTest, GenMode_FirstMaskedWritePreservesZeroResetBits) { + for (const u32 mask : {0u, 1u << 10}) { + reset_gx_state(); + const auto before = g_gxState.pipelineStateGeneration; + decode_fifo(bp_cmd(0xFE, mask)); + decode_fifo(bp_cmd(0, 0xFFFFFF)); + EXPECT_EQ(g_gxState.bpRegCache[0], mask); + EXPECT_EQ(g_gxState.bpRegCache[0xFE], 0xFFFFFFu); + EXPECT_EQ(g_gxState.numTevStages, mask ? 2u : 1u); + EXPECT_EQ(g_gxState.cullMode, GX_CULL_NONE); + EXPECT_NE(g_gxState.pipelineStateGeneration, before); + decode_fifo(bp_cmd(0, 0)); + EXPECT_EQ(g_gxState.numTevStages, 1u); + EXPECT_EQ(g_gxState.bpRegCache[0], 0u); + } +} + +TEST_F(GXFifoTest, GenMode_ColdSingleStageApiSetupDecodes) { + reset_gx_state(); + GXSetNumTevStages(1); + GXSetNumTexGens(0); + GXSetNumChans(0); + GXSetCullMode(GX_CULL_NONE); + const auto bytes = flush_and_capture(); + decode_fifo(bytes); + EXPECT_EQ(g_gxState.numTevStages, 1u); + EXPECT_EQ(g_gxState.cullMode, GX_CULL_NONE); +} + TEST_F(GXFifoTest, BpMask_AppliesOnlyToNextWrite) { std::vector bytes; auto mask = bp_cmd(0xFE, 1u << 19);