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:
;========================================================
; Mario's Name Tiles
;========================================================
REP #$20 ; Set accumulator to 16-bit mode
LDA #$0A0B ; Write #$0B/0A to RAM address $0EF9/$0EFA
STA $0EF9 ; Mario's Name Setup
LDA #$180C ; Write #$0C/18 to RAM address $0EFB/$0EFC
STA $0EFB ; Mario's Name Setup
LDA #$1D17 ; Write #$17/1D to RAM address $0EFD/$0EFE
STA $0EFD ; Mario's Name Setup
LDA #$581D ; Write #$1D/58 to RAM address $0719/$071A
STA $0719 ; Mario's Name Color
;========================================================
; Palette A Colors
;========================================================
LDA #$2C00 ; Write #$00/2C to RAM address $0849/$084A
STA $0849 ; Palette A Setup
LDA #$3800 ; Write #$00/38 to RAM address $084B/$084C
STA $084B ; Palette A Setup
LDA #$7400 ; Write #$00/74 to RAM address $084D/$084E
STA $084D ; Palette A Setup
;========================================================
; Palette B Colors
;========================================================
LDA #$1FF1 ; Write #$F1/1F to RAM address $0869/$086A
STA $0869 ; Palette B Setup
LDA #$03F9 ; Write #$F9/03 to RAM address $086B/$086C
STA $086B ; Palette B Setup
LDA #$FF03 ; Write #$03/FF to RAM address $086D/$086E
STA $086D ; Palette B Setup
;========================================================
; Palette C Colors
;========================================================
LDA #$4E08 ; Write #$08/4E to RAM address $0889/$088A
STA $0889 ; Palette C Setup
LDA #$6770 ; Write #$70/67 to RAM address $088B/$088C
STA $088B ; Palette C Setup
LDA #$7FFF ; Write #$FF/7F to RAM address $088D/$088E
STA $088D ; Palette C Setup
;========================================================
; Palette D Colors
;========================================================
LDA #$347D ; Write #$7D/34 to RAM address $08A9/$08AA
STA $08A9 ; Palette D Setup
LDA #$551E ; Write #$1E/55 to RAM address $08AB/$08AC
STA $08AB ; Palette D Setup
LDA #$65FF ; Write #$FF/65 to RAM address $08AD/$08AE
STA $08AD ; Palette D Setup
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:
;========================================================
; Miscellaneous things
;========================================================
LDA #$184C ; Write #$4C/18 to RAM address $1F29/$1F2A
STA $1F29 ; JMP $4218 setup
LDA #$0A1D ; Write #$1D/0A to RAM address $0F0A/$0F0B
STA $0F0A ; "Time" Tile Setup
SEP #$20 ; Return accumulator to 8-bit mode
LDA #$1C ; Write #$1C to RAM address $0F0C
STA $0F0C ; "Time" Tile Setup
LDA #$00 ; Write #$00 to RAM address $0DA0
STA $0DA0 ; Which controllers are plugged in
LDA #$42 ; Write #$42 to RAM address $1F2B
STA $1F2B ; JMP $4218 setup
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:
;========================================================
; $0DAB checks
;========================================================
LDA $0DAB ; Load controller 2 bits = byetUDLR
CMP #$0C ; If $0DAB == #$0C -> execute gamemode routine
BEQ gamemode
CMP #$06 ; If $0DAB == #$06 -> execute music routine
BEQ music
CMP #$08 ; If $0DAB == #$08 -> execute slot4 routine
BEQ slot4
CMP #$04 ; If $0DAB == #$04 -> execute slot5 routine
BEQ slot5
CMP #$02 ; If $0DAB == #$02 -> execute waterlevel routine
BEQ waterlevel
CMP #$01 ; If $0DAB == #$01 -> execute itembox routine
BEQ itembox
CMP #$20 ; If $0DAB == #$20 -> execute deathcancel routine
BEQ deathcancel
CMP #$10 ; If $0DAB == #$10 -> execute superspeed routine
BEQ superspeed
CMP #$40 ; If $0DAB == #$40 -> execute slot7inc routine
BEQ slot7inc
CMP #$80 ; If $0DAB == #$80 -> execute infjumps routine
BEQ infjumps
;========================================================
; $0DAD checks
;========================================================
LDA $0DAD ; Load controller 2 bits = axlr----
BIT #$40 ; If bit 6 is set -> execute powerup routine
BNE powerup
BIT #$80 ; If bit 7 is set -> execute animation routine
BNE animation
BIT #$20 ; If bit 5 is set -> execute slot6tweaker routine
BNE slot6tweaker
BIT #$10 ; If bit 4 is set -> execute starpower routine
BNE starpower
;========================================================
; $17 checks
;========================================================
LDA $17 ; Load controller 1 bits = axlr----
CMP #$30 ; If $17 == #$30 -> execute messages routine
BEQ messages
CMP #$10 ; If $17 == #$10 -> execute generator routine
BEQ generator
CMP #$20 ; If $17 == #$20 -> execute spinjump routine
BEQ spinjump
;========================================================
; compare
;========================================================
LDA $19 ; Load player powerup value
CMP #$05 ; If powerup value == #$05
BEQ powerupcmp ; Reset powerup value
RTL ; Return
The final part contains the actions executed by each button:
;========================================================
; actions
;========================================================
gamemode:
LDA $A5 ; Load value from $A5
STA $0100 ; Store into game mode address
RTL ; Return
waterlevel:
INC $85 ; Increment water level flag address
LDA $85 ; Load water level flag
AND #$03 ; Limit range to 0-3
STA $85 ; Store result back
RTL ; Return
slot4:
LDA #$0B ; Load value #$0B
STA $14CC ; Store into slot 4 sprite status
RTL ; Return
slot5:
LDA #$08 ; Load value #$08
STA $14CD ; Store into slot 5 sprite status
RTL ; Return
music:
INC $58 ; Increment $58 (empty address)
LDA $58 ; Load value from $58
CMP #$1E ; Compare with maximum value
BCC music_spc ; Branch if below #$1E
STZ $58 ; Reset $58 value to #$00
music_spc:
LDA $58 ; Load value from $58
STA $1DFB ; Send $58 value to SPC register
RTL ; Return
itembox:
INC $0DC2 ; Increment item box address
RTL ; Return
deathcancel:
LDA #$06 ; Load value #$06
STA $18AE ; Store into Yoshi's tongue address
STZ $71 ; Set $71 to #$00
RTL ; Return
superspeed:
LDA #$7F ; Load maximum positive X speed (127)
STA $7B ; Set player X speed
RTL ; Return
slot7inc:
INC $A5 ; Increment $A5 (slot 7 ID)
RTL ; Return
infjumps:
LDA #$01 ; Load value #$01
STA $1471 ; Set $1471 to #$01 (player is on top of a solid sprite)
RTL ; Return
powerup:
INC $19 ; Increment player powerup status
RTL ; Return
animation:
LDA $0DC2 ; Load item box value
STA $71 ; Copy into animation/state address
RTL ; Return
slot6tweaker:
STZ $1680 ; Set $1680 to #$00 (slot 6, fourth tweaker)
STZ $1493 ; Set $1493 to #$00 (end level timer)
RTL ; Return
starpower:
LDA #$03 ; Load value #$03
STA $1490 ; Store into star power address
RTL ; Return
messages:
LDA $0DC2 ; Load item box value
STA $12 ; Store into message trigger address
RTL ; Return
generator:
INC $18B9 ; Increment generator value
LDA $18B9 ; Load generator value
CMP #$0F ; Compare with maximum value (#$0F)
BNE gen_limit ; Branch if not equal
STZ $18B9 ; Reset generator value
gen_limit:
RTL ; Return
spinjump:
INC $140D ; Increment spinjump flag
LDA $140D ; Load spinjump flag
AND #$03 ; Limit range to 0-3
STA $140D ; Store result back
RTL ; Return
;========================================================
; reset compare
;========================================================
powerupcmp:
STZ $19 ; Reset player powerup value if $19 is >5
RTL ; Return
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: ; $00806B
LDA $10 ; Has the NMI released the game loop?
BEQ MainLoop ; No: remain here waiting for the next frame.
CLI ; Allow maskable IRQs, such as the status-bar IRQ.
INC $13 ; Advance the global frame counter.
JSR $9322 ; Dispatch and execute the current game mode.
; $0100 selects the routine to execute:
; level, overworld, credits, title screen, etc.
STZ $10 ; Mark the current logic frame as finished.
; The next iteration must wait for NMI again.
BRA MainLoop ; Repeat forever.
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: ; $7F9C80
PHK ; Push the current program bank ($7F).
; This will become the bank used by the final RTL.
PEA.w ReturnFromGame-1 ; Push $9C8A.
; The final RTL increments this and returns to $7F9C8B.
PEA.w $84CE ; Create a fake 16-bit RTS return address.
JML $009322 ; Enter SMW's normal game-mode dispatcher.
ReturnFromGame: ; $7F9C8B
JSL $7FA000 ; Execute the current custom ACE routine(s).
JSL $7FA180 ; Execute the persistent WRAM writer.
STZ $10 ; Mark the current logic frame as complete.
; This permits the next NMI to perform its normal
; DMA, controller, OAM, VRAM and other updates.
WaitForNMI: ; $7F9C95
LDA $10
BEQ WaitForNMI ; Wait until NMI increments $10.
INC $13 ; Advance SMW's global frame counter.
CLI ; Re-enable maskable IRQs before running the
; next game-logic frame.
BRA FakeLoop ; Repeat indefinitely.
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:
; Read controller 2 command byte.
LDA $421B
CMP #$07
BEQ execute ; Execute the payload at $7FA1E0.
CMP #$09
BEQ reset_pointer ; Reset the destination pointer.
CMP #$0B
BEQ toggle_writer ; Enable or disable writing.
; Return when the writer is disabled.
LDA $7F8300
BEQ return
; Keep writes inside bank $7F and below $7FC7FF.
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 ; Read one byte from controllers 3 and 4.
STA [$F1] ; Write it to the current WRAM pointer.
; Advance the 24-bit destination pointer.
INC $F1
BNE next
INC $F2
BNE next
INC $F3
next:
INY
CPY #$04 ; Write four bytes per frame.
BNE write_loop
return:
RTL
toggle_writer:
LDA $7F8300
EOR #$01 ; Toggle the writer enable flag.
STA $7F8300
RTL
reset_pointer:
LDA #$E0
STA $F1
LDA #$A1
STA $F2
LDA #$7F
STA $F3 ; Reset pointer to $7FA1E0.
RTL
execute:
JML $7FA1E0 ; Run the newly written payload.
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.