DULLES, Va.—Regular watchers of the annual Awesome Games Done Quick(AGDQ) video game speedrun marathon are probably intimately familiar with the power ofTASBot (short for tool-assisted speedrun robot). Two years ago, the emulator-fueled bot used its controller-port interface to write a simple version of Pong and Snake on top of a running Super Mario World cartridge. Last year, TASBot outdid itself by using a copy ofPokemon Red and a Super Game Boy to force a live, IRC-based Twitch chat through an unmodified Super Game Boy.
By now, simply taking over a game and replacing it with a brand new app was beginning to feel a little predictable. So this year, TASBot decided to show off a new skill. At the AGDQ marathon, the bot set out to edit new features onto a game that’s still running in active memory. TASBot wanted to be magnanimous with its new capabilities, too, allowing human players (and livestream viewers) the opportunity to edit the game on the fly.
But just how did TASBot—and the team of coders behind it—intend to turn an old game of Super Mario World, running on a standard SNES, into a heavily editable game of Super Mario Maker? Luckily, we had a behind-the-scenes invite to the event and the opportunity to find out.
The archived Twitch video of TASBot’s SNES “Super Mario Maker” exploit looks like magic; or at the very least the kind of thing that requires a hacked ROM or memory manipulation through a Game Genie-style device. But it’s important to remember that like in the past, everything TASBot does is technically possible on standard classic game hardware and software. That is, it’s possible provided you have the superhuman ability to enter precisely timed controller inputs 60 times a second. The first step to hacking up a level editor on top of Super Mario World is old hat for TASBot by now. The robot takes “total control” of the system through arcane in-game glitches. Next, it juggles items with pixel-perfect positioning and split-second timing to effectively write and execute a bit of assembly code through the system’s active sprite management memory.
This year’s takeover uses a slightly different total control method than those in years past. The new route uses precise pausing and unpausing after getting hit by a mole enemy to get some in-game timers lined up just right to represent that crucial memory code. The end result is the same: TASBot tells the game to start reading controller inputs as raw, binary programming code at a rate of about 3.8KB/sec.
From there, it’s relatively easy for TASBot to use precisely timed button presses to essentially write a new program that lets it take “total control” of the SNES. The details involve first writing a block loader to a small, “safe” area of memory, then running that block loader to continually sample the controller inputs for new data (the process is described in much more detail in this write-up of the 2015 Pokemon Red/Super Game Boy exploit).
The total control process was made simpler for the TASBot team this time around by a handy Lua script that can recode arbitrary PC files into the appropriate controller inputs. “For ‘Pokemon Plays Twitch,’ when we wanted to test a new version of the payload we were reliant on Ilari to create a new movie file,” TASBot organizer Allan Cecil (who goes by DwangoAC online) told Ars. “For this version, Ilari created a handy ‘script kiddie’ Lua script that allows us to specify a binary file to write to a particular location in memory based on its filename.”
Hiding in the SRAM
In previous demonstrations after TASBot gained total control of a system, it threw out the existing game and coded whatever software it wanted in its place—Pong,Super Mario Bros., Twitch chat, etc. This year, the level-editing script the team was building would be inserted on top of the still-active Super Mario World code already in memory. That way, instead of having to recode an entire Mario engine, the code could simply call existing functions for everything from block placement to screen scrolling to Mario physics. As Cecil put it, “Hey, why write something new when you don’t have to?”
As you can imagine, keeping the real Mario code in active memory while writing a level editor on top requires a much lighter touch, and much more delicate memory management, than in the past. “There’s not much main [SNES] memory that’s left over once Super Mario World gets done hogging it,” Cecil said.
As it stands, TASBot can only write over a few small, “safe” portions of the system’s 128KB of main RAM without breaking the copy of Super Mario World already loaded there. What’s more, that safe RAM is laid out in small, scattered pockets that are hard to access contiguously (and the much larger ROM chips on the cartridge itself can’t be overwritten at all).
The TASBot team briefly considered trying to write and execute the level editor on the RAM dedicated to the SNES sound chip, but Cecil said that idea was thrown out pretty quickly. The sound system was designed to run totally independently from the graphics and logic systems powering the rest of the SNES, so trying to hardcode and run executable logic on there “goes against everything the SNES was supposed to do” (even more so than everything else TASBot does, apparently). “Accessing [the audio RAM] is just so actively risky and dangerous. You’re just asking for trouble,” he said.
The safest place to store TASBot’s inserted code, it turns out, was on the game cartridge itself. Specifically, the small bit of rewritable SRAM that many SNES games include for storing battery-backed save games. Overwriting that would ruin any saved progress on the cart itself, but it wouldn’t alter the running Super Mario World code at all.
While the original Super Mario World cart has a mere 2KB of SRAM to work with, Nintendo later released an expanded cartridge that put the Super Mario All-Stars compilation and Super Mario World in a single package. The expanded Super Mario All-Stars + Super Mario World (SMAS+W) cartridge has a whopping 8KB of SRAM to accommodate save files for all of those games. TASBot would end up needing almost all of that expanded space to write its level editor.
Fooling the game
Even with a convenient place for TASBot to write new code, it was still difficult getting a level editor running on top of a game that was decidedly not designed to have a level editor running on top of it. “Painful” is the one word description of the takeover process used by coder p4plus2, a senior at Cal State East Bay who did most of the work on the actual level editor payload.
“It’s been one of those interesting experiences,” he told Ars. “You never know what you’re going to be able to get into the final product… It was definitely a lot of last-minute panic, a lot of sleepless nights…”
The first step for inserting a level editor into Super Mario World is convincing the game that the new code belongs there in the first place. “The thing that has to happen is you’re kind of running a game loop inside of a game loop,” p4plus2 said. “You’re running your editor as an outer shell, and you take over the internal game and sort of restart the game inside of itself to jump into a known state—you have to reinitialize things to something you know you can control.
A lot of p4plus2’s further coding efforts focused on “stopping the game from acting like it wants to act” as the player is editing a level, he said. For instance, Mario touching an enemy while the level was being edited would lead to instant death and the end of the level. “We needed to come up with loopholes like hiding him off screen, so he doesn’t get hurt,” p4plus2 said.
The level editor code is full of similar routines that make sure the game doesn’t break itself while being edited. The game’s entire sprite-handling system had to be reverse engineered to allow the editor to let the cursor move in-game spriteswithout them being actively processed by the game engine. The usual pause function also had to be overridden so the start button could restart the level, instead. Mario’s standard return to the world map after death had to be completely circumvented as well.
There were also ever-present worries about introducing too much lag into the game. If the level editor code takes too many processor cycles between display frames, the carefully timed inputs from TASBot could end up becoming desynchronized from what was planned, breaking the entire process. P4plus2 estimates his code leaves only a few thousands extra CPU cycles of overhead to work with each frame, under normal conditions.
If a human level maker places too many enemy sprites on screen at once, those extra cycles can easily spill over into game-breaking lag. As it is, the team spent many of the final hours before the live demo scrambling to reposition particularly processor-intensive enemies, and recoding inputs so blocks didn’t end up desynchronized out of place. The final version of the level was sent to TASBot literally minutes before the team had to walk down to the stage.