Fix two decisions taken from the cartridge's IPL3 - #824
Merged
LibretroAdmin merged 2 commits intoAug 22, 2026
Conversation
fe0cbbf sends a cartridge whose IPL3 checksum is not in the CIC table to the LLE RSP under "auto". The check runs in core_settings_autoselect_rsp_plugin(), reached from emu_step_initialize(), which is before main_run() byte-swaps the cartridge into host word order. cic_ipl3_known() reads native uint32, so on a little-endian host it sums transposed words. The sum matches no retail CIC, and every cartridge - retail or not - went to the LLE core. Resident Evil 2 (Europe) reads 0x000000A316ADC55A that way against the 0x000000D057C85244 the table holds for it. The consequence is not a crash: cxd4 is accurate enough that most games look right, and the two that gave it away were the ones that paint from RDRAM. RE2 shredded its pre-rendered backgrounds and language select into repeating horizontal streaks while its polygonal characters stayed correct, and Donkey Kong 64 lost the HLE-side microcode handling it needs. Setting rspplugin to hle by hand fixed both, which is what placed the fault here. Sum a corrected copy of the 0xfc0 bytes rather than the live buffer, byte-swapping when g_RomWordsLittleEndian says the cartridge has not been converted yet. Both games are correct again under "auto", and a big-endian host is unaffected.
25c769d sets RI_SELECT for every cartridge so that libdragon's open IPL3, which reads it and otherwise drives RDRAM registers this core does not model, boots. Retail IPL3 never reads it: it runs its sizing procedure unconditionally, and that procedure is what publishes osMemSize at 0x80000318 and hands the detected module count to rdram.c through s4. Telling it the RDRAM is already up makes it skip all of that, and nothing else in the core fills those in. South Park Rally shows the result. It runs 12.3M cycles, derives a memory top from a machine that reports no memory, and takes a TLB refill on 0x7ffffffc - 0x80000000 minus four, the last word of an empty RDRAM. It never programs COMPARE after that, so COMPARE_INT stays due at count 0, cycle_count is COUNT on every pass, and the dynarec re-enters gen_interrupt without ever running an instruction: one core at 100%, no output, killall required. Donkey Kong 64 fails more quietly and detects 4 MB. Gate the claim on cic_ipl3_known(), the helper fe0cbbf added for the same distinction: a cartridge whose IPL3 checksum is in the CIC table runs the retail sequence and needs nothing from us, and takes exactly the path it took before 25c769d. An unknown IPL3 keeps the claim, and since the sizing procedure no longer runs for it, osMemSize is published here instead. South Park Rally boots and Donkey Kong 64 sees 8 MB again; removing the gate reproduces both failures.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Found while porting to a Raspberry Pi 5 (aarch64, GLES 3.1). Both commits
concern code that identifies a cartridge by its IPL3 checksum and acts on
the answer; both were isolated by bisecting on real hardware.
1. The autoselect reads the checksum in the wrong byte order
core_settings_autoselect_rsp_plugin()runs fromemu_step_initialize(),before
main_run()byte-swaps the cartridge into host word order.cic_ipl3_known()reads nativeuint32, so on a little-endian host it sumstransposed words and matches no retail CIC. Every cartridge looked like
homebrew and went to the LLE core.
Resident Evil 2 (Europe) sums to
0x000000A316ADC55Athat way, against the0x000000D057C85244the table holds for it.Most games tolerate cxd4, which is why this is easy to miss. The two that
gave it away paint from RDRAM: RE2 shredded its pre-rendered backgrounds
into repeating horizontal streaks while its polygonal characters stayed
correct, and Donkey Kong 64 lost the HLE-side microcode handling it needs.
Forcing
rspplugin=hlefixed both, which is what placed the fault.2. Only claim the RDRAM is up for an IPL3 that checks
25c769d sets
RI_SELECTfor every cartridge so libdragon's open IPL3 boots.Retail IPL3 never reads it: it runs its sizing procedure unconditionally, and
that procedure publishes
osMemSizeat0x80000318and hands the detectedmodule count to
rdram.cthroughs4. Telling it the RDRAM is already upmakes it skip all of that.
South Park Rally runs 12.3M cycles, derives a memory top from a machine that
reports no memory, and takes a TLB refill on
0x7ffffffc. It never programsCOMPARE afterwards, so COMPARE_INT stays due at count 0 and the dynarec
re-enters
gen_interrupt()without running an instruction: one core at 100%,no output. Donkey Kong 64 fails more quietly and detects 4 MB.
Gating on
cic_ipl3_known()puts retail cartridges back on exactly the paththey took before 25c769d.
Verification
South Park Rally boots and Donkey Kong 64 sees 8 MB again; removing either
commit reproduces its failure.
One caveat: the
osMemSizewrite on the unknown-IPL3 branch is notexercised here, as I have no libdragon cartridge to test with. Happy to drop
it if you would rather keep the change minimal.