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 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-15 21:05:08 -05:00
co-authored by Claude Opus 4.8
parent 170b35d812
commit 9f6cd44333
2 changed files with 20 additions and 6 deletions
+14 -4
View File
@@ -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
+6 -2
View File
@@ -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