From e220152c831df73566c6ca90746d9f1fb2d2f0b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20Unneb=C3=A4ck?= Date: Thu, 10 Sep 2026 10:07:05 +0200 Subject: [PATCH 1/2] cargo fmt Co-Authored-By: Claude Fable 5.1 --- tc/src/codegen/control_flow.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tc/src/codegen/control_flow.rs b/tc/src/codegen/control_flow.rs index 951a0d3e..5f80215a 100644 --- a/tc/src/codegen/control_flow.rs +++ b/tc/src/codegen/control_flow.rs @@ -133,8 +133,7 @@ impl<'a> CodeGen<'a> { )); } Je | Jne | Jb | Js | Jns | Ja | Jae | Jl | Jg | Jge | Jecxz | Jle | Jbe | Jcxz | Jp - | Jnp | Jo | Jno - | Loop | Loopne => { + | Jnp | Jo | Jno | Loop | Loopne => { let next = self.resolve_jmp(instr.next_ip()); let (None, None, cont) = self.jmp_target(instr) else { panic!() From 99b6e473b8045bce3ef08424b28c3a3ed0cb733e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20Unneb=C3=A4ck?= Date: Thu, 10 Sep 2026 10:07:20 +0200 Subject: [PATCH 2/2] report unordered fcom results in the status word fcom compared with total_cmp, which always produces an ordering, so the status word could never carry C2. After fnstsw/sahf that bit becomes PF, and the jp/jnp MSVC emits around every float compare exists precisely to catch it: a NaN operand looked like an ordered result. total_cmp also ordered -0.0 below +0.0 where the hardware calls them equal. Compare with partial_cmp instead and map None to C0|C2|C3, as the FPU does. The checked-in chillin and mofo output assigns fpu.cmp directly, so it is updated in the same step; the inputs to rerun tc are not in the repo, so the change was applied by hand and formatted with rustfmt, which is what tc would have emitted. Co-Authored-By: Claude Fable 5.1 --- out/chillin/src/generated/part_00.rs | 30 ++++---- out/chillin/src/generated/part_01.rs | 10 +-- out/mofo/src/generated/part_00.rs | 104 +++++++++++++-------------- out/mofo/src/generated/part_01.rs | 28 ++++---- runtime/src/fpu.rs | 54 ++++++++++++-- tc/src/codegen/fpu.rs | 2 +- 6 files changed, 135 insertions(+), 93 deletions(-) diff --git a/out/chillin/src/generated/part_00.rs b/out/chillin/src/generated/part_00.rs index 36ebcb15..bddd20ae 100644 --- a/out/chillin/src/generated/part_00.rs +++ b/out/chillin/src/generated/part_00.rs @@ -10842,7 +10842,7 @@ pub fn x403a5e(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x40c080u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x40c080u32) as f64)); ctx.cpu.fpu.pop(); // 00403a6b fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -10863,7 +10863,7 @@ pub fn x403a70(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x40c080u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x40c080u32) as f64)); ctx.cpu.fpu.pop(); // 00403a79 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -10908,7 +10908,7 @@ pub fn x403a98(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x40c084u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x40c084u32) as f64)); ctx.cpu.fpu.pop(); // 00403aa1 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -10929,7 +10929,7 @@ pub fn x403aa6(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x40c084u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x40c084u32) as f64)); ctx.cpu.fpu.pop(); // 00403aaf fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -10974,7 +10974,7 @@ pub fn x403ace(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x40c080u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x40c080u32) as f64)); ctx.cpu.fpu.pop(); // 00403ad7 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -11057,7 +11057,7 @@ pub fn x403b02(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x40c080u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x40c080u32) as f64)); ctx.cpu.fpu.pop(); // 00403b0b fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -11288,7 +11288,7 @@ pub fn x403b94(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x40c084u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x40c084u32) as f64)); ctx.cpu.fpu.pop(); // 00403b9d fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -11371,7 +11371,7 @@ pub fn x403bc8(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x40c084u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x40c084u32) as f64)); ctx.cpu.fpu.pop(); // 00403bd1 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -11602,7 +11602,7 @@ pub fn x403c5a(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x40c080u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x40c080u32) as f64)); ctx.cpu.fpu.pop(); // 00403c63 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -11623,7 +11623,7 @@ pub fn x403c6c(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x40c080u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x40c080u32) as f64)); ctx.cpu.fpu.pop(); // 00403c75 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -11668,7 +11668,7 @@ pub fn x403c8c(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x40c084u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x40c084u32) as f64)); ctx.cpu.fpu.pop(); // 00403c95 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -11689,7 +11689,7 @@ pub fn x403c9a(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x40c084u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x40c084u32) as f64)); ctx.cpu.fpu.pop(); // 00403ca3 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -14059,7 +14059,7 @@ pub fn x4045b8(ctx: &mut Context) -> Cont { ); ctx.cpu.fpu.pop(); // 00404643 fcomp dword ptr [ebp-0Ch] - ctx.cpu.fpu.cmp = ctx.cpu.fpu.get(0).total_cmp( + ctx.cpu.fpu.cmp = ctx.cpu.fpu.get(0).partial_cmp( &(ctx .memory .read::(ctx.cpu.regs.ebp.wrapping_add(0xfffffff4u32)) as f64), @@ -15039,7 +15039,7 @@ pub fn x404979(ctx: &mut Context) -> Cont { ); ctx.cpu.fpu.pop(); // 0040497c fcomp qword ptr [ebp-20h] - ctx.cpu.fpu.cmp = ctx.cpu.fpu.get(0).total_cmp( + ctx.cpu.fpu.cmp = ctx.cpu.fpu.get(0).partial_cmp( &(ctx .memory .read::(ctx.cpu.regs.ebp.wrapping_add(0xffffffe0u32))), @@ -18320,7 +18320,7 @@ pub fn x4053fc(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x40c1bcu32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x40c1bcu32) as f64)); ctx.cpu.fpu.pop(); // 00405427 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); diff --git a/out/chillin/src/generated/part_01.rs b/out/chillin/src/generated/part_01.rs index 2ef7f6e1..7855fdad 100644 --- a/out/chillin/src/generated/part_01.rs +++ b/out/chillin/src/generated/part_01.rs @@ -4411,7 +4411,7 @@ pub fn x4061f6(ctx: &mut Context) -> Cont { // 0040621f fldz ctx.cpu.fpu.push(0.0); // 00406221 fcomp dword ptr [eax+8] - ctx.cpu.fpu.cmp = ctx.cpu.fpu.get(0).total_cmp( + ctx.cpu.fpu.cmp = ctx.cpu.fpu.get(0).partial_cmp( &(ctx .memory .read::(ctx.cpu.regs.eax.wrapping_add(0x8u32)) as f64), @@ -4438,7 +4438,7 @@ pub fn x406229(ctx: &mut Context) -> Cont { // 0040622f fldz ctx.cpu.fpu.push(0.0); // 00406231 fcomp dword ptr [eax+8] - ctx.cpu.fpu.cmp = ctx.cpu.fpu.get(0).total_cmp( + ctx.cpu.fpu.cmp = ctx.cpu.fpu.get(0).partial_cmp( &(ctx .memory .read::(ctx.cpu.regs.eax.wrapping_add(0x8u32)) as f64), @@ -4463,7 +4463,7 @@ pub fn x406239(ctx: &mut Context) -> Cont { // 0040623d fldz ctx.cpu.fpu.push(0.0); // 0040623f fcomp dword ptr [edi+eax+8] - ctx.cpu.fpu.cmp = ctx.cpu.fpu.get(0).total_cmp( + ctx.cpu.fpu.cmp = ctx.cpu.fpu.get(0).partial_cmp( &(ctx.memory.read::( ctx.cpu .regs @@ -10332,7 +10332,7 @@ pub fn x407be7(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x40c628u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x40c628u32) as f64)); ctx.cpu.fpu.pop(); // 00407c1f fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -10439,7 +10439,7 @@ pub fn x407c2e(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x40c640u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x40c640u32) as f64)); ctx.cpu.fpu.pop(); // 00407c8a fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); diff --git a/out/mofo/src/generated/part_00.rs b/out/mofo/src/generated/part_00.rs index bcf13029..e2bad87a 100644 --- a/out/mofo/src/generated/part_00.rs +++ b/out/mofo/src/generated/part_00.rs @@ -326,7 +326,7 @@ pub fn x401000(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 0040116e mov [esp+7Ch],ecx ctx.memory @@ -373,7 +373,7 @@ pub fn x401197(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 004011a1 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -407,7 +407,7 @@ pub fn x4011b1(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 004011bb add eax,eax ctx.cpu.regs.eax = add(ctx.cpu.regs.eax, ctx.cpu.regs.eax, &mut ctx.cpu.flags); @@ -445,7 +445,7 @@ pub fn x4011cf(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 004011d9 lea edx,[eax*4] ctx.cpu.regs.edx = (ctx.cpu.regs.eax * 4); @@ -483,7 +483,7 @@ pub fn x4011f2(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 004011fc shl eax,3 ctx.cpu.regs.eax = shl(ctx.cpu.regs.eax, 0x3u8, &mut ctx.cpu.flags); @@ -521,7 +521,7 @@ pub fn x401211(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 0040121b shl eax,4 ctx.cpu.regs.eax = shl(ctx.cpu.regs.eax, 0x4u8, &mut ctx.cpu.flags); @@ -559,7 +559,7 @@ pub fn x401230(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 0040123d shl eax,5 ctx.cpu.regs.eax = shl(ctx.cpu.regs.eax, 0x5u8, &mut ctx.cpu.flags); @@ -592,7 +592,7 @@ pub fn x401252(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 00401258 shl eax,6 ctx.cpu.regs.eax = shl(ctx.cpu.regs.eax, 0x6u8, &mut ctx.cpu.flags); @@ -1576,7 +1576,7 @@ pub fn x401560(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 004016ce mov [esp+7Ch],ecx ctx.memory @@ -1623,7 +1623,7 @@ pub fn x4016f7(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 00401701 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -1657,7 +1657,7 @@ pub fn x401711(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 0040171b add eax,eax ctx.cpu.regs.eax = add(ctx.cpu.regs.eax, ctx.cpu.regs.eax, &mut ctx.cpu.flags); @@ -1695,7 +1695,7 @@ pub fn x40172f(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 00401739 lea edx,[eax*4] ctx.cpu.regs.edx = (ctx.cpu.regs.eax * 4); @@ -1733,7 +1733,7 @@ pub fn x401752(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 0040175c shl eax,3 ctx.cpu.regs.eax = shl(ctx.cpu.regs.eax, 0x3u8, &mut ctx.cpu.flags); @@ -1771,7 +1771,7 @@ pub fn x401771(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 0040177b shl eax,4 ctx.cpu.regs.eax = shl(ctx.cpu.regs.eax, 0x4u8, &mut ctx.cpu.flags); @@ -1809,7 +1809,7 @@ pub fn x401790(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 0040179d shl eax,5 ctx.cpu.regs.eax = shl(ctx.cpu.regs.eax, 0x5u8, &mut ctx.cpu.flags); @@ -1842,7 +1842,7 @@ pub fn x4017b2(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 004017b8 shl eax,6 ctx.cpu.regs.eax = shl(ctx.cpu.regs.eax, 0x6u8, &mut ctx.cpu.flags); @@ -3536,7 +3536,7 @@ pub fn x401ea0(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 00401ee3 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -3741,7 +3741,7 @@ pub fn x401f90(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 00401f9a sub esp,240h ctx.cpu.regs.esp = sub(ctx.cpu.regs.esp, 0x240u32, &mut ctx.cpu.flags); @@ -3785,7 +3785,7 @@ pub fn x401fb4(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 00401fc1 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -3819,7 +3819,7 @@ pub fn x401fd1(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 00401fde fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -3853,7 +3853,7 @@ pub fn x401fee(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 00401ffb fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -7929,7 +7929,7 @@ pub fn x40342e(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x4200d0u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x4200d0u32) as f64)); ctx.cpu.fpu.pop(); // 00403438 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -8233,7 +8233,7 @@ pub fn x403443(ctx: &mut Context) -> Cont { .read::(ctx.cpu.regs.esp.wrapping_add(0x20u32)) as f64, ); // 00403526 fcomp - ctx.cpu.fpu.cmp = ctx.cpu.fpu.get(0).total_cmp(&(ctx.cpu.fpu.get(1))); + ctx.cpu.fpu.cmp = ctx.cpu.fpu.get(0).partial_cmp(&(ctx.cpu.fpu.get(1))); ctx.cpu.fpu.pop(); // 00403528 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -8262,7 +8262,7 @@ pub fn x403535(ctx: &mut Context) -> Cont { .read::(ctx.cpu.regs.esp.wrapping_add(0x24u32)) as f64, ); // 00403539 fcomp - ctx.cpu.fpu.cmp = ctx.cpu.fpu.get(0).total_cmp(&(ctx.cpu.fpu.get(1))); + ctx.cpu.fpu.cmp = ctx.cpu.fpu.get(0).partial_cmp(&(ctx.cpu.fpu.get(1))); ctx.cpu.fpu.pop(); // 0040353b fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -8296,7 +8296,7 @@ pub fn x403551(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x4200c8u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x4200c8u32) as f64)); ctx.cpu.fpu.pop(); // 0040355b fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -8398,7 +8398,7 @@ pub fn x4035a0(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x4200bcu32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x4200bcu32) as f64)); ctx.cpu.fpu.pop(); // 004035aa fild dword ptr [esp+10h] ctx.cpu.fpu.push( @@ -8609,7 +8609,7 @@ pub fn x403678(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); // 00403682 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); // 00403684 test ah,1 @@ -8634,7 +8634,7 @@ pub fn x403693(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420120u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420120u32) as f64)); // 00403699 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); // 0040369b test ah,41h @@ -8744,7 +8744,7 @@ pub fn x4036f2(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); // 00403700 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); // 00403702 test ah,1 @@ -8769,7 +8769,7 @@ pub fn x403711(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420118u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420118u32) as f64)); // 00403717 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); // 00403719 test ah,41h @@ -9089,7 +9089,7 @@ pub fn x40386a(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420150u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420150u32) as f64)); ctx.cpu.fpu.pop(); // 00403874 add esp,4 ctx.cpu.regs.esp = add(ctx.cpu.regs.esp, 0x4u32, &mut ctx.cpu.flags); @@ -9164,7 +9164,7 @@ pub fn x4038b4(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x42012cu32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x42012cu32) as f64)); ctx.cpu.fpu.pop(); // 004038be add esp,8 ctx.cpu.regs.esp = add(ctx.cpu.regs.esp, 0x8u32, &mut ctx.cpu.flags); @@ -9277,7 +9277,7 @@ pub fn x403935(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420124u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420124u32) as f64)); ctx.cpu.fpu.pop(); // 00403945 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -10384,7 +10384,7 @@ pub fn x403d69(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); ctx.cpu.fpu.pop(); // 00403d7b fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -10405,7 +10405,7 @@ pub fn x403d86(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420230u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420230u32) as f64)); ctx.cpu.fpu.pop(); // 00403d90 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -10514,7 +10514,7 @@ pub fn x403d9b(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420218u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420218u32) as f64)); ctx.cpu.fpu.pop(); // 00403def fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -10861,7 +10861,7 @@ pub fn x403f30(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420234u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420234u32) as f64)); ctx.cpu.fpu.pop(); // 00403f40 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -12539,7 +12539,7 @@ pub fn x404651(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420200u32))); + .partial_cmp(&(ctx.memory.read::(0x420200u32))); ctx.cpu.fpu.pop(); // 00404657 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -13447,7 +13447,7 @@ pub fn x404a08(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x4200c8u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x4200c8u32) as f64)); ctx.cpu.fpu.pop(); // 00404a18 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -14120,7 +14120,7 @@ pub fn x404c94(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); // 00404caf fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); // 00404cb1 test ah,1 @@ -14145,7 +14145,7 @@ pub fn x404cc0(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420120u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420120u32) as f64)); // 00404cc6 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); // 00404cc8 test ah,41h @@ -14259,7 +14259,7 @@ pub fn x404d1b(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); // 00404d39 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); // 00404d3b test ah,1 @@ -14284,7 +14284,7 @@ pub fn x404d4a(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420118u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420118u32) as f64)); // 00404d50 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); // 00404d52 test ah,41h @@ -14762,7 +14762,7 @@ pub fn x404f2c(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420350u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420350u32) as f64)); ctx.cpu.fpu.pop(); // 00404f36 add esp,30h ctx.cpu.regs.esp = add(ctx.cpu.regs.esp, 0x30u32, &mut ctx.cpu.flags); @@ -14854,7 +14854,7 @@ pub fn x404f83(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x4200d0u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x4200d0u32) as f64)); ctx.cpu.fpu.pop(); // 00404f93 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -16035,7 +16035,7 @@ pub fn x4053b9(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); // 004053d3 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); // 004053d5 test ah,1 @@ -16060,7 +16060,7 @@ pub fn x4053e4(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420120u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420120u32) as f64)); // 004053ea fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); // 004053ec test ah,41h @@ -16183,7 +16183,7 @@ pub fn x405443(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); // 00405459 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); // 0040545b test ah,1 @@ -16208,7 +16208,7 @@ pub fn x40546a(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420118u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420118u32) as f64)); // 00405470 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); // 00405472 test ah,41h @@ -17307,7 +17307,7 @@ pub fn x405892(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420238u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420238u32) as f64)); ctx.cpu.fpu.pop(); // 004058a2 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -17327,7 +17327,7 @@ pub fn x4058ad(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x42023cu32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x42023cu32) as f64)); ctx.cpu.fpu.pop(); // 004058b7 fld dword ptr [esp] ctx.cpu @@ -17520,7 +17520,7 @@ pub fn x405965(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x4203ecu32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x4203ecu32) as f64)); ctx.cpu.fpu.pop(); // 0040596f fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); diff --git a/out/mofo/src/generated/part_01.rs b/out/mofo/src/generated/part_01.rs index 484b35dd..76ba1624 100644 --- a/out/mofo/src/generated/part_01.rs +++ b/out/mofo/src/generated/part_01.rs @@ -87,7 +87,7 @@ pub fn x405b2d(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420224u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420224u32) as f64)); ctx.cpu.fpu.pop(); // 00405b49 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -116,7 +116,7 @@ pub fn x405b5a(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420128u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420128u32) as f64)); ctx.cpu.fpu.pop(); // 00405b64 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -137,7 +137,7 @@ pub fn x405b6b(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420424u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420424u32) as f64)); ctx.cpu.fpu.pop(); // 00405b75 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -158,7 +158,7 @@ pub fn x405b7c(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420424u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420424u32) as f64)); ctx.cpu.fpu.pop(); // 00405b86 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -179,7 +179,7 @@ pub fn x405b8d(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420420u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420420u32) as f64)); ctx.cpu.fpu.pop(); // 00405b97 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -200,7 +200,7 @@ pub fn x405b9e(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x42041cu32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x42041cu32) as f64)); ctx.cpu.fpu.pop(); // 00405ba8 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -1153,7 +1153,7 @@ pub fn x405ed1(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); // 00405f48 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); // 00405f4a test ah,41h @@ -1289,7 +1289,7 @@ pub fn x405fdc(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x420098u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x420098u32) as f64)); // 00405fe2 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); // 00405fe4 test ah,1 @@ -3010,7 +3010,7 @@ pub fn x406610(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x42049cu32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x42049cu32) as f64)); ctx.cpu.fpu.pop(); // 00406632 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -6452,7 +6452,7 @@ pub fn x407397(ctx: &mut Context) -> Cont { .read::(ctx.cpu.regs.esi.wrapping_add(0xfffffff4u32)) as f64, ); // 0040739a fcomp dword ptr [esi-8] - ctx.cpu.fpu.cmp = ctx.cpu.fpu.get(0).total_cmp( + ctx.cpu.fpu.cmp = ctx.cpu.fpu.get(0).partial_cmp( &(ctx .memory .read::(ctx.cpu.regs.esi.wrapping_add(0xfffffff8u32)) as f64), @@ -11397,7 +11397,7 @@ pub fn x41e7a5(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x4204c8u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x4204c8u32) as f64)); ctx.cpu.fpu.pop(); // 0041e7af fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -11420,7 +11420,7 @@ pub fn x41e7be(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x4204c8u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x4204c8u32) as f64)); // 0041e7c4 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); // 0041e7c6 test ah,1 @@ -11449,7 +11449,7 @@ pub fn x41e7d3(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x4204c0u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x4204c0u32) as f64)); ctx.cpu.fpu.pop(); // 0041e7dd fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); @@ -11472,7 +11472,7 @@ pub fn x41e7ec(ctx: &mut Context) -> Cont { .cpu .fpu .get(0) - .total_cmp(&(ctx.memory.read::(0x4204c0u32) as f64)); + .partial_cmp(&(ctx.memory.read::(0x4204c0u32) as f64)); // 0041e7f2 fnstsw ax ctx.cpu.regs.set_ax(ctx.cpu.fpu.status()); // 0041e7f4 test ah,41h diff --git a/runtime/src/fpu.rs b/runtime/src/fpu.rs index c6a40235..919ff6c8 100644 --- a/runtime/src/fpu.rs +++ b/runtime/src/fpu.rs @@ -16,8 +16,9 @@ pub struct FPU { pub st: [f64; 8], /// Index of top of FPU stack; 8 when stack empty. pub st_top: usize, - /// The result of the last fcmp, used to generate status word. - pub cmp: std::cmp::Ordering, + /// The result of the last fcom, used to generate the status word. + /// None is unordered: one of the operands was a NaN. + pub cmp: Option, /// Control word, as managed by fldcw/fnstcw. We only round-trip the value; /// precision/rounding control bits are not honored. pub control: u16, @@ -28,7 +29,7 @@ impl Default for FPU { Self { st: [0.; 8], st_top: 8, - cmp: std::cmp::Ordering::Equal, + cmp: Some(std::cmp::Ordering::Equal), control: 0x037f, } } @@ -93,9 +94,12 @@ impl FPU { pub fn status(&self) -> u16 { let status = match self.cmp { - std::cmp::Ordering::Less => Status::C0, - std::cmp::Ordering::Equal => Status::C3, - std::cmp::Ordering::Greater => Status::empty(), + Some(std::cmp::Ordering::Less) => Status::C0, + Some(std::cmp::Ordering::Equal) => Status::C3, + Some(std::cmp::Ordering::Greater) => Status::empty(), + // Unordered sets all three. After fnstsw/sahf, C2 lands in PF, + // which is what the jp following a float compare tests for. + None => Status::C0 | Status::C2 | Status::C3, }; // Our status register impl doesn't include st_top so include it here. let mut status = status.bits(); @@ -109,3 +113,41 @@ impl FPU { val.round_ties_even() } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::Flags; + + fn condition_codes(fpu: &FPU) -> u16 { + fpu.status() & Status::all().bits() + } + + #[test] + fn compare_condition_codes() { + let mut fpu = FPU::default(); + fpu.cmp = 1.0f64.partial_cmp(&2.0); + assert_eq!(condition_codes(&fpu), Status::C0.bits()); + fpu.cmp = 2.0f64.partial_cmp(&2.0); + assert_eq!(condition_codes(&fpu), Status::C3.bits()); + fpu.cmp = 2.0f64.partial_cmp(&1.0); + assert_eq!(condition_codes(&fpu), 0); + fpu.cmp = f64::NAN.partial_cmp(&1.0); + assert_eq!( + condition_codes(&fpu), + (Status::C0 | Status::C2 | Status::C3).bits() + ); + } + + #[test] + fn unordered_reaches_parity_flag() { + // fnstsw ax; sahf copies the high byte of the status word into flags. + let mut fpu = FPU::default(); + fpu.cmp = 1.0f64.partial_cmp(&1.0); + let ah = (fpu.status() >> 8) as u32; + assert!(!Flags::from_bits_retain(ah).contains(Flags::PF)); + fpu.cmp = f64::NAN.partial_cmp(&1.0); + let ah = (fpu.status() >> 8) as u32; + assert!(Flags::from_bits_retain(ah).contains(Flags::PF)); + } +} diff --git a/tc/src/codegen/fpu.rs b/tc/src/codegen/fpu.rs index 3746d0cc..1d19e132 100644 --- a/tc/src/codegen/fpu.rs +++ b/tc/src/codegen/fpu.rs @@ -194,7 +194,7 @@ impl<'a> CodeGen<'a> { _ => unreachable!(), }; self.line(format!( - "ctx.cpu.fpu.cmp = {}.total_cmp(&({}));", + "ctx.cpu.fpu.cmp = {}.partial_cmp(&({}));", arg0, arg1 )); if instr.mnemonic() == Fcomp {