fix(cpu): PPCBUG-123/124/125/126/161/162/566 XER TBC + lswi/stswi/lmw

Phase 6 batch 2 — XER TBC enabling + load/store-multiple cleanups.

- PPCBUG-123/124/161/566 (coupled): XER TBC field was unmodelled —
  `ctx.xer()` always returned 0 in bits 0-6, and `ctx.set_xer()`
  silently discarded any TBC writes. Result: `lswx` and `stswx` were
  permanent no-ops (the `while bytes_left > 0` loop never executed).
  Fix: add `pub xer_tbc: u8` to `PpcContext`; wire into `xer()` and
  `set_xer()`. Initialize to 0 in `PpcContext::new()`. lswx/stswx
  bodies are correct as-is once the infrastructure is wired.

- PPCBUG-125 lmw: PowerISA marks `lmw rT, D(rA)` invalid when rA is
  in [rT..31]; canary skips the write to rA to preserve the EA base.
  Now matches canary.

- PPCBUG-126/162 lswi/stswi: replaced `instr.rb()` with `instr.nb()`
  for the NB field. Both accessors return identical values today
  (bits 16-20), but the maintenance hazard from the misnomer is now
  removed. A future `rb()` type-system refactor would have broken
  lswi/stswi silently.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-05-02 13:48:03 +02:00
parent d96986a10e
commit 68c0ee55ce
2 changed files with 18 additions and 3 deletions

View File

@@ -1520,7 +1520,7 @@ fn execute(ctx: &mut PpcContext, mem: &dyn MemoryAccess, instr: &DecodedInstr) -
// String load/store
PpcOpcode::lswi => {
let mut ea = if instr.ra() == 0 { 0u32 } else { ctx.gpr[instr.ra()] as u32 };
let nb = if instr.rb() == 0 { 32 } else { instr.rb() as u32 };
let nb = if instr.nb() == 0 { 32 } else { instr.nb() };
let mut rd = instr.rd();
let mut bytes_left = nb;
while bytes_left > 0 {
@@ -1539,7 +1539,7 @@ fn execute(ctx: &mut PpcContext, mem: &dyn MemoryAccess, instr: &DecodedInstr) -
}
PpcOpcode::stswi => {
let mut ea = if instr.ra() == 0 { 0u32 } else { ctx.gpr[instr.ra()] as u32 };
let nb = if instr.rb() == 0 { 32 } else { instr.rb() as u32 };
let nb = if instr.nb() == 0 { 32 } else { instr.nb() };
let mut rs = instr.rs();
let mut bytes_left = nb;
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
@@ -1707,9 +1707,15 @@ fn execute(ctx: &mut PpcContext, mem: &dyn MemoryAccess, instr: &DecodedInstr) -
// ===== Load multiple =====
PpcOpcode::lmw => {
// PPCBUG-125: PowerISA marks `lmw` invalid when rA is in [rT..31];
// canary skips the write to rA in that case to preserve the EA base.
let mut ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
ea = ea.wrapping_add(instr.d() as i64 as u64);
for r in instr.rd()..32 {
if r == instr.ra() {
ea = ea.wrapping_add(4);
continue;
}
ctx.gpr[r] = mem.read_u32(ea as u32) as u64;
ea = ea.wrapping_add(4);
}