mirror of
https://github.com/libretro/parallel-ps2.git
synced 2026-11-10 17:26:34 -10:00
Every GS register a game writes goes through a GIFRegHandler, and a good many of those do more than store the value. They mask a field to its real width, clamp it to a range the rest of the emulator relies on, substitute a value a particular game depends on, or maintain an invariant between two registers. Defrost restores registers with a raw copy, so none of it happens, and each of those guarantees is void for the rest of the session. Two registers were being re-normalised by hand there. That is how the ALPHA clamp came to be noticed -- it had an 81-entry table downstream of it, so it showed. The other nine did not show, which is the only reason they were still there. Doing this one register at a time is what produced the gap, so this does it in one place instead, and a new handler that transforms a value needs a line in it. What was missing: Q, twice over. GIFRegHandlerRGBAQ replaces a NaN, and the comment names Silent Hill and says it breaks the min/max in GSVertexTrace. GIFPackedRegHandlerSTQ replaces a zero, and the comment names Vexx and says it is there to avoid a division by zero. A state file put both straight back, past everything downstream written to assume they were gone -- including the conversions hardened earlier in this series. FRAME.FBW, clamped to 32 by the handler. The field is 6 bits, so a state can ask for 63, and Defrost fed the raw value into the offset recomputation and the texture cache sizes a target from FBW * 64. The FRAME/ZBUF coupling. On hardware a Z-format FRAME forces the Z buffer to colour swizzling, Powerdrome clears Z by relying on it, and both handlers re-establish it on every write to either register, so the pair is never seen uncoupled -- except after a restore, which brings them back independently and then derives offset.zb and offset.fzb4 from the mismatch. TEX0.CPSM, restricted to 1010b. Defrost calls ApplyTEX0 for the active context only, and after m_prev_env is taken, so neither copy of the other context was ever masked. UV, masked to 14 bits. GSRegs.h states the handlers are what keeps the pad bits clear; a raw restore made the header wrong. The FRAME PSM substitution for the three formats that address part of a 32-bit pixel, and the SCANMSK latch that Reset had just cleared. The call goes before m_prev_env is taken, which also fixes the ordering half of the CPSM case, and before the scissor and offset derivation, which was being computed from unnormalised FRAME and ZBUF. tests/gs/gs_restore_norm.cpp links the real GSVector.cpp and runs the handlers' own vector expressions against the scalar ones the restore path uses, bit pattern by bit pattern. The transcription was the risk here, not the idea: both handlers test Q's bit pattern rather than its value, so a negative zero is deliberately not substituted, and a scalar version written the tidier way silently disagrees on exactly that input. The test fails on it. Two things looked worse than they are and are recorded rather than changed. A restored m_tr.x/m_tr.y reaches a write-image routine as raw ints, but GSOffset::bn wraps the block number modulo GS_MAX_BLOCKS before BlockPtr indexes m_vm8, so a wild coordinate lands somewhere wrong inside the 4MB VM rather than outside it. And ZBUF.PSM reaches a shift count as m_psm[PSM].fmt * 8, which would be the dangerous shape, but fmt is 0..3 for all 64 table entries, so the shift is in range whatever the PSM.