palmaris puts retroachievements on a nintendo dsi xl. the achievements of a ds game are checked on the console itself, while the game runs, with no wi-fi and no computer beside it, and when one unlocks a small line says so at the edge of the screen, without pausing the game. it is a month old, it is mine, and it has no release yet. this is how it got this far.
every number about the dsi below comes from a record palmaris keeps while it runs and writes to the card, read on the pc afterwards. a number measured on the pc says so.
retroachievements is a community that writes achievements for old games. an emulator such as retroarch checks them as the game runs, against what is in the game's memory, and sends each unlock to the site. a ds game played on a real ds or dsi has never had any of that.
a 3ds has. nds-bootstrap is the loader that runs a ds game from an sd card, and a fork of it, nds-bootstrap-ra, checks achievements while the game runs on a 3ds.1 it carries rcheevos, the library retroachievements' own emulators use for the checking, and signs in to the site from the launcher. on a dsi its achievement code returns at once: the line that does it asks the console's model, and the dsi is model zero.
taking that line out would not have been enough. the fork sets aside a quarter of a megabyte for its code, at an address that says nothing about what a dsi is using there during a game, and its sign-in sends the password without encryption. so palmaris kept the fork's idea, its loader and rcheevos, and built the rest again for the dsi, offline first: no wi-fi while playing, and the unlocks kept on the card.
the name is latin. palmaris is what is worthy of the palm, the branch a winner was handed.
a ds game owns the whole machine. it has no system beneath it to ask for memory, and it uses what it likes. palmaris needs memory no game will touch, and time that no game will miss.
the memory comes from the dsi itself. it added banks of work memory that the ds did not have, and a game made for the ds knows nothing about them. the loader takes one, copies palmaris into it, reads every word back, and seals it with a checksum. a few later games do know about those banks: one whose code names them is never set up for palmaris, and before every turn palmaris checks that its bank is still its own. while it runs, it takes the checksum of its own code again, and stops if the code has changed.
the time comes in turns. the loader already hooks a call the game makes as it plays, and palmaris runs a little at a time inside it, measured in the lines the screen draws. a frame is 263 of them, and palmaris aims at 48 at most a turn. anything longer, it cuts up: even reading a game's achievements at the start, which held new super mario bros. still for whole frames, now stops after 16 lines and goes on in the next turn.
on 24 september a build carried one achievement, built into it: ultimate destroyer, from new super mario bros., for filling the destruction bar with a mega mushroom. it fired exactly once, at the 3,740th turn. nothing was saved and nothing appeared. the only sign was in the record.
the next day went fast. the build checked all 112 of the game's core achievements at once. an unlock was written to the card when the game paused. the in-game menu got a page of achievements, with titles, points and what had unlocked. unlocks from earlier sessions were read back at the start, shown with their dates, and no longer checked. the achievements themselves moved out of the build onto the card, filed by the rom's hash, so the build carries none of them.
and the build went into twilight menu++'s slot for a second loader, set for new super mario bros. alone, so that every other game kept the stock one. that is how i have played with it since: on the dsi, from the menu, as any other game.
retroarch shows an unlock without stopping the game, and i wanted the same. the rules were set before the first try: no pause, only sprites the game is not using, and everything given back the moment the game wants it. it took ten runs on the dsi, over two days.
other games had other ways. castlevania: dawn of sorrow plays on the bottom screen, and it and kirby super star ultra park their unused sprites off the screen instead of leaving them empty. the popup learned to use either screen, to take a parked slot as free, to test where it could stand by drawing itself invisibly first, and to move when a sprite of the game was drawn across it. on 1 october it was shown in full, at the unlock, in all three games. in sonic rush a boss's sprites took every free slot on both screens, and the popup gave up for the rest of the session; now it rests and tries again.
| box | frames | left | top | right | bottom |
|---|---|---|---|---|---|
| sprite | 1–12 | 1 | 0 | 33 | 10 |
| sprite | 1 | 23 | 136 | 55 | 152 |
| sprite | 1 | 23 | 152 | 55 | 160 |
| sprite | 2 | 108 | 104 | 140 | 120 |
| sprite | 2 | 108 | 120 | 140 | 128 |
| sprite | 3 | 93 | 1 | 125 | 17 |
| sprite | 3 | 93 | 17 | 125 | 25 |
| sprite | 3 | 148 | 128 | 212 | 192 |
| sprite | 3 | 221 | 161 | 253 | 177 |
| sprite | 3 | 221 | 177 | 253 | 185 |
| sprite | 4 | 3 | 140 | 35 | 156 |
| sprite | 4 | 3 | 156 | 35 | 164 |
| sprite | 4 | 54 | 98 | 118 | 162 |
| sprite | 4 | 99 | 88 | 131 | 120 |
| sprite | 4 | 111 | 176 | 127 | 192 |
| sprite | 4 | 122 | 19 | 186 | 83 |
| sprite | 4 | 195 | 52 | 227 | 68 |
| sprite | 4 | 195 | 68 | 227 | 76 |
| sprite | 5 | 0 | 37 | 29 | 101 |
| sprite | 5 | 10 | 27 | 42 | 59 |
| sprite | 5 | 22 | 163 | 38 | 179 |
| sprite | 5 | 33 | 0 | 97 | 22 |
| sprite | 5 | 106 | 0 | 138 | 7 |
| sprite | 5 | 106 | 7 | 138 | 15 |
| sprite | 9 | 207 | 70 | 223 | 86 |
| sprite | 9 | 207 | 102 | 223 | 118 |
| sprite | 9 | 207 | 134 | 223 | 150 |
| sprite | 10 | 94 | 100 | 110 | 116 |
| sprite | 10 | 94 | 132 | 110 | 148 |
| sprite | 10 | 94 | 164 | 110 | 180 |
| popup | 3 | 8 | 32 | 145 | 48 |
| popup | 4 | 40 | 0 | 177 | 16 |
| popup | 5–9 | 40 | 168 | 177 | 184 |
| popup | 10–12 | 40 | 8 | 177 | 24 |
each frame of the strip is eight of the game's, so it plays at about the game's speed. the popup is laid on from the hundredth ring, and moved, as palmaris moves it, each time a sprite of the game is drawn across it: three times in just over a second, in a busy jungle. on the dsi this unlock was earned on 21 october, and its popup was seen.
the popup still waits, sometimes. in the last session, on 21 october, sonic rush unlocked five achievements and kirby mass attack one. palmaris counted all six popups shown in full, and i saw five. kirby's started 56 seconds after its unlock, most of that spent finding the game's copy of its sprite table and watching a screen it had just moved to, and only a little of it waiting for room.
an emulator checks every achievement on every frame. at first palmaris looked at a snapshot of the game every 16 frames, about four times a second, which is fine for a level finished and wrong for a jump that lasts three frames. a simulator on the pc, with rcheevos and made-up memory, played every achievement of new super mario bros. it could reach both ways: for 80 of those 81, sampling gave another answer somewhere.
checking everything every frame would cost about 180 of the frame's 263 lines. so palmaris reads all of the memory it watches every frame, but checks an achievement only when something it reads has changed, or when its own last check changed it. on the pc that gives the same unlocks as checking every frame, frame for frame: all 50 sets i have, 30,200,000 frames, and not one difference.3 on the dsi, new super mario bros. checks about 13 of its 112 achievements a frame that way.
| frames a second | |
|---|---|
| snapshots, every 16 frames | 3.7 |
| 27 september, run 2 | 14.7 |
| 27 september, run 3 | 17.3 |
| 27 september, run 4 | 9.9 |
| 29 september, other games' fixes | 8.3 |
| 29 september, a check skipped | 9.4 |
nine frames a second is about one in six. on the pc, a state that lasts one to eight frames is missed about half as often as with the snapshots. only every frame would close the gap, and the dsi does not have the time.
a census on the pc read each of my 124 ds roms: its retroachievements hash, its game, and whether it would pass palmaris's checks. 71 have a game on retroachievements, and of those, 57 have achievements. not every set fits in the room palmaris has, so each is weighed on the pc with palmaris's own code before it goes on the card, and a set that does not fit whole goes on with the achievements that do.
since 1 october every game that passes and has a set that fits is set to palmaris's loader. where my copy was a version retroachievements does not know, i replaced it with one it does. 70 roms are on now. five games have been seen working on the dsi: new super mario bros., castlevania: dawn of sorrow, kirby super star ultra, sonic rush and kirby mass attack. the rest have not been tried.
palmaris is not an official retroachievements client, and nothing it unlocks reaches the site. the unlocks stay on the card, game by game. an app to send them later may come, but whether the site would take unlocks earned offline is not palmaris's to promise.
what is left is in the record. three of the popup's ways round a busy screen have never drawn a popup on the dsi. a faint, constant sound in new super mario bros. came with switching the achievements on, and went away when that was cut into small turns; why the game made it was never found. and every session still ends with a check of the record, because a popup counted as shown is not always a popup seen.
| day | commits |
|---|---|
| 2026-09-22 | 62 |
| 2026-09-23 | 30 |
| 2026-09-24 | 19 |
| 2026-09-25 | 37 |
| 2026-09-26 | 52 |
| 2026-09-27 | 41 |
| 2026-09-28 | 55 |
| 2026-09-29 | 50 |
| 2026-09-30 | 29 |
| 2026-10-01 | 10 |
| 2026-10-02 | 74 |
| 2026-10-03 | 18 |
| 2026-10-21 | 15 |
Bakkerrrs/Retroachievements-DS-on-3DS, of DS-Homebrew/nds-bootstrap. palmaris builds on it at the commit read on 22 september, and keeps its notices.