diff --git a/crates/xenia-cpu/src/interpreter.rs b/crates/xenia-cpu/src/interpreter.rs index 6395b25..8239266 100644 --- a/crates/xenia-cpu/src/interpreter.rs +++ b/crates/xenia-cpu/src/interpreter.rs @@ -2783,28 +2783,38 @@ fn execute(ctx: &mut PpcContext, mem: &dyn MemoryAccess, instr: &DecodedInstr) - // ===== FPU: Rounding/Conversion ===== PpcOpcode::frspx => { - // Round to single precision honouring FPSCR[RN] + // Round to single precision honouring FPSCR[RN]. + // PPCBUG-225: set XX on inexact rounding (almost every frsp call). let b = ctx.fpr[instr.rb()]; if fpscr::is_snan(b) { fpscr::set_exception(ctx, fpscr::VXSNAN); } let result = to_single(ctx, b); + if b.is_finite() && result.is_finite() && result != b { + fpscr::set_exception(ctx, fpscr::XX); + } ctx.fpr[instr.rd()] = result; fpscr::update_after_op(ctx, result, b.is_finite()); if instr.rc_bit() { update_cr1_from_fpscr(ctx); } ctx.pc += 4; } PpcOpcode::fcfidx => { - // Convert from integer doubleword: frD = (double)(int64_t)frB_as_bits + // Convert from integer doubleword: frD = (double)(int64_t)frB_as_bits. + // PPCBUG-224: set XX when |i64| > 2^53 (precision loss in conversion). let bits = ctx.fpr[instr.rb()].to_bits(); - let result = (bits as i64) as f64; + let i = bits as i64; + let result = i as f64; + if (result as i64) != i { + fpscr::set_exception(ctx, fpscr::XX); + } ctx.fpr[instr.rd()] = result; fpscr::set_fprf(ctx, fpscr::classify_fprf(result)); if instr.rc_bit() { update_cr1_from_fpscr(ctx); } ctx.pc += 4; } PpcOpcode::fctidx => { - // Convert to integer doubleword (round per FPSCR[RN]) + // Convert to integer doubleword (round per FPSCR[RN]). + // PPCBUG-229: set XX on inexact (fractional input). let val = ctx.fpr[instr.rb()]; let result = if val.is_nan() { fpscr::set_exception(ctx, fpscr::VXCVI | if fpscr::is_snan(val) { fpscr::VXSNAN } else { 0 }); @@ -2816,6 +2826,7 @@ fn execute(ctx: &mut PpcContext, mem: &dyn MemoryAccess, instr: &DecodedInstr) - fpscr::set_exception(ctx, fpscr::VXCVI); 0x8000_0000_0000_0000u64 } else { + if val != val.trunc() { fpscr::set_exception(ctx, fpscr::XX); } fpscr::round_to_i64(ctx, val) as u64 }; ctx.fpr[instr.rd()] = f64::from_bits(result); @@ -2823,7 +2834,8 @@ fn execute(ctx: &mut PpcContext, mem: &dyn MemoryAccess, instr: &DecodedInstr) - ctx.pc += 4; } PpcOpcode::fctidzx => { - // Convert to integer doubleword (round toward zero) + // Convert to integer doubleword (round toward zero). + // PPCBUG-229: set XX on inexact. let val = ctx.fpr[instr.rb()]; let result = if val.is_nan() { fpscr::set_exception(ctx, fpscr::VXCVI | if fpscr::is_snan(val) { fpscr::VXSNAN } else { 0 }); @@ -2835,6 +2847,7 @@ fn execute(ctx: &mut PpcContext, mem: &dyn MemoryAccess, instr: &DecodedInstr) - fpscr::set_exception(ctx, fpscr::VXCVI); 0x8000_0000_0000_0000u64 } else { + if val != val.trunc() { fpscr::set_exception(ctx, fpscr::XX); } (val.trunc() as i64) as u64 }; ctx.fpr[instr.rd()] = f64::from_bits(result); @@ -2842,7 +2855,8 @@ fn execute(ctx: &mut PpcContext, mem: &dyn MemoryAccess, instr: &DecodedInstr) - ctx.pc += 4; } PpcOpcode::fctiwx => { - // Convert to integer word (round per FPSCR[RN]) + // Convert to integer word (round per FPSCR[RN]). + // PPCBUG-230: set XX on inexact. let val = ctx.fpr[instr.rb()]; let result_u32: u32 = if val.is_nan() { fpscr::set_exception(ctx, fpscr::VXCVI | if fpscr::is_snan(val) { fpscr::VXSNAN } else { 0 }); @@ -2854,6 +2868,7 @@ fn execute(ctx: &mut PpcContext, mem: &dyn MemoryAccess, instr: &DecodedInstr) - fpscr::set_exception(ctx, fpscr::VXCVI); 0x8000_0000 } else { + if val != val.trunc() { fpscr::set_exception(ctx, fpscr::XX); } fpscr::round_to_i32(ctx, val) as u32 }; ctx.fpr[instr.rd()] = f64::from_bits(result_u32 as u64); @@ -2861,7 +2876,8 @@ fn execute(ctx: &mut PpcContext, mem: &dyn MemoryAccess, instr: &DecodedInstr) - ctx.pc += 4; } PpcOpcode::fctiwzx => { - // Convert to integer word (round toward zero) + // Convert to integer word (round toward zero). + // PPCBUG-230: set XX on inexact. let val = ctx.fpr[instr.rb()]; let result_u32: u32 = if val.is_nan() { fpscr::set_exception(ctx, fpscr::VXCVI | if fpscr::is_snan(val) { fpscr::VXSNAN } else { 0 }); @@ -2873,6 +2889,7 @@ fn execute(ctx: &mut PpcContext, mem: &dyn MemoryAccess, instr: &DecodedInstr) - fpscr::set_exception(ctx, fpscr::VXCVI); 0x8000_0000 } else { + if val != val.trunc() { fpscr::set_exception(ctx, fpscr::XX); } val.trunc() as i32 as u32 }; ctx.fpr[instr.rd()] = f64::from_bits(result_u32 as u64);