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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wg7mB8ogCWmp9GH19Uc82B
This commit is contained in:
Claude committed 2026-10-05 13:29:26 +00:00
1 parent b361ecd54c
commit 20ef08b3b8
2 files changed
+47

No files matched your search

+2
View File
@@ -439,6 +439,8 @@ struct GXState {
u32 pipelineStateGeneration = next_gx_state_epoch();
std::array<u32, 0x100> bpRegCache = [] {
std::array<u32, 0x100> regs{};
// Force the first GEN_MODE decode without changing its masked reset value.
regs[0x00] = 0xFF000000;
regs[0xFE] = 0x00FFFFFF;
return regs;
}();
+45
View File
@@ -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<u8> bytes;
auto mask = bp_cmd(0xFE, 1u << 19);