Firmware 4.03 re-disassembly of the drawup bodies fixes three
transliteration errors:
- Option1Flags[1] is 0x3000, not 0x2000: the option1 jump table at
0x37fcc routes index 1 to case code that ors 0x2000 then falls into
or 0x1000.
- Sealed paths run the shared error epilogue (store at frame+4 and
invoke the registered callback), like every other error path; use
DrawupRecordError instead of a bare return.
- DrawupPoolAllocate returns memory + completingNodeIndex*slotSize
(0x1bcc0 marks slots start..start+count but returns the node whose
span finished the run), not the run's first slot.
The 13.42 emulator report matches on all three; its callback
suppression and +0x20 pool entries are firmware differences, not
deviations here.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
DrawupPackColor converted channel floats with static_cast<int32_t>,
which is undefined for NaN — the same input DrawupColorValid admits.
The firmware bodies pack with vcvttss2si into 64-bit registers, whose
invalid-conversion result is 0x8000000000000000; the low dword the
packing ORs in is 0, so an unrepresentable channel contributes a zero
byte. Implement the conversion explicitly: in-range values truncate
through int64_t, everything else contributes 0. The FlatColor NaN
regression now checks the firmware-faithful packed word (0x00000000
instead of the int32-indefinite 0x80000000 the old codegen happened to
emit). Verified under -fsanitize=float-cast-overflow
-fno-sanitize-recover on Linux: clean run, test passes.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Firmware 4.03 re-disassembly of the drawup bodies fixes three
transliteration errors:
- Option1Flags[1] is 0x3000, not 0x2000: the option1 jump table at
0x37fcc routes index 1 to case code that ors 0x2000 then falls into
or 0x1000.
- Sealed paths run the shared error epilogue (store at frame+4 and
invoke the registered callback), like every other error path; use
DrawupRecordError instead of a bare return.
- DrawupPoolAllocate returns memory + completingNodeIndex*slotSize
(0x1bcc0 marks slots start..start+count but returns the node whose
span finished the run), not the run's first slot.
The 13.42 emulator report matches on all three; its callback
suppression and +0x20 pool entries are firmware differences, not
deviations here.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Firmware 4.03 bodies at 0x20cb0 (image) and 0x20f50 (pattern) recovered:
frame magic/sticky/parameter checks with error store at +4 and hook,
record-pool u16 free-run allocation under the pool mutex, 0x80 record
header (type 0x40/0x50, sequence, payload at +0x80), descriptor memcpy
of mode bytes, and same-sequence queue append bumping frame+0x28.
libSceFont links libkernel for scePthreadMutexLock/Unlock; guest test
covers validation, record bytes, queue order and the allocator.
Firmware 4.03 body at 0x20f50 takes ten integer arguments with the\noutput pointer last - the PPSA20397 libSceFontGsm.prx caller rotates\nsix registers and pushes four stack arguments, no float registers.\nUpdate the declaration, stub and signature assertion, and record the\nverified offsets and record/queue semantics in the debt file.