The 92-second return to the game library
The first SNES audio run had an awkward companion: returning to the library took 92 seconds. It happened at startup and after every game, which made a working audio path feel academic; the delay also interrupted the simple act of trying a second title, because every return rebuilt a process whose visible library had barely changed. The head unit had already scanned the card once. Again, it walked the card.
The library held 7,953 titles across fourteen systems. Super Nintendo, Game Boy, and Game Boy Colour accounted for most of the wait. Their large ROM and cover sets made them a good place to look, but the first explanation only described work after browsing had begun; the repeated delay happened before a game launched, while the browser was assembling its model from the card's directory and metadata overlays, so the startup path had to be measured as its own piece of work.
The first fix changed nothing
The initial trace pointed at cover lookups. Each cover hit called stat on a
FAT directory, at about 23 milliseconds per entry. I changed the media index
to trust its directory scan rather than stat every cover again, installed the
result, and measured the next run.
Startup was 92.1 seconds. The per-system shape was almost identical to the previous run. No media-index line appeared during loading. The optimisation was valid work in a later phase, and it had no bearing on the wait at hand.
The real cost was the gamelist merge. It re-admitted every metadata overlay entry through a filesystem path check before looking it up in the tree that the initial scan had already built, repeating a card-level existence question thousands of times even though an entry absent from that tree could never become a selectable title. Super Nintendo had 1,681 overlay entries, Game Boy 1,052, Game Boy Colour 909, and Game Boy Advance 232. Each admission cost about 19 milliseconds on the card because it walked path components and canonicalised them.
I replaced that admission with a lexical check. The tree lookup decides whether the scan admitted a file, so the merge has no reason to visit the card again. A 24-hour frontend profile also uncovered a separate validation limit; the loop still rejected anything above its original 30-minute bound. Keeping the public interface bytes unchanged mattered because a prebuilt consumer module used that header, so the new declaration stayed local to the parser. The resulting change did not make a claim about files that were absent from the scan: an overlay entry still needs a matching tree node before it can become part of the library, which preserves the earlier marker-based admission rule while removing only the redundant filesystem confirmation.

The next measurement fell to 37.0 seconds per cycle. The merge removed 55
seconds, then upstream parsing exposed the remaining duplicate check:
parseGamelist called exists() for every entry before the same lookup. I
replaced that check with the lexical rule as well.
There was one more fixed cost. The frontend rebuilt its All Games collection, its alias and index before sorting it on every fresh return, paying 4.8 seconds for about 7,500 titles even when nobody opened that collection. Its count can exist without its list, so All Games now builds on first use. This also keeps global search honest: a search is a use and triggers construction.
Run 12 reached ready in 12.6 to 13.2 seconds. Super Nintendo fell from 11.7 to 1.9 seconds, Game Boy from 5.4 to 1.4, and Game Boy Colour from 5.2 to 1.4. The remaining time was 10.6 seconds across systems and about two seconds for the controller. That is a faster return path on this exact card, whose metadata-overlay sizes and FAT lookup behaviour determined the original delay; another library or filesystem needs its own timing before those figures travel any further than this unit.
The slow games were a separate question
The same run sequence exposed emulation limits that the audio sink could not explain. A PlayStation title ran at 5.4 frames per second, producing 12 seconds of sound during 113 seconds of play. A Game Boy Advance title reached 36 frames per second. Both builds were interpreters; the cores already had ARM dynamic recompilers.
The port needed executable memory before either recompiler could run. A small unit probe executed code from a page whose permissions changed at runtime and from a newly mapped executable page, then returned the expected value through both paths, which established the kernel and cache-maintenance behaviour that the two cores need before their generated ARM instructions can execute. The rebuilt PlayStation host used its ARM recompiler; the Game Boy Advance host did the same.
The later measurement matters more than the build receipt. PlayStation rose from 5.4 to 50 frames per second, with music and audio at the expected speed. Game Boy Advance rose from 36 to 56. That is 94 per cent of real time, so it still left audible gaps in the slower title. The figure puts those two changes beside the startup series because they were separate bottlenecks found in the same measurement loop.
Other limits stayed visible. Neo Geo reached 47 frames per second, but its host wrote audio outside the frame function and delivered zero audio frames. Master System reached 42 frames per second because its build still carried a cycle-stepped Z80 core. A PlayStation image hash also delayed play by about 80 seconds, so the host now checks the image size while retaining checks for the cue, manifest, and firmware material; that change targets a long pause before play rather than the frontend's already measured return-to-library cost.
The library return is now short enough to use, and the two recompilers have turned clear interpreter failures into working game paths. The next pass is more specific: repair the three remaining host faults, then improve the GBA and Neo Geo rates without confusing those with frontend startup again.