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.

5 games seen working on the dsi · 70 roms set up for it · 112 achievements checked at once · 48 of a frame's 263 lines to do it in

  1. twilight menu++ starts a game with palmaris's build of the loader
  2. the loader sets palmaris down in memory the game does not use, with the game's achievements from the card
  3. while the game plays
  4. the in-game menu writes the unlocks to the card

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.

a fork that stopped at the dsi

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.

room beside a running game

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.

the first unlock

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.

a popup that does not pause

a dark strip with a gold trophy and the words a rush of rings, 1, in small white pixel letters
the popup, drawn on the pc by palmaris's own code: a trophy, the achievement's title and its points, 16 pixels tall and as wide as its words.

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.

  1. the game rewrote the back of its sprite table from a copy of its own, and the popup refused. 25 september
  2. too little sprite memory was free for a picture in full colour, so the popup borrowed one of the game's sixteen-colour palettes instead.
  3. the popup appeared, and the whole screen flickered: checking whether it could stay cost 58 lines.
  4. a hard stop at 16 lines refused it, because the palettes were all weighed in one turn. they are now weighed over several.
  5. shown as designed, and not seen. the game copies its table over the screen's after palmaris has written, every frame. 26 september
  6. a copy of its own at the start of the frame found no free channel to make it with: the game uses all four.
  7. no copy into the table showed where the game keeps its own.
  8. palmaris found the game's copy by what is in it, and wrote into the slots it hides. the popup appeared, steady, and the game's coin counter drew over it.
  9. placed where the game keeps no sprites, two popups appeared at the bottom left, clear of the counters. they were too big, and slow to come.
  10. one line, 16 pixels tall, with a smaller trophy: both shown in full.

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.

sonic rush on both screens of a ds, sonic running through a jungle level under a ring counter, with a dark one-line popup on the top screen from the third frame
the sprites of the game on the top screen, and the popup, in the ds's pixels from the top left
boxframeslefttoprightbottom
sprite1–12103310
sprite12313655152
sprite12315255160
sprite2108104140120
sprite2108120140128
sprite393112517
sprite3931712525
sprite3148128212192
sprite3221161253177
sprite3221177253185
sprite4314035156
sprite4315635164
sprite45498118162
sprite49988131120
sprite4111176127192
sprite41221918683
sprite41955222768
sprite41956822776
sprite503729101
sprite510274259
sprite52216338179
sprite53309722
sprite510601387
sprite5106713815
sprite92077022386
sprite9207102223118
sprite9207134223150
sprite1094100110116
sprite1094132110148
sprite1094164110180
popup383214548
popup440017716
popup5–940168177184
popup10–1240817724
a picture of the rule, not a recording of the dsi: sonic rush in an emulator on the pc, with the popup as palmaris draws it.2

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.

every frame, or close

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 of new super mario bros. checked a second on the dsi
frames a second
snapshots, every 16 frames3.7
27 september, run 214.7
27 september, run 317.3
27 september, run 49.9
29 september, other games' fixes8.3
29 september, a check skipped9.4
run 3 checked the most, and its slowest turn reached the whole 48 lines, so run 4 split the work further. the sets of other games showed two cases the schedule had missed, and fixing them cost some speed. skipping one check for achievements that had been checked four frames running won most of it back, with the unlocks still the same on the pc.

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.

more games than one

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.

where it stands

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.

commits a day, merges included
daycommits
2026-09-2262
2026-09-2330
2026-09-2419
2026-09-2537
2026-09-2652
2026-09-2741
2026-09-2855
2026-09-2950
2026-09-3029
2026-10-0110
2026-10-0274
2026-10-0318
2026-10-2115
twelve days in a row, then a pause, then a day to record a session and give the project its name. it was called dsi-retroachievements until 21 october.

notes

  1. the fork is 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.
  2. the frames were made with desmume, from a save state, and the sprites' boxes read from the emulator's copy of the top screen's sprite table in each frame, but for two that show only two pixels at its left edge, too narrow to draw a box round. the unlock at the hundredth ring is the picture's, as is the popup's place in each frame: on the dsi it also counts the sprites drawn in the frames before.
  3. the 50 are the core sets of my games that the pc could export and check, each run against the same memory both ways.