Welcome to Super BaconT World!
Game Objectives 🟢
- Emulator used: BizHawk 2.6.3
- Allow Left+Right / Up+Down
- Core BSNES
- Abuses programming errors
- Crashes the game intentionally
- Uses arbitrary code execution
- Takes total control of the game
About the TAS 🟢
I do not even know where to begin, but here we go. First, I used BizHawk 2.6.3 to make this TAS because my ACE tools, spreadsheets, and Lua scripts are configured for it. However, I will work on creating tools compatible with BizHawk 2.11.1. I actually could have made this TAS in BizHawk 2.11.1, but by the time I considered it, it was already too late: the TAS was far too advanced for me to convert the inputs. For my next project, BizHawk 2.6.3 will already have been retired—it has served its purpose.
This is one of the most insane TASes I have ever made, second only to the
glitchfest, which, in my opinion, is still the best one, although this TAS is not far behind; it is almost on the same level, even though the
glitchfest took seven months and this one took "only" three.
Like its
predecessor, this TAS uses arbitrary code execution to write code into the game's memory and gain access to resources that an ordinary TAS could never use. Although the basic ideas are similar, this TAS is extremely different from its
predecessor. In the previous TAS, I only wrote a routine into one of the game's subroutines that gave me access to every item in the item box, every power-up, and the ability to increment a few sprite-slot IDs, and I carried that code all the way to the end of the game. In this TAS, I went much further and took complete control of the game: its music, graphics, palettes, text, cutscenes, and practically anything else you can imagine. This TAS literally created a ROM hack live while the game was running.
Another interesting aspect of this TAS is that things will appear to get progressively more extreme as time goes on. That is because I did not begin the project already knowing everything; I learned while making it, haha. My original idea was exactly the same as in the previous ACE TAS: write one routine into memory and use it until the end of the game. The only difference was that the routine would be much larger and more complete this time, with several functions. However, as I continued making the TAS, I discovered new things, learned more and more about ACE, and, with help from ChatGPT, managed to do extraordinary things that I did not even know were possible. To give you an idea, I started this TAS without understanding some basic ACE concepts. Did you know that if address $7E0000 contains "A9 18 8D 19 00 6B" (`LDA #$18 / STA $0019 / RTL`) and I execute `JML $7E0000`, the game interprets those bytes as code and writes #$18 to address $0019? Well, I did not know that when I started the TAS, haha, even though it now feels extremely simple to me. You are going to see an ACE lesson in this TAS—and I was the student!
Technical Explanations 🟢
All right, let us start from the beginning. I will explain some simple things here that were not explained in the
previous TAS, simply because I did not know them 😂
Super Mario World has a loop at
$7F8000 that runs every frame and writes to OAM to move all sprites off-screen. This loop is 387 bytes long and ends at $7F8182 with an `RTL` (`6B`). So far, everything is normal; this is part of the game. However, coincidentally, hehe, immediately after that loop, starting at $7F8183, there is an
unused area containing more than 500 bytes! That is wonderful—it is exactly what we need 😼
By intercepting this subroutine and removing the `RTL` at $7F8182, we can write any code we want immediately after it, as long as it does not exceed 504 bytes, the size of the unused area. The game will then execute that code every frame, believing it to be part of the original routine. This is exactly what I did in the previous TAS, but I could not explain it because I genuinely did not understand it at the time.
Okay, but how do we intercept this code and write custom instructions there? This is where the initial ACE setup comes in: we must find a way to make the game jump into the controller registers and interpret controller inputs as machine code. The setup I used is the same one created by Masterjun and explained
here. Let us get to the code!
Code, Code, and More Code 🟢
I will explain the initial routine, which I originally intended to use until the end of the TAS, in separate parts because it is extremely long. Here we go!
The first part of the code changes Mario's name, the color of Mario's name, and the colors of palettes A, B, C, and D:
REP #$20
LDA #$0A0B
STA $0EF9
LDA #$180C
STA $0EFB
LDA #$1D17
STA $0EFD
LDA #$581D
STA $0719
LDA #$2C00
STA $0849
LDA #$3800
STA $084B
LDA #$7400
STA $084D
LDA #$1FF1
STA $0869
LDA #$03F9
STA $086B
LDA #$FF03
STA $086D
LDA #$4E08
STA $0889
LDA #$6770
STA $088B
LDA #$7FFF
STA $088D
LDA #$347D
STA $08A9
LDA #$551E
STA $08AB
LDA #$65FF
STA $08AD
There is nothing particularly complex in this first part. The code is mostly self-explanatory and runs every frame. It forces the game to write these values to the palette buffer beginning at
$0703 and to the addresses responsible for Mario's name in the status bar, beginning at
$0EF9. This is the code that turned Mario into BaconT, created the pink Yoshi, and changed the default sprite colors.
The second part of the code performs several unrelated tasks:
LDA #$184C
STA $1F29
LDA #$0A1D
STA $0F0A
SEP #$20
LDA #$1C
STA $0F0C
LDA #$00
STA $0DA0
LDA #$42
STA $1F2B
As you can see, this part of the code does several different things. First, it writes "4C 18 42" to $1F29/$1F2A/$1F2B; I will explain that later. Second, it changes the "TIME" label in the status bar into "TAS". Finally, it forces $0DA0 to remain zero (`STZ $0DA0` would have been enough—my mistake 🤦♂️).
$0DA0 is a flag that determines how many controllers are connected. Because I used four controllers in this TAS to write code, the game would force this address to #$82, which causes Luigi to be controlled by controller 2. I did not want that, because controller 2's buttons execute functions, as you will see below. By keeping this address at zero, I can control both Mario and Luigi exclusively with controller 1 while freely executing my functions with controller 2. This was a problem in the
previous TAS.
The third part of the code assigns functions to controller 2 buttons, as well as a few controller 1 buttons:
LDA $0DAB
CMP #$0C
BEQ gamemode
CMP #$06
BEQ music
CMP #$08
BEQ slot4
CMP #$04
BEQ slot5
CMP #$02
BEQ waterlevel
CMP #$01
BEQ itembox
CMP #$20
BEQ deathcancel
CMP #$10
BEQ superspeed
CMP #$40
BEQ slot7inc
CMP #$80
BEQ infjumps
LDA $0DAD
BIT #$40
BNE powerup
BIT #$80
BNE animation
BIT #$20
BNE slot6tweaker
BIT #$10
BNE starpower
LDA $17
CMP #$30
BEQ messages
CMP #$10
BEQ generator
CMP #$20
BEQ spinjump
LDA $19
CMP #$05
BEQ powerupcmp
RTL
The final part contains the actions executed by each button:
gamemode:
LDA $A5
STA $0100
RTL
waterlevel:
INC $85
LDA $85
AND #$03
STA $85
RTL
slot4:
LDA #$0B
STA $14CC
RTL
slot5:
LDA #$08
STA $14CD
RTL
music:
INC $58
LDA $58
CMP #$1E
BCC music_spc
STZ $58
music_spc:
LDA $58
STA $1DFB
RTL
itembox:
INC $0DC2
RTL
deathcancel:
LDA #$06
STA $18AE
STZ $71
RTL
superspeed:
LDA #$7F
STA $7B
RTL
slot7inc:
INC $A5
RTL
infjumps:
LDA #$01
STA $1471
RTL
powerup:
INC $19
RTL
animation:
LDA $0DC2
STA $71
RTL
slot6tweaker:
STZ $1680
STZ $1493
RTL
starpower:
LDA #$03
STA $1490
RTL
messages:
LDA $0DC2
STA $12
RTL
generator:
INC $18B9
LDA $18B9
CMP #$0F
BNE gen_limit
STZ $18B9
gen_limit:
RTL
spinjump:
INC $140D
LDA $140D
AND #$03
STA $140D
RTL
powerupcmp:
STZ $19
RTL
Explaining what each button does:
- Controller 2 Buttons
- Up - Sets #$0B at $14CC ("teleports" any sprite in slot 4 to Mario's hand).
- Right - Increments $0DC2 (access to every item in the item box).
- Left - Increments $85 up to 03, then returns to 00 (any nonzero value in $85 enables a water level).
- Down - Sets #$08 at $14CD (revives or gives infinite life to any sprite in slot 5).
- Down + Up - Loads the value from $A5 and writes it to $0100 (access to any game mode).
- Down + Left - Increments $58, an unused address, up to #$1E and writes it to $1DFB (access to any song in the game).
- A Button - Loads the value from $0DC2 and writes it to $71. $71 controls several Mario animations and states, giving me access to all of them.
- B Button - Sets #$01 at $1471. This flag determines whether Mario is standing on a solid sprite, effectively allowing infinite jumps.
- X Button - Increments $19 (access to every power-up).
- Y Button - Increments $A5, which is the ID of sprite slot 7.
- L Button - Clears $1680, a tweaker byte specific to slot 6. Clearing it produces effects associated with a null sprite: sprites that normally cannot hurt Mario or cannot be killed may become able to do so. This button also clears $1493, the level-end timer. After completing a level, clearing this address cancels the level ending and lets gameplay continue normally inside the level.
- R Button - Sets #$03 at $1490. Since $1490 is the star-power timer, I could activate star power at any time and maintain it indefinitely by holding the button.
- Start - Sets $7F at $7B, giving Mario maximum rightward speed at any time.
- Select - Sets #$00 at $71 and #$06 at $18AE. When $71 is 00, Mario is not performing any special animation. When he is dying, for example, the game writes #$09 to $71, the death animation. By forcing $71 to remain zero, Mario never dies. This can also cancel cutscenes or the animation used when Mario or Yoshi collects wings. I mainly used it to cancel death and make Mario invincible. $18AE controls Yoshi's tongue; setting it to #$06 makes Yoshi extend his tongue even when Mario is not riding him.
- Controller 1 Buttons
- L Button - Increments $140D up to #$03, then resets it. Any nonzero value in $140D enables spin jumping, allowing me to alternate between normal jumps and spin jumps at any time.
- R Button - Increments $18B9 up to #$0F. This address controls generators inside levels, giving me access to all generators, although I barely used this feature.
- R Button + L Button - Loads the value from $0DC2 into $12. This address is a stripe-image loader. It is used for several purposes, but I mainly used it to display random messages during castle cutscenes.
Complete code in HEX (this code was written from $7F8183 through $7F82D1):
C2 20 A9 0B 0A 8D F9 0E
A9 0C 18 8D FB 0E A9 17
1D 8D FD 0E A9 1D 58 8D
19 07 A9 00 2C 8D 49 08
A9 00 38 8D 4B 08 A9 00
74 8D 4D 08 A9 F1 1F 8D
69 08 A9 F9 03 8D 6B 08
A9 03 FF 8D 6D 08 A9 08
4E 8D 89 08 A9 70 67 8D
8B 08 A9 FF 7F 8D 8D 08
A9 7D 34 8D A9 08 A9 1E
55 8D AB 08 A9 FF 65 8D
AD 08 A9 4C 18 8D 29 1F
A9 1D 0A 8D 0A 0F E2 20
A9 1C 8D 0C 0F A9 00 8D
A0 0D A9 42 8D 2B 1F AD
AB 0D C9 0C F0 4C C9 06
F0 63 C9 08 F0 53 C9 04
F0 55 C9 02 F0 42 C9 01
F0 63 C9 20 F0 63 C9 10
F0 67 C9 40 F0 68 C9 80
F0 67 AD AD 0D 89 40 D0
66 89 80 D0 65 89 20 D0
67 89 10 D0 6A A5 17 C9
30 F0 6A C9 10 F0 6C C9
20 F0 76 A5 19 C9 05 F0
7C 6B A5 A5 8D 00 01 6B
E6 85 A5 85 29 03 85 85
6B A9 0B 8D CC 14 6B A9
08 8D CD 14 6B E6 58 A5
58 C9 1E 90 02 64 58 A5
58 8D FB 1D 6B EE C2 0D
6B A9 06 8D AE 18 64 71
6B A9 7F 85 7B 6B E6 A5
6B A9 01 8D 71 14 6B E6
19 6B AD C2 0D 85 71 6B
9C 80 16 9C 93 14 6B A9
03 8D 90 14 6B AD C2 0D
85 12 6B EE B9 18 AD B9
18 C9 0F D0 03 9C B9 18
6B EE 0D 14 AD 0D 14 29
03 8D 0D 14 6B 64 19 6B
More Technical Explanations 🟢
This main routine remained almost entirely intact, with only a few changes, until the game's first crash in Forest of Illusion 1, a little over one hour into the TAS. After that, I wrote another routine that was very similar, but with several differences. I won't go into detail about the differences, as this code has been modified hundreds of times (I modified a few bytes repeatedly to control different addresses).
Remember that the main routine writes "4C 18 42" to $1F29/$1F2A/$1F2B? Let us break that down. The byte sequence "4C 18 42" means `JMP $4218`.
$4218 contains the controller registers—the exact address that allows us to write machine code through controller inputs. But what do I gain by writing this at $1F29? Here is how it works.
Sprite D0 is a dolphin generator. If this sprite is spawned incorrectly, either through the stun glitch or through the item box, it jumps directly to $1F29. At this point, you can probably see the idea. By writing "4C 18 42" at $1F29 and spawning sprite D0 through the item box, the game jumps directly to my instruction and then into the controller registers, allowing me to write anything I want through controller inputs. But why write code through inputs when I already have an enormous routine at $7F8183 running every frame? Simply because I wanted complete control of the game. Even though the main routine was huge and had many functions, it could not cover everything.
However, this method of writing code by jumping directly to $4218 was not very efficient, because I had to freeze the game in a loop (`STZ $10 / WAI / BRA $F8`) while entering the code. You may notice—or perhaps not—that the game froze for a few frames several times; that was me writing code. Although this method was inefficient, I used it for a long time because I did not know any alternative.
I continued using sprite D0 to write code until approximately 1 hour and 27 minutes into the TAS, when everything changed completely. Thanks to TheBiob and the explanation in
this TAS, I found a method for writing code with controllers 3 and 4 while controlling Mario and Luigi with controller 1 and executing my routines normally with controller 2.
Fake Loop
Super Mario World's main loop is located at
$806B and is essentially this:
MainLoop:
LDA $10
BEQ MainLoop
CLI
INC $13
JSR $9322
STZ $10
BRA MainLoop
The game waits until the NMI sets $10, enables IRQs, increments the global frame counter at $13, calls the game-mode dispatcher at $009322, clears $10, and waits for the following NMI. Based on this, I created a "fake" loop, which allowed me to do the things you will see near the end of the TAS. This is the fake loop I created:
FakeLoop:
PHK
PEA.w ReturnFromGame-1
PEA.w $84CE
JML $009322
ReturnFromGame:
JSL $7FA000
JSL $7FA180
STZ $10
WaitForNMI:
LDA $10
BEQ WaitForNMI
INC $13
CLI
BRA FakeLoop
HEX:
4B F4 8A 9C F4 CE 84 5C
22 93 00 22 00 A0 7F 22
80 A1 7F 64 10 A5 10 F0
FC E6 13 58 80 E2
This fake loop was written at $7F9C80. After writing it, I executed `JML $7F9C80`, and from that jump onward (especifically
here), the game was officially running inside a loop that I had created myself. The fake loop manually constructs a "fake" return address (`PEA.w $84CE`) so the game can execute its normal functions (`JML $009322`) but always return to $7F9C8B inside my own loop.
Notice that two instructions stand out in this fake loop: `JSL $7FA000` and `JSL $7FA180`. As soon as the game completes its normal processing and returns to the loop, it executes these two `JSL`s. But what is stored at $7FA000 and $7FA180?
$7FA000 contains my custom routine, which was copied from $7F8182 to $7FA000. I did this because, after leaving SMW's original loop and entering my fake loop, I needed to force the game to execute my code from a different location every frame. This time, it truly runs on every frame, because $7F8000 was not executed in certain game modes or situations, such as while a message box was open. With this setup, I could execute the button functions in every game mode, inside message boxes, and even on the credits' "THE END" screen—yes, Mario managed to die on the final screen of the game 🤦♂️.
Writing Code Through Controllers 3 and 4 🟢
At this point, the TAS reached another level. This is the routine written at $7FA180:
LDA $421B
CMP #$07
BEQ execute
CMP #$09
BEQ reset_pointer
CMP #$0B
BEQ toggle_writer
LDA $7F8300
BEQ return
LDA $F3
CMP #$7F
BNE return
LDA $F2
CMP #$C7
BCC write_data
BNE return
LDA $F1
CMP #$FF
BCS return
write_data:
LDY #$00
write_loop:
LDA $421C,Y
STA [$F1]
INC $F1
BNE next
INC $F2
BNE next
INC $F3
next:
INY
CPY #$04
BNE write_loop
return:
RTL
toggle_writer:
LDA $7F8300
EOR #$01
STA $7F8300
RTL
reset_pointer:
LDA #$E0
STA $F1
LDA #$A1
STA $F2
LDA #$7F
STA $F3
RTL
execute:
JML $7FA1E0
I will not explain this routine in detail because the submission is already enormous. In summary, it uses a 24-bit pointer stored in $F1/$F2/$F3 to read $421C-$421F sequentially. These registers contain the four input bytes from controllers 3 and 4, and the routine stores those four bytes beginning at $7FA1E0. The pointer advances automatically by four bytes per frame, allowing me to write four bytes per frame through controllers 3 and 4 while the game continues running normally.
The writer uses a flag at $7F8300. Pressing LEFT + UP + RIGHT on controller 2 sets it to 1 and enables the pointer. Pressing the same combination again returns $7F8300 to 0 and disables the pointer. RIGHT + UP resets the pointer to its initial address, $7FA1E0. Finally, LEFT + DOWN + RIGHT executes the code written at $7FA1E0 through controllers 3 and 4. Using this system, I could write enormous payloads containing thousands of bytes without the viewer even noticing that anything was being written.
Both $7FA000 and $7FA180 are executed every frame because the fake loop calls them with `JSL`.
Custom Music 🟢
At one point in the TAS, I had the idea of manipulating addresses in APURAM, the memory used by the game's sound processor. After some testing, and with help from ChatGPT, I discovered that I could literally insert custom music into the game while it continued running normally. In summary, I injected an AddmusicK driver and the song files I wanted through a payload. Initially, these were the four main songs from Top Gear: Las Vegas, Hiroshima, Bordeaux, and Frankfurt. The complete payload was almost 49 KB—48,959 bytes—including the AddmusicK driver, song samples, song data, and BRR directories.
This payload was written into unused or currently free memory regions, such as $7F0000-$7F4000—which is used on the overworld but free inside levels—and $7FA300-$7FC7FF, among others, and was then transferred to APURAM. In APURAM, the data occupied $0400 through $C33E. The data was transferred from WRAM to APURAM by a temporary installer written at $7FE690-$7FEA82. The installer used AddmusicK's transfer protocol to copy four blocks into APURAM, perform the internal relocations, apply the necessary patches, and load a bootstrap on the SPC700. This is a complex part of the TAS that even I do not fully understand. ChatGPT wrote all of the code, while I was responsible for testing it and preparing the required files.
Near the end, I also managed to add Aquatic Ambience from Donkey Kong Country—an incredible song—by replacing the other four songs, since there was not enough memory for all of them. I did not finish the game with the custom music installed because the game would freeze during the transition into the credits, and I could not solve the problem. Instead, I restored the vanilla SPC engine and completed the TAS normally. I chose Top Gear songs because Top Gear is the game I have played second most in my life, behind only SMW, and because its soundtrack is obviously one of the best ever made, in my humble opinion. I probably do not need to explain why I chose Aquatic Ambience, right?
Note: The game was silent in Sunken Ghost Ship because I had to stop the SPC700 before transferring all of the data, since I could not send everything at once. If I had left the game's audio running, my data would have overwritten parts of the vanilla SPC engine or music data and caused the game to crash.
Gameplay 🟢
There are still dozens of things that will remain unexplained, because otherwise this submission would never end. I wrote routines that manipulated CGRAM addresses and VRAM tiles; changed message boxes, castle-message text, and the credits; added a custom boss; added a "menu" activated by pressing Start; and much more. If I tried to explain everything, there would not be enough space here, so I will leave those details out. Let us talk about the gameplay!
In this TAS, much like in the SMB3
glitchfest, Mario—and Luigi as well—dies dozens of times, and is also revived many times, in funny and often very stupid ways. I actually think I may have overdone the number of deaths during the first half of the TAS, and I apologize for that. Those poor plumbers.
Unfortunately, I cannot provide a level-by-level explanation because it would be completely impossible to explain everything that happened in every level. Many things occurred that even I do not fully understand. Understanding the code and what each button does should already give you a good idea of the kind of gameplay that awaits you.
The crashes were intentional and calculated, although they did not have especially "obvious" purposes. The first crash happened because I wanted to update the code and decided to reset the game in a more entertaining way—crashes are fun, come on. The second crash was used to leave two-player mode, although I could have left it at any time, and enter a "Mario and Luigi" mode that allowed me to switch between them inside levels. In summary, the crashes were included purely for entertainment and were not strictly necessary.
I did not plan any route at all. My original idea was to make a 40-minute to one-hour TAS, but as I learned new things, the project kept growing until it nearly reached three hours.
Anyway, I believe I have already explained far too much here. I am certainly forgetting many other things, but please forgive me; there is simply too much to process. Just watch and enjoy one of the most glitched TASes you will ever see!
Note: I spoke about Jesus at several points in the TAS through message boxes, Mario's name in the status bar, the credits, and other places. Some people may not like or accept that, and that is okay. I speak about Jesus because He saved me from a completely purposeless life, and without Him I probably would not even be here. In any case, I have no intention of being religious or anything like that; on the contrary, I have the utmost respect for all religions, and everyone follows what brings meaning to their own life. Peace!
Special Thanks 🟢
This TAS was a somewhat "solitary" project, to the point that almost nobody knew I was making it.
Still, I would like to give special thanks to TheBiob for the TAS explanation I mentioned earlier in this submission; to God, who keeps me standing every day; and to Noise de Gole, who helped me at the beginning of this TAS, especially by sending me
this page. MrCheeze created that table, so he also deserves a mention here. My thanks also go to Major Flare, because I used his code to "spawn" the custom Iggy. Finally, thank you to everyone who will watch and enjoy almost three hours of TAS :)
A quick disclaimer: I used ChatGPT solely to save time. It helped me write code, conduct research, find routines in the SMW disassembly, locate memory addresses, create lua scripts, analyze trace logs and so on. These are things I could have done on my own, but the process would have taken hours—if not days—whereas the AI does it in minutes. As for the TAS itself, the gameplay was entirely my own work; it’s actually funny that I even have to mention this, since it should be obvious, lmao. Anyway, that’s about it. AI was just a tool, just as save states, lua scripts, frame advance and slow-down are.