Submission Text Full Submission Page
This run is marked as Cancelled. So far it has failed verification on console, and it appears to only work through inaccurate Open Bus behavior. See: the discussion for more info.
Update: After some research, the reason Bizhawk behaved differently from the console has been confirmed, and with a slight modification to the TAS, the run has been verified on console with help from Alyosha. In the meantime, I will leave this run marked as "Cancelled". With some help from Mizumaririn and SeraphmIII, the SMB1 portion of the TAS has been optimized, so I plan to write author comments that explain the run better and submit again in the future. There still lies the issue of the cartridge swapping during the TAS, and the unofficial emulator fork, though that's an issue to figure out at a later date. Cheers!
With the introduction of ACEVideos, it should stand that executing arbitrary code is, above all else, the primary objective. Silly little roadblocks such as "Website rules" and "Unofficial releases of the Bizhawk emulator" shouldn't hold back innovation in the world of executing arbitrary code!
With my custom fork of the Bizhawk emulator that allows for swapping ROMs mid-TAS, I'm able to write a payload using subframe inputs in Super Mario Bros. 3, then hot swap to Super Mario Bros to execute it.

Objectives:

  1. Executing Arbitrary Code in Super Mario Bros.
  2. Complete the game in 8-4
It would seem logical that the ideal code to execute would set the game to 8-4. Though it begs the question, how can you run ACE in Super Mario Bros?
Unlike Super Mario Bros. 3, you can't press buttons at 4 kilohertz and expect anything cool to happen, you need to actually play the game. Though it may be a sacrifice, it seems like advancing past the title screen is a necessary step towards executing arbitrary code and completing the game.
This method of executing arbitrary code relies on a funny prank bowser pulls on one of his minions. You see, Super Mario Bros. has a series of castle levels in which one of Bowser's loyal subjects dress up in disguise as a feeble attempt for Bowser to steal all the credit. Mario is a plumber, so naturally shooting fireballs (which is the enemy of water) is a talent he possesses. By burning Bowser's disguised subject, their true form is revealed! Each world throughout the game has a new enemy hidden within a bowser costume, but if Mario does some warm up by playing a round of NES Tennis, Mario can achieve a head start and begin his adventure beyond world 8. In some of these worlds, it would seem Bowser's minions have pulled a fast one, by shoving a tear in the fabric of reality inside a Bowser-costume. In most cases, burning away the disguise will reveal nothing at all, a spring, a firebar, or even one of Bowser's loyal subjects. However, there are a few cases where Bowser's subjects reality-bomb the universe as a gesture of tomfoolery, and these are the worlds we want to investigate.
I’ll cut to the chase and say, world ‘N’. That’s the world where the magic happens.

Setting up the code

While it would be cool to use the gameplay of Super Mario Bros. to set up the code, there’s nothing to work with in terms of manipulable RAM. This brings up the notion of setting up the payload inside another game, then swapping to Super Mario Bros. without turning off the console’s power. There’s a small issue though… When Super Mario Bros. boots up, it clears almost everything in RAM. The bytes that remain are address $160 through $1FF, and $7D7 through $7FF. To make it even trickier, address $7D7 through $7FF will be wiped if the game determines the state as a “cold start”. This could be prevented by writing a value of “5A” to address $7FF, and making sure all the bytes corresponding to Marios score on the title screen ($7D7 through $7DC) are all a value of 9 or less. There’s a well known exploit where you boot up SMB, swap to Tennis and take a few steps, then swap back to SMB. This works because the byte tracking what world you should start the game on (if you hold down the A button when you start) is the same byte as the number of footsteps taken in Tennis, and none of the bytes Mario uses to determine if it’s a “cold start” are affected through Tennis’s gameplay. Since some of those bytes are used by Tennis, let’s write the payload in the $160 through $1FF region of RAM.
This is where playing Tennis brings along a terrible ancient curse. You see, Tennis clears the region of RAM at $160 through $1FF. This means, we’ll have to play Tennis before writing the payload.
I decided to use Super Mario Bros. 3 to write the payload, as that game seems to have the fastest method of Arbitrary Code Execution, clocking in at a little over one fifth of a second. Unfortunately, booting up SMB3 will clear the bytes used by SMB1 to determine if it’s a “cold start”. Oh well, instead of playing Tennis at all, we can just use SMB3 to fix all that since we already have total control.
By pressing buttons really fast, you can beat SMB3 really fast. Though, we’re trying to beat SMB1 really fast, so instead of jumping to SMB3’s credits, I write a bunch of code that lets me write code in the stack and fix the “cold start” issue as well as setting the world to start SMB1 in.
With that taken care of, we swap out SMB3 for SMB1.

Gameplay

A bit of a foreign concept for my TASes, but I gave it a shot. We begin the game in world ‘N’, which looks a lot like 8-3. This level is ideal, as it lets us obtain a fire flower, which we will need when we encounter Bowser’s secret weapon. Using cutting edge tricks, like ignoring the part where 8-4 loops and you need to enter pipes, we can make it to the end of the stage in record time! We burn away the disguise to reveal… a bus?

Imagine A Bus

An open bus! This merry prankster transforms into an object that stores the number 4 at address $772. This address is used to determine if we’re initializing a level or running the regular gameplay loop. With a value of 4, it tells the PC to jump to address $53AE, which isn’t mapped to anything. This leads to a fun phenomenon known as “Open Bus” where whatever the CPU did most recently will dictate what it’s about to do. Let’s walk through what just happened:
We encountered a JMP Indirect instruction, which tells the PC to jump to wherever some pointer is pointing. This is a 5 step process, and I’m somehow simplifying the final steps.
  1. Fetch the opcode. (increment PC)
  2. Fetch the low byte of the pointer (Increment PC)
  3. Fetch the high byte of the pointer (Increment PC)
  4. Fetch the new low address for the PC
  5. Fetch the new high address of the PC
According to this, the most recent step was to fetch the high byte of the new address, which in this case was “53”. Since we are now executing open bus, when the CPU attempts to fetch an opcode, there’s nothing to fetch! Since “53” is currently on the bus, “53” is interpreted as the opcode, as well as all the operands! “53” corresponds to an undocumented opcode, commonly referred to as SRE Indirect, Y, which of course, stands for “Logical Shift Right then Bitwise Exclusive-OR with Accumulator (Indirect, Y)” It would also help to know what “Indirect, Y” means, but this explanation is getting a little off the rails.
The end result is, address $0A is on the receiving end of this instruction, and we need to perform a bitwise XOR with that address and the A Register. The PC Fetches this byte, and now whatever value it holds will be the next instruction to execute. Luckily for us, this byte is easy to manipulate. By simply holding down the B Button and nothing else, we fetch a value of “40” which becomes an RTI instruction. We’re departing the open bus, and setting foot into the stack. Address $181 is our destination, and the local time is whatever you want it to be, because SMB never clears this portion of the stack when the game boots up, and we can use SMB3 to write anything we want.

Creative Exercise

Like a blank canvas in the hit game “Color-A-Dinosaur”, we could fill this space with anything we want! Remember, the objective is to win the game in 8-4, so let’s start by setting the game to world 8. World 8 is actually world 7, since world 1 is actually world 0, so the code I write is:
LDA #07 STA $075F
Now when we encounter the princess, she will give the proper “Your quest is over” speech. Our magical open bus ride did a number to the stack pointer, so let’s correct that real quick.
LDA #$57 PHA
We need to make sure the proper gameplay loop happens next frame, so we’ll decrement $772 from a value of 4 (what lead us to the open bus) to a value of 3 (regular gameplay loop)
DEC $0772
Now, we may be in world 8, but the HUD still says “N-2”. I decided to correct this by writing
LDA #03 STA $075C JSR $865A
Which sets the level to 3, which is actually level 4, and jumps to a subroutine that updates the HUD. Finally, we need to jump to stable gameplay, so I write:
JMP $8178
Which safely leads to the end of the frame.
When this executes, we leave N-2 and enter 8-4 while already falling to grab the axe with a final time of 1:17.06 (at the time of the final input)

Verifying This TAS

As mentioned, this is not an official release of Bizhawk. I had to modify the .bk2 file format in order to make this run work, by adding a file path to hot swap with whenever the reset button is pressed. To obtain my fork of Bizhawk, you’ll need to download the source code and compile it yourself: https://github.com/100thCoin/BizHawk-HotSwap
In order to make the hot swap file paths consistent between computers, the file path is relative to folder containing the .exe
Your ROM of Super Mario Bros.3 can be anywhere, but the ROM of Super Mario Bros must be placed in the bizhawk “output” folder that is generated when you compile the code.
The rom must be named “Super Mario Bros.nes”, and the version I used had a SHA1 of “EA343F4E445A9050D4B4FBAC2C77D0693B1D0922”
I created this TAS with the PRG1 release of SMB3, though it also runs on the PRG0 release just fine.
The TAS begins inside Super Mario Bros. 3 using the SubNesHawk Core

Verifying This TAS Without Doing That

If compiling a fork of the Bizhawk emulator is not something you care to do, then you can imagine this TAS as a movie starting with some preset RAM. Just know that the RAM is supposed to be obtained through playing SMB3, and some super rad cutting edge stop n’ swop technology
A user file of just the SMB1 portion of this TAS can be found here: The User File.
That User File runs on Bizhawk 2.8, though starts with the Preset RAM that SMB3 sets up.
You should start that TAS in SMB1 instead of SMB3.

Where do we go from here?

Well, It may be bold of me to say this, but my ACE was so powerful, we might need to close ACEVideos for good and return to TASVideos. I plan to keep working on my hot-swapping fork of Bizhawk, and eventually get a pull request going, but for now it’s not an official feature. I was planning on getting total control in SMB1, which honestly wouldn’t be all that difficult since I already have total control in SMB3, but I ran out of time. Perhaps at a later date I could showcase total control in SMB1. Maybe if ACEVideos returns at some point in the future. Until then!

Suggested Screenshot:



TASVideoAgent
They/Them
Moderator
Joined: 8/3/2004
Posts: 15577
Location: 127.0.0.1
Samsara
She/They
Senior Judge, Site Admin, Expert player (2238)
Joined: 11/13/2006
Posts: 2822
Location: Northern California
Finally, a real submission. I feel like people have been taking the rebrand to ACEVideos really poorly for the most part and have been submitting protest runs, but I'm glad to see at least one person is taking us seriously. Thank you.
TASvideos Admin and acting Senior Judge 💙 Currently unable to dedicate a lot of time to the site, taking care of family. Now infrequently posting on Bluesky
warmCabin wrote:
You shouldn't need a degree in computer science to get into this hobby.
MrTASer
He/Him
Player (231)
Joined: 7/3/2021
Posts: 147
Location: Udaipur, Rajasthan, India, God'sBeautifulWorld
The last level feels nothing without the pupes😮
My TASing channel ; My MUSIC channel ; My SoundCloud ; My HomePage ; Music Composer ; Mr.TASer#5922 VCop 2 TAS completed, but in a completely different way ; And SMB - GAGOTO (WL) TAS COMPLETED TILL W-4 ; Some more...
Patashu
He/Him
Joined: 10/2/2005
Posts: 4043
Funny and clever idea!
My Chiptune music, made in Famitracker: http://soundcloud.com/patashu My twitch. I stream mostly shmups & rhythm games http://twitch.tv/patashu My youtube, again shmups and rhythm games and misc stuff: http://youtube.com/user/patashu
Moderator, Senior Ambassador, Experienced player (907)
Joined: 9/14/2008
Posts: 1014
Let's figure out how to console verify this
I was laid off in May 2023 and became too ill to work this year and could use support via Patreon or onetime donations as work on TASBot Re: and TASBot HD is stalled. I'm dwangoAC, TASVideos Senior Ambassador and BDFL of the TASBot community; when healthy, I post TAS content on YouTube.com/dwangoAC based on livestreams from Twitch.tv/dwangoAC.
Player (246)
Joined: 6/16/2020
Posts: 28
dwangoAC wrote:
Let's figure out how to console verify this
It shouldn't be too difficult. The SMB3 TAS intentionally ends with a HLT instruction, so you could take the time for a human to swap out the cartridges. I can separate the SMB3 TAS into it's own file and send over both halves. I'm currently working with some folks in the Retro Mario Speedrunning Discord to optimize it (My SMB1 gameplay was fairly suboptimal) I'll go ahead and upload both halves of the TAS as user files: The SMB3 TAS that runs first: https://tasvideos.org/UserFiles/Info/638160503431898737 The SMB1 TAS that follows: https://tasvideos.org/UserFiles/Info/638160458975160308 A more optimized TAS is in the works, but these files are from the TAS that was submitted.
Alyosha
He/Him
Editor, Emulator Coder, Expert player (3821)
Joined: 11/30/2014
Posts: 2829
Location: US
I always like seeing new innovations in TAS technology, and this one is really cool, nice work! I tried console verifying, but it crashes in various ways at the axe grab. Not sure what this means. here is an example: Link to video I hold reset in between cart changes. Gives pretty consistent results up until the axe grab. EDIT: Upon reading the submission text, it seems it's probably crashing at the unofficial opcode, or maybe the payload is not getting written properly.
Mizumaririn
Other
Player (243)
Joined: 2/26/2020
Posts: 44
Location: Super Bell Hill
Alyosha wrote:
I tried console verifying, but it crashes in various ways at the axe grab. Not sure what this means. EDIT: Upon reading the submission text, it seems it's probably crashing at the unofficial opcode, or maybe the payload is not getting written properly.
According to 100th Coin in the Speedrunning Discord, the purpose of this setup is to fire kill Bowser to initiate ACE in SMB1 instead of beating the game.
100th Coin wrote:
The objective is not to grab the axe as fast as possible, the objective is to run arbitrary code and complete the game in 8-4. (the code changes the world and level from N-2 to 8-4) the arbitrary code requires killing Bowser with a fire flower, and world 'N' is the only world where killing bowser can be used to jump the PC to RAM that could be manipulated.
I admit these two lines is easier to understand than the submission text. So yes it is specifically done to crash the game since payload isn't written yet. EDIT: I just checked 100th Coin's temp encode and it didn't crash? Maybe the payload wasn't completely written but I have no clue. At least from the above two lines that beating the game isn't the main objective.
pronouns: Mizu/Mizu
Alyosha
He/Him
Editor, Emulator Coder, Expert player (3821)
Joined: 11/30/2014
Posts: 2829
Location: US
I tried a few more times, and it seems like the SMB3 part of the run isn't being completed properly on console. It looks like some inputs are not being processed. This is occasionally a problem for games that require processing input very rapidly. Maybe someone with a different bot or a V4 TAStm32 could make it work. I'll try some more when an updated movie is made.
Player (246)
Joined: 6/16/2020
Posts: 28
Alyosha wrote:
I tried a few more times, and it seems like the SMB3 part of the run isn't being completed properly on console. It looks like some inputs are not being processed. This is occasionally a problem for games that require processing input very rapidly.
Interesting. I probably should've went into more detail what the SMB3 portion is doing in my author notes, but the April first deadline was swiftly approaching, and I put too much effort into writing bad jokes. In SMB3, the final instructions to execute are what sets up that variables to start SMB1 in world 'N', so it would seem everything is being executed, though it's certainly possible something is going wrong during the setup. I write a loop that's essentially
    LDY #PayloadSize
Loop:
    ; -- wait for next frame, snip --
    LDA Controller 1
    ORA Controller 2
    STA (pointer) , Y
    DEY
    BNE Loop
    ; -- Set up SMB1 bytes for starting in world 'N', snip --
And by running that, I write the payload (in reverse order) at the pointer. (in this case, $0180). It's possible the payload is being written incorrectly. I don't know if this would affect anything, but while making the TAS I placed inputs in controller 2 during a read of controller 1, just for visual clarity of "Here's where stage 2 of my code is happening". (Line 1393 in TAStudio) Perhaps I could've used a marker. If that would throw off the bytes controller 2 is pressing during the replay device playback, then it would explain why you still start in world 'N' but the payload doesn't work. Then again, it could be something else, like the unofficial opcode. More specifically, how the result of that opcode affects open bus. It's worth noting, if instead of the payload running, a BRK instruction were to execute (if the entire stack was cleared when the game booted? I don't believe any version of the game does that. Open Bus could have behaved different from the emulator?) the audio that plays is identical to Alyosha's recording, though unlike the recording the background moves away from the bridge:
Alyosha
He/Him
Editor, Emulator Coder, Expert player (3821)
Joined: 11/30/2014
Posts: 2829
Location: US
OnehundredthCoin wrote:
In SMB3, the final instructions to execute are what sets up that variables to start SMB1 in world 'N', so it would seem everything is being executed, though it's certainly possible something is going wrong during the setup.
Interesting, this would then point to something on the SMB1 side (even though it's still strange the bot doesn't exit the SMB3 part correctly.) Recent work on open bus behaviour shows some inconsistency, and I haven't personally tested unofficial opcodes on my NES, so there are several variables at play. Some kind of test to verify the baseline behaviour would probably help sort things out.
Player (246)
Joined: 6/16/2020
Posts: 28
As a brief update, the cause of the crash is now known, but not for the reason I initially thought. To figure out if the crash was due to a BRK instruction, I created a ROM hack with a simple crash handler (when a BRK instruction executes, instead of an infinite loop, I made some code that shows on screen the address of the BRK instruction that was executed, as well as a frame counter to verify the frame the game crashes on is on the same frame as expected.) and I sent the TAS with the ROM hack over to BigBass to verify: It would appear the cause was indeed a BRK instruction due to unexpected open bus behavior. Remember, the indirect jump is to address $53AE, which means the SRE instruction never executed, and instead a BRK was. This seemed rather fishy, as my knowledge of open bus behavior contradicts that outcome. I discussed it with some members of the NesDev discord server, and apparently everdrives have wildly inaccurate Open Bus behavior. My current theory is that the cause of the crash wasn't actually a BRK when Alyosha tested it, and the crash handler ROM I put together ended up as a red herring. After some more conversation, it would appear the real culprit was the SRE instruction after all! Bizhawk leaves a value of 40 on the bus, though the SRE instruction should leave the bit shifted result on the bus instead, which in this case was 20. that creates JSR $2020, which leads to, you guessed it- open bus. An infinite loop of JSR $2020 occurs, thus the game crashes. After I came to the conclusion of "The mystery BRK is the cause of the crash!" I decided to mark the submission as "Cancelled", though perhaps that was done a little too hastily. If anybody wants to verify the run, use this movie for the SMB1 portion, and it must be done without an everdrive: https://tasvideos.org/UserFiles/Info/638169302314508374 The SMB3 portion is still the movie that can be found here: https://tasvideos.org/UserFiles/Info/638160503431898737
TASVideosGrue
They/Them
Joined: 10/1/2008
Posts: 2785
Location: The dark corners of the TASVideos server
om, nom, nom... blech, bitter!
Player (246)
Joined: 6/16/2020
Posts: 28
The issue with the game crash has been solved, and confirmed to be a malfunction of Bizhawk's open bus behavior for the SRE instruction. In the submitted TAS, open bus executes as follows: We enter open bus through an indirect jump, leading to address $53AE. The Y register holds a value of $0A. SRE ($53), Y. This is an indirect instruction, so the first step is to find the address we're going to execute the instruction on. The operand was $53, so let's take a look at address $0053 and $0054. both of these addresses have a value of $00, which forms a pointer to address $0000. This particular instruction also adds an offset of the Y register, which has a value of $0A, so we're going to perform the SRE on address $0A. In the submitted TAS, Address $0A holds a value of $40. The SRE instruction takes this value and leaves it on the bus. Therefore the next instruction to execute will use the value of $40 as the opcode. 40 is an RTI instruction, and that takes us to the payload. However, that is incorrect behavior, as demonstrated by Alyosha's console verification. The correct behavior would take the value of $40, bit-shift it to the right, and leave the result on the open bus. To correct this, I made sure address $0A had a value of $80 for the next console verification. When performed on console, $80 is bit shifted to become $40, and so the following instruction is RTI. That leads to the payload that was written, and the run is verified! Thanks for the help, everyone! Especially Alyosha and BigBass for help with console verification, and Mizumaririn and SeraphmIII for help optimizing the SMB1 part of the run. Link to video