From bf202eb5d0cc4ca65a8c9fb4d9e68ec27da74aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20Unneb=C3=A4ck?= Date: Thu, 10 Sep 2026 10:28:18 +0200 Subject: [PATCH 1/2] fix neg's overflow flag The unsigned overflow of 0 - x is set for every nonzero x, but OF only belongs on the most negative value, whose negation is itself. Co-Authored-By: Claude Fable 5.1 --- runtime/src/ops/math.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/runtime/src/ops/math.rs b/runtime/src/ops/math.rs index 8e7f149c..d2a9ded5 100644 --- a/runtime/src/ops/math.rs +++ b/runtime/src/ops/math.rs @@ -66,9 +66,12 @@ pub fn or(x: I, y: I, flags: &mut Flags) -> I { /// neg: Two's Complement Negation pub fn neg(x: I, flags: &mut Flags) -> I { - let (result, of) = I::zero().overflowing_sub(&x); + let (result, _) = I::zero().overflowing_sub(&x); flags.set(Flags::ZF, result.is_zero()); flags.set(Flags::CF, !result.is_zero()); + // Only the most negative value overflows: it is the one input whose + // negation is still negative. + let of = x.high_bit().is_one() && result.high_bit().is_one(); flags.set(Flags::OF, of); flags.set(Flags::PF, result.low_byte().count_ones() % 2 == 0); result From b3c4f78c16b0d70a2a8743434a6c03e60c3e4332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20Unneb=C3=A4ck?= Date: Thu, 10 Sep 2026 10:28:31 +0200 Subject: [PATCH 2/2] fix neg's sign flag neg set ZF, CF, OF and PF from the result but never SF. Co-Authored-By: Claude Fable 5.1 --- runtime/src/ops/math.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/runtime/src/ops/math.rs b/runtime/src/ops/math.rs index d2a9ded5..cb81f858 100644 --- a/runtime/src/ops/math.rs +++ b/runtime/src/ops/math.rs @@ -68,6 +68,7 @@ pub fn or(x: I, y: I, flags: &mut Flags) -> I { pub fn neg(x: I, flags: &mut Flags) -> I { let (result, _) = I::zero().overflowing_sub(&x); flags.set(Flags::ZF, result.is_zero()); + flags.set(Flags::SF, result.high_bit().is_one()); flags.set(Flags::CF, !result.is_zero()); // Only the most negative value overflows: it is the one input whose // negation is still negative. @@ -183,6 +184,31 @@ mod tests { (result, flags) } + fn neg8(x: u8) -> (u8, Flags) { + let mut flags = Flags::default(); + let result = neg(x, &mut flags); + (result, flags) + } + + #[test] + fn neg_flags() { + let (result, flags) = neg8(0x00); + assert_eq!(result, 0x00); + assert_eq!(flags.to_string(), "PF ZF"); + + let (result, flags) = neg8(0x01); + assert_eq!(result, 0xff); + assert_eq!(flags.to_string(), "CF PF SF"); + + let (result, flags) = neg8(0xff); + assert_eq!(result, 0x01); + assert_eq!(flags.to_string(), "CF"); + + let (result, flags) = neg8(0x80); + assert_eq!(result, 0x80); + assert_eq!(flags.to_string(), "CF SF OF"); + } + #[test] fn sbb_edge_cases() { let (result, flags) = sbb8(0x00, 0x7f, true);