From d4f6ea787b64125a430be25fe3c0212dec76c4a7 Mon Sep 17 00:00:00 2001 From: MechaCat02 Date: Sat, 2 May 2026 10:40:45 +0200 Subject: [PATCH] fix(disasm): PPCBUG-640+650 fmt_bc spurious condition suffix on bdnz/bdz MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PPCBUG-640: For BO=16 (bdnz: decrement CTR, branch if non-zero, ignore CR) and BO=18 (bdz: same with branch-if-zero), `fmt_bc` fell through to the `if decr` block and computed `cond_name_opt` from the don't-care BI=0 / cond_true=false pair, yielding `Some("ge")`. The output was therefore `bdnzge` / `bdzge` — a CTR-only branch with a spurious CR-derived suffix. PPCBUG-650 (companion): the golden fixture pinned the wrong output, so the regression had no detection signal until now. `fmt_bclr` already had the correct `if decr && uncond` guard at line 872 producing `bdnzlr` / `bdzlr`. `fmt_bc` lacked the equivalent. Fix: gate the condition string on `!uncond` inside the `if decr` block. For BO=16/18 (uncond bit set), the condition suffix is now empty. Tests: extended_mnemonics.json fixture rows for bdnz/bdz now expect the correct `ext_mnemonic: "bdnz"` / `"bdz"`. Impact: every analysis-DB query for `bdnz` loops (common in pixel-shader and vertex processing) was returning zero rows; matches stored as `bdnzge`. Co-Authored-By: Claude Sonnet 4.6 --- crates/xenia-cpu/src/disasm.rs | 5 ++++- crates/xenia-cpu/tests/golden/extended_mnemonics.json | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/xenia-cpu/src/disasm.rs b/crates/xenia-cpu/src/disasm.rs index 6edbce1..5a98981 100644 --- a/crates/xenia-cpu/src/disasm.rs +++ b/crates/xenia-cpu/src/disasm.rs @@ -828,7 +828,10 @@ fn fmt_bc(instr: &DecodedInstr) -> DisasmText { if decr { let z = if bo & 0x02 != 0 { "z" } else { "nz" }; - let cond_str = cond_name_opt.unwrap_or(""); + // BO bit 4 (uncond) means CR is ignored — pure CTR-decrement branch. + // Without this guard, bdnz/bdz would emit a spurious `ge` suffix derived + // from the don't-care BI=0 / cond_true=false pair (PPCBUG-640). + let cond_str = if uncond { "" } else { cond_name_opt.unwrap_or("") }; let ext_mnem = format!("bd{z}{cond_str}{a}{l}"); let ext_ops = format!("{cr}0x{target:08X}"); with_ext(&base_mnem, base_ops, 8, &ext_mnem, ext_ops, 8) diff --git a/crates/xenia-cpu/tests/golden/extended_mnemonics.json b/crates/xenia-cpu/tests/golden/extended_mnemonics.json index efb251b..6749f94 100644 --- a/crates/xenia-cpu/tests/golden/extended_mnemonics.json +++ b/crates/xenia-cpu/tests/golden/extended_mnemonics.json @@ -366,7 +366,7 @@ "addr": "0x82000000", "mnemonic": "bc", "operands": "16, lt, 0x82000040", - "ext_mnemonic": "bdnzge", + "ext_mnemonic": "bdnz", "ext_operands": "0x82000040", "branch_target": "0x82000040" }, @@ -376,7 +376,7 @@ "addr": "0x82000000", "mnemonic": "bc", "operands": "18, lt, 0x82000040", - "ext_mnemonic": "bdzge", + "ext_mnemonic": "bdz", "ext_operands": "0x82000040", "branch_target": "0x82000040" },