From 9f6cd44333e214ff21d87452b14af98ca3be586c Mon Sep 17 00:00:00 2001 From: Cyd Date: Wed, 15 Jul 2026 21:05:08 -0500 Subject: [PATCH] i860 emu: pfgt/pfle exact semantics (R bit selects gt/le, pipelined retire+advance); IGC consumption resets page write-index for reuse - pfgt/pfle/pfeq are always pipelined: fdest <- A-pipe retire, push undefined; bit7 selects pfle (inverted CC sense) -- was misread as result precision. 316 pipelined compares in the firmware previously desynced the A-pipe. - h_igcwait now models full consumption: done-status nonzero AND write-index reset (+0x7f8=0), otherwise queue pages saturate at 0x7f0 across frames and enqueueing silently stops (observed: even the per-frame marker item stopped binning by cmd ~4300). Co-Authored-By: Claude Opus 4.8 --- emulator/firmware-decomp/emu860.py | 18 ++++++++++++++---- emulator/firmware-decomp/emu_main.py | 8 ++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/emulator/firmware-decomp/emu860.py b/emulator/firmware-decomp/emu860.py index d9b2b96..c08e7a1 100644 --- a/emulator/firmware-decomp/emu860.py +++ b/emulator/firmware-decomp/emu860.py @@ -594,10 +594,20 @@ class I860: self.fwr(b0 | 1, (iv >> 32) & 0xFFFFFFFF) else: self.fwr(dest, iv & 0xFFFFFFFF) - elif sub == 0x34: # fgt: CC = src1 > src2 - self.set_cc(self.rdf(src1, sp) > self.rdf(src2, sp)) - elif sub == 0x35: # feq: CC = src1 == src2 - self.set_cc(self.rdf(src1, sp) == self.rdf(src2, sp)) + elif sub in (0x34, 0x35): # pfgt/pfle (0x34) / pfeq (0x35) + # MAME-validated: bit7 (the "R" bit) selects pfgt (0) vs pfle (1) -- + # NOT result precision. pfle CLEARS CC when src1 <= src2. These are + # always pipelined: fdest <- A-pipe retire; push undefined (0.0). + v1 = self.rdf(src1, sp); v2 = self.rdf(src2, sp) + if sub == 0x35: + self.set_cc(v1 == v2) + elif w & 0x80: # pfle + self.set_cc(not (v1 <= v2)) + else: # pfgt + self.set_cc(v1 > v2) + ap, mp = self._fp_pipes() + self._retire(dest, ap[2]) + self._padv(ap, 0.0, sp, 3) elif sub == 0x21: # fmlow.dd -- i860 FP-unit INTEGER multiply. # The i860 has no imul: ints are ixfr'd into FP regs, fmlow.dd multiplies, # and the low 32 bits (fdest) are fxfr/fst'd back. Operands are the low diff --git a/emulator/firmware-decomp/emu_main.py b/emulator/firmware-decomp/emu_main.py index 007ee90..531e590 100644 --- a/emulator/firmware-decomp/emu_main.py +++ b/emulator/firmware-decomp/emu_main.py @@ -113,8 +113,12 @@ class MainRunner: def h_igcwait(self, cpu): # Post-flush drain wait: the firmware polls [page] until the IGC writes a # NONZERO completion status after consuming it (loop at 0xf0421504: exits - # the per-page wait when [r16] != 0). We are the IGC: complete instantly. - cpu.mem.w32(cpu.rd(16), 1) + # the per-page wait when [r16] != 0). We are the IGC: complete instantly, + # and RESET the page's write index (+0x7f8) so the page is reusable -- + # otherwise pages fill at 0x7f0 across frames and enqueueing silently dies. + page = cpu.rd(16) + cpu.mem.w32(page, 1) + cpu.mem.w32(page + 0x7f8, 0) cpu.step() return True