Вы находитесь на странице: 1из 48

Assembly for Super Mario

World
A tutorial made for SMW hackers

Author​: Ersanio
Date of publishing​: 2nd of February, 2017
Website​: https://www.smwcentral.net/ ||​ Personal Website:​ http://ersan.io/
Version​: 1.1
Preface
This is an ASM tutorial written by me, Ersanio. I wrote this tutorial in order to teach other
people 65c816 assembly.

However, this time there is a twist. This tutorial is tailored specifically for SMW hackers, in
order to make examples more ‘tangible’. I realized my previous tutorial is quite… the read,
so this tutorial will have their chapters be short and sweet.

My previous ASM tutorial, “Assembly for the SNES” mainly focused on opcodes and what
happened behind the scenes. This tutorial will use examples which have clear effects on the
game Super Mario World. Think about stuff like changing the player’s power-up status, or
the extra lives count.

This tutorial won’t teach you how to code the most savage bosses ever or code a patch
which turns SMW into a railroad shooter, but it will teach you the very basics of ASM to get
you started on your assembly adventure.

Before you start reading this tutorial, I highly recommend you to download my
previous ASM tutorial and keep the few chapters about hexadecimal, binary,
addresses & values and finally, ROM and RAM ready for reading. ​These are basic
concepts you must understand, otherwise this ASM tutorial won’t make a lot of sense.

If you ever want to contact me for whatever reason, you can find me at:

https://www.smwcentral.net/​ as user ID 3, Ersanio


http://ersan.io/​ my own website
https://twitter.com/Ersanio​ my Twitter account
Discord: Ersanio#9746
IRC: irc.caffie.net, channels #smwc and #serioushax, as Ersanio

Many special thanks go to certain ASM people for directly (and indirectly) teaching me
assembly:
Bio, Killozapit, MiOr, schwa, Smallhacker, smkdan, Sukasa, Roy (Fuzzyfreak),
p4plus2, and many others

1
Version history
Version 1.1
- Chapter 6: Brief explanation about SEC
- Chapter 6: Very brief mention about indexing and the stack
- Chapter 9: Fixed a wrongly named button reference (“Into” -> “Step”)
- Chapter 9: Additional information about identifying infinite loops
- Chapter 10: Register B explanation made more clear
- Chapter 10: Made zero-flag branching more clear

Version 1.0
- Initial version

2
Table of contents

Preface

Changelog

Table of contents

Chapter 1: Introduction to 65c816 Assembly

Chapter 2: How SMW works

Chapter 3: Your new best friend, the RAM map

Chapter 4: Basic operations

Chapter 5: Manipulating the game mechanics

Chapter 6: Introducing coding

Chapter 7: Gopher Popcorn Stew (also known as blocktool)

Chapter 8: Patches

Chapter 9: Debugging and crashes

Chapter 10: Frequently Asked ASM Questions

Chapter 11: Final notes

3
Chapter 1: Introduction to 65c816 Assembly
You might have heard of ASM, or 65c816, or assembly. So what is ASM? ASM stands for
AsSeMbly. Breaking down the different parts of the acronym 65c816, the 816 means that the
processor can be either 8-bit mode or 16-bit mode. The c stands for CMOS, 65 means that
this processor is from the 65xx CPU family. The processor is supposed to be pretty
revolutionary for its time. In this tutorial I will explain mnemonics/instructions (I call them
opcodes), and how to use them properly.

With 65c816 ASM you can code stuff for SNES games (such as custom features for Super
Mario World). ASM is a 2nd generation programming language (which is low-level compared
to C# for example). It is readable machine code, which eventually gets translated into
hexadecimal machine code. All the opcodes consist of 3 letters, along with various
parameters.

4
Chapter 2: How SMW works
Super Mario World runs thanks to the ROM (“Read-Only Memory”) inside the cartridge being
read out by the SNES. The SNES has a fixed area of memory designated to be the game’s
RAM (“Random Access Memory”). The programmers gave the RAM ​meaning​ using ASM
inside the ROM.

This way, changing the values within RAM addresses result in manipulating the game with
clearly visible effects, such as making the player grow or shrink, or giving the player coins.
Some RAM addresses even manipulate other RAM addresses when a value is written to
them.

To give a very simple example of above paragraph, here is what happens if the player
happens to touch a coin:
1. The coin sound effect plays
2. The coin disappears
3. Coin sparkles appear
4. The game tells itself to add 1 coin to the coin counter

To play a sound effect, the game writes to the sound effects RAM address $7E1DFC. This
address is directly linked with the SNES’ SPC-700 thanks to the game’s programming, giving
the address this purpose.

Number #2 and #3 are slightly complicated. It is not required to understand these (yet). What
they do is call a routine to despawn the coin and to spawn in sparkles.

#4 is a bit more complicated. To increase the player’s coins by 1, the game writes 01 to
$7E13CC. You’d think the game would just i​ ncrease​ the player’s coin counter by 1 rather
than ​change/set​ it to 1, but this is what point #4 means: the game telling itself to add 1 coin
to the counter, because whenever you write to $7E13CC, the game reads it out
behind-the-scenes and increases the actual current coins of the player, $7E0DBF, by that
amount.

This may sound confusing, but consider the following: There are a few sources of coins in
Super Mario World. The coin blocks, Yoshi eating berries and enemies, not touching the
goal tape’s tape gives you 1 coin, silver POW turns enemies into coins, and so on. Also
consider that the game gives the player a 1-up whenever the player has 100 coins and then
the coin counter resets.

If the game had to check for 100 coins every time the player obtained a coin, it would cause
a lot of duplicate code. Therefore, the developers programmed an address which tells the
game “hey, ​increase​ the player’s coins by an X amount and if the coins amount to 100, give
the player a 1-up and reset the coins back to 0”. This is how RAM $7E13CC got its p ​ urpose​:
it became a general coin handler.

5
On a semi-related note, there’s an unused sprite which gives the player 5 coins at once:
sprite $7E, the flying red coin sprite. This is probably another reason why $7E13CC exists.

If the programmers wanted to, they could’ve used other addresses for this purpose, such as
$7E1358, $7E1734, and so on. It could’ve been ​anything​, but the game has been
completed and published, so there’s no changing them. Every single address has a purpose
(even “unused” is a purpose), thus each address can have its effect documented. This is
where I introduce...

6
Chapter 3: Your new best friend, the RAM map
That’s right. Considering Super Mario World’s RAM addresses have a set purpose, it is
possible to compile a gigantic list of addresses with their effects on the game. Super Mario
World Central hosts a complete RAM map of Super Mario World. This is how a documented
RAM address looks like:

A RAM address consists of at least 3 parts: The address itself (first column), the size
(second column) and finally the description (fourth column). The third column is the address
type. Kind of like a tag/label. It’s not mandatory.

This RAM address is a perfect example of a well-documented RAM address. It tells in few
words what it is, and also gives a list of valid values the RAM address can contain.

Maybe it’s noticeable that the values end at #$03. If a RAM address documentation has a list
of values but abruptly ends like this, it usually means that the other values (04, 05, … FE,
FF) are invalid. They either do absolutely nothing, or glitch out/crash the game.

You can do a few things with RAM addresses. You can either read their values, or you can
overwrite them with new values.

In this address’ case, the powerup status is read when the player hits a which contains a
powerup. If the player is small, the block will spawn a super mushroom. If the player is
anything but small, the block will spawn a cape feather or a fire flower, depending on what
block you have placed.

The address is also written to when the player actually touches a powerup. If the player
touches a cape feather, this address is set to 02, meaning the player now has the cape
powerup.

7
Chapter 4: Basic operations
Assembly is capable of many many things, but to start off with the basics and to keep things
simple, you will be introduced to basic operations first.

Loading values
Assembly is capable of “loading operations”. You can for example load a RAM address’
value. Handy if you want to ​read out​ the coin counter or Mario’s current powerup status for
example, or if you want to ​load any number you want​ to store it somewhere later.

Storing values
Assembly is also capable of “storing operations”. With storing operations, you can
manipulate the game’s mechanics, such as s ​ etting​ Mario’s powerup to cape, or changing
the level’s current music.

Increasing/decreasing values
Assembly is capable of increasing and decreasing numbers. A simple example of this is
​ ecreasing​ the timer by one.
increasing​ Mario’s lives by one, or d

Comparing values and jumping to other bits of code


Finally, assembly is capable of ​comparing​ values. An example of this is giving the player an
extra life after collecting 100 coins, because the game basically checks “​If​ player has exactly
100 coins ​then​ give them an extra life and set his coins to 0”. If the condition of 100 coins is
not met, the code jumps over the extra lives code and skips it.

You will be seeing examples of these 4 operations later in the tutorial.

8
Chapter 5: Manipulating the game mechanics
To make a smooth start with learning ASM, you will need a certain emulator: “Geiger’s
snes9x debugger”. Google this and you’ll find websites like Zophar’s Domain,
Romhacking.net and even SMWCentral hosting this. Feel free to download the latest version
you can find. This emulator will let you play around with Super Mario World’s inner workings
without having to write a single line of code. It is important that you actually do the steps
mentioned. Don’t just read this chapter and think “oh, ok, I understand this” because then
you won’t be learning anything.

Admittedly, the emulator is kind of outdated but it’s still excellent for learning ASM. It’s also
not CPU-intensive and is really easy to use. It’s just a modified version of snes9x, after all.

Upon running this emulator, there will be two windows you will be met with immediately: the
regular emulator window, and the following window called the Debug Console:

The debug console allows you to ​debug ROMs. Debugging is basically the process of finding
and fixing bugs of computer software. I recommend you don’t get too attached to this
emulator though, because in the debugging chapter we’ll be using a clearly more superior
SNES debugger.

9
Load the ROM as you would in any emulator, then make sure to press the Run button if the
​ how Hex​ button. You will now see a Hex
game hasn’t started running yet. Now press the S
Editor:

In this window, press the drop down list saying ROM, and select RAM. Now you will see the
window gets filled with a bunch of numbers. All these numbers are hexadecimal:

What you will see here is the RAM of Super Mario World, and it’s going to change ​a lot​ as
the game runs. We’re talking about thousands of changes in less than a second. This
happens because Super Mario World runs code, and the code uses RAM to store and read
its variables.

In order to learn to manipulate the basics of the game, we will play a bit with the RAM. First,
enter any level you desire. To not be interrupted by Nintendo’s brilliant design to put a
charging enemy in the first screen, avoid entering Yoshi’s Island 1 because your hands will
be busy with the debugger console rather than the gameplay itself.

10
In the first input field which says “7E0000”, replace it with “7E0019” and press Set Range.
The red text at the top left now will say 7E0019. The first number to the right of that red
7E0019 should say 00. Click it, and overwrite it with the number 01 (don’t forget the 0). What
happens now? Click the emulator window to put the focus back to the game, and see for
yourself:

That’s right, Mario turns into Super Mario. The change in the RAM address is immediately
visible in the gameplay; Mario increased in size. Also, behind the scenes, the game noticed
the change in the powerup status and started displaying appropriate graphics and palettes.
The game is programmed that way. Of course, you can also set powerup status to 02 and
03:

11
So… what’s beyond 03…? Well:

Woah! Suddenly, Mario looks all weird! The game doesn’t recognize powerup states 04 and
05, and changed Mario into Halloween-y and Christmas-y because it started reading bogus
data to alter Mario’s appearance, and when you touch a powerup the game simply crashes
most of the time. The lesson here is that when you write ASM, don’t accidentally write invalid
values to RAM addresses.

Let’s play with another address. Let’s manipulate the coin counter. Replace 7E0019 in that
first input field with 7E0DBF, and then input 09 into the RAM. You’ll see the coin counter
suddenly changes from 0 to 9:

12
That’s great, now we learned how to manipulate the coin counter. Let’s go for 100 coins so
that it gives us a 1-up. Change the value from 09 to 64. 0x64 in hex is 100 in decimal. You
will see that something strange happens.

As you can see, the coin counter turned into something strange. It’s saying “A0” which is a
strange amount of coins. We never see that in Super Mario World during normal gameplay.

So how ​can we get 100 coins and get a 1-up out of it? As I mentioned earlier in the tutorial,
there is a coin handler in SMW: RAM $7E13CC.

Change the RAM address we edited from 64 to 63. You will see the coin counter will now
say “99”. Then, go to RAM $7E13CC inside the hex editor and change that address to 01.
As you can see, Mario will obtain a 1-up and the coin counter resets to 0. However, the
address we edited also resets to 00 immediately. Change it to 01, and it resets back to 0
while adding another coin to the counter. Let’s change it to 02 this time. The game adds 2
coins to the counter! The game ​normally never adds multiple coins at once to the counter,
but it is capable of this anyway. Just for fun, change the value to FF now - the highest
possible number in hexadecimal. You’ll see how the coin counter will suddenly increase
incredibly fast, while the value you inputted in the RAM clearly decreases in number. This is
how the coin handler works. “So what is 7E0DBF used for?” You can use it to compare coin
values (for example checking if the player for example has 43 coins). It’s good for reading,
rather than writing to.

Finally, go to address 7E1DFB and change it to 03. Suddenly, the underwater music plays.

If you want, feel free to play around with other addresses as well to get used to how the
game handles certain elements, such as the player’s extra lives, the player’s X position
within the level, the player’s speed, pausing/unpausing the game, etc. Just ctrl+f in SMWC’s
RAM map and search for anything you want to play around with. You will need to learn how

13
the game works behind-the-scenes like this in order to write code and understand what its
results are. You will also need some practice in navigating and searching in the RAM map.

14
Chapter 6: Introducing coding
When coding in ASM, you’ll have to understand how the processor reads all the code and
executes them. ASM is executed by the processor of Super Nintendo, and in order to
understand how the processor executes ASM opcodes, you’ll have to understand what the
opcodes actually do. For that, I’d like to refer to my previous ASM tutorial for in-depth details
about the opcodes. Here, I will focus on opcodes and especially their practical applications in
SMW.

Loading, storing and the accumulator


ASM is written by writing ​opcodes​ with ​parameters​. An opcode is a 3-letter long acronym.
An opcode can have either no parameter or a parameter which is 1, 2 or 3 bytes in size.
​ DA
Let’s introduce you to the first opcode: L

LDA #$02

As explained above, an opcode has a 3-letter acronym and parameters. In this case, this
opcode has an immediate value parameter.

What LDA does is load a value into the accumulator. This value can either be an immediate
value (a number prefixed by #$) or a value from an address (a number prefixed by $). In this
case, what this code does is load the value #$02 into the a​ ccumulator​ register (or “A” in
short). This is why it’s called LDA; LDA stands for LoaD into Accumulator.

So what is the accumulator, then? The accumulator is a register of the 65c816 (but for
simplicity’s sake let’s just call 65c816 the SNES from now on). It is capable of memorizing a
number for later use. It is also capable of being modified by math, like increasing or
decreasing its value by 1, 6, 14, etc. With “later use”, imagine things such as storing its value
into the RAM, comparing its value with the RAM, and so on.

Basically imagine the accumulator as you reading and memorizing a number. Look at the
time. What hour is it? Memorize it. Let’s say 2 PM. See, you just read a number from
somewhere and memorized it, and you can do many things with the number like… telling
a friend about the current time!

The second opcode you’ll learn is STA. STA stands for “STore Accumulator”, and what it
does is storing the value inside the accumulator somewhere. Here’s an example:

STA $7E0019

15
What this code does is store whatever number is inside A, to the address specified after
STA. With this opcode, you w​ rite​ to addresses. This is your primary opcode to manipulate
addresses, and thus, the game.

There’s a tiny variation of the opcode STA called STZ. STZ - STore Zero - sets an address’
value to 00 immediately. It doesn’t need A, it doesn’t modify A. It just straight out writes 00 to
an address.

Finally, there’s another important opcode which is always used to finish up a function (a
“subroutine”) you just wrote. Actually, it comes in two variations:

RTS

RTL

They both serve the same purpose, they’re just used in different contexts you don’t need to
know yet. If a patch or tool you use tells you to end a subroutine with an RTS, then you use
that. Same deal with RTL. RTS and RTL stand for “ReTurn from Subroutine” and “ReTurn
from subroutine Long”, respectively. A general rule to follow is “if a subroutine is called with a
JSR, end it with an RTS. If called with a JSL, end it with an RTL”. We will talk about
subroutines later in this chapter.

So what we’ve learned so far is that with LDA we can load a value into A, with STA we store
A into an address, and with RTS or RTL we finish up the function. Combining these 3
opcodes, it’s possible to write code to change Mario’s powerup:

LDA #$01
STA $7E0019
RTS

If you remember that RAM map example from earlier, we know that value #$01 of $7E0019
means “Big Mario”. This code loads 01 into A, then stores it into RAM $7E0019. Then the
routine finishes up with an RTS.

Comparing values, branching and labels


ASM is also capable of conditional operations. You can make it so that if a certain condition
is met, you can skip a bunch of code. There are two types of opcodes to do this. The first
one is CMP:

CMP #$02

CMP (which means CoMPare), compares the contents inside A with the parameter it has.
Suppose A has the value of 01. Compare 01 with 02, they don’t match.

16
So, what now? There are opcodes which can skip a code depending on if a comparison
​ ranches​. Here’s an example of a branch:
matches or not, which are called b

BNE skip

BNE (which means Branch if Not Equal) does exactly what it name says. If a comparison
mismatches, the branch will be “​ taken”​; the code jumps to the ​label​ called “skip”. There is
also a BEQ (which means Branch if EQual). This code branches if the comparison DID
match.

So what is a label? A label is basically any word you want to use to mark a location inside
your code. You will see an example of a label very soon.

So let’s combine our knowledge so far. We know how to load and store values, and we know
how to skip certain code depending on comparisons and their results. Knowing this, we can
easily write code which changes the level’s music whenever Mario has the fire powerup:

LDA $7E0019
CMP #$03
BNE ​skip
LDA #$05
STA $7E1DFB
skip:
RTS

To explain what this code does: Mario’s powerup status gets loaded in the accumulator, then
is checked if it’s #$03 (fire Mario). If it’s NOT fire Mario, then skip to the RTS, finishing up the
routine. ​Otherwise, play music #$05 then also finish up the routine.

Doing math
The SNES is capable of basic + and - operations, which work by editing the value inside the
accumulator or an address.

To increase A by 1, you can use the following opcode:

INC A

INC stands for INCrease. Its parameter is A. What this opcode does is increase the current
number inside the accumulator by 1. If the accumulator had the number #$45, it is now
#$46.

What if you wanted to increase A by more than 1? Writing “INC A” #$25 times to increase A
by #$25 seems kinda unhandy. Thankfully, there’s an opcode which can deal with larger
increments:

17
CLC
ADC #$25

ADC stands for “ADd with Carry” and adds whatever is in A with the parameter of ADC.
What this code does is add #$25 to the accumulator. Remember that we’re still working with
hexadecimal numbers. If the accumulator had the number #$45, after this operation it is now
#$6A - 106 in decimal.

However, as you can see, the opcode is preceded by “CLC”. CLC stands for “CLear Carry”.
Because ADC is “add ​with​ carry”, you’ll want the carry cleared 99% of the times, otherwise
you might end up with 6B instead of 6A - the carry counts as +1 in this case.

There are also opcodes which can decrease A. To decrease A by 1, you can simply use:

DEC A

...which stands for DECrease A. If the accumulator was #$45, now it’s #$44.

You can also decrease A with larger numbers:

SEC
SBC #$25

SBC stands for “SuBtract with Carry” and subtracts whatever is in A with the parameter of
SBC. As for “SEC”, it’s basically SBC’s counterpart used for subtractions.

ADC and SBC can also modify A when these opcodes have an address supplied as a
parameter:

SEC
SBC $7E0019

In this case, the value of accumulator is subtracted with the value​ inside ​$7E0019. It doesn’t
modify 7E0019. SBC still modifies A. The same goes for ADC.

Also, INC and DEC are capable of increasing/decreasing addresses directly without affecting
the accumulator:

INC $0019

This increases 7E0019 by 1, but something is off...

Woah! The 7E of 7E0019 is missing. How come? Well, the thing is, INC $7E0019 doesn’t
exist. INC with 6 numbers as its parameter doesn’t exist. Same with DEC. Therefore, the
parameter got shortened to $0019. So what’s this about?.

18
Shortening addresses
It is indeed possible to use a shorthand notation for addresses, but they have a certain
condition. You can change these conditions manually using more advanced coding, but for
now we’ll focus on the basic conditions which occur like 99% of the time.

You can make an address 4 digits instead of 6 digits if:


1. The address is in bank 7E (the “bank” being the first 2 digits of a 6-digit address).
2. The final four numbers are between 0000 and 1FFF hex
With this, you can for example shorten $7E1344 to $1344.

You can also make an address 2 digits instead of 6 or 4 if:


1. The address is in bank 7E
2. The final four numbers are between 0000 and 00FF hex
With this, you can for example shorten $7E0019 - the player’s current powerup - to $19,
which can result in the following:

STA $7E0019
STA $19

They both do the exact same thing, except the second code is shorter! This technique also
deals with INC $7E0019 being nonexistent. You can simply use INC $19.

Indexing and stack operations


There are two more important concepts in coding, which are “indexing” and “the stack”. With
indexing, you’ll deal with similar registers as A, called X and Y. With the stack, you can
preserve values inside A, X and Y at a certain (predefined) location in SMW’s RAM. For
more information about indexing and the stack, please refer to my other tutorial (“Assembly
for the SNES”), chapters ​18​ and ​21​.

Applying what we have learned so far


Let’s combine most of the opcodes we have learned so far to get some practice. We will be
writing code which increases Mario’s bonus stars by 9 when Mario has a cape and make him
small Mario again. The address which handle’s Mario’s bonus stars is $7E0F48. We will also
shorten down the addresses to the bare minimum along the way:

LDA $19
CMP #$02 ;Check for cape status
BNE NoCape ;If not cape, skip next block of code
LDA $0F48
CLC ;Add 09 to Mario’s bonus stars
ADC #$09
STA $0F48
STZ $19 ;Set powerup status to zero - 0, meaning Small Mario
NoCape:
RTS ;Finished

19
Notice that I suddenly started writing actual text inside my code explaining things. These are
called ​comments​. Comments are ignored by the assembler. Comments always start with a
“​;​”. Thanks to the comments, I don’t have to explain what the code does anymore.
(Hopefully).

One more thing: Subroutines


Earlier in this chapter I mentioned subroutines. Subroutines are blocks of codes which can
be called during another block of code. Calling subroutines is very handy, because you won’t
have to write duplicate code.

Assume the following situation. In Super Mario World, you get points awarded when you
stomp on an enemy, right? You don’t want to rewrite the same point awarding routine for
every single enemy, right? That’s why it can be written as a “subroutine”:

As you can see, this Award Score routine is located outside the flow of code in both
situations. The routine is being “called”. There are two opcodes to call subroutines: JSL and
JSR. For now, you don’t really need to know the difference, but for the sake of simplicity let’s
use JSL.

LDA #$03
STA $1DFC
LDA #$01
JSL AwardScore ;A = 01
STZ $SpriteStatusRAMaddress ;For now I’ll use names rather than numbers
[...]
[...]

AwardScore: ;When the code gets here A remains unchanged


STA $somescoreRAMaddress ;It’s like calling functions with parameters
RTL ;Therefore, we stored 01 in this address

What this code does is: play a sound effect (write to $1DFC), load the score value 01 into A,
then call a subroutine which deals with awarding the player score. Once the subroutine is
done, finish it off with RTL. Then the code jumps back straight into that STZ.

20
RTL allows you to call the subroutine from anywhere. This way, in the subroutine, you don’t
have to specify where exactly the code needs to return. It doesn’t need to know if it needs to
return to the shelless koopa routine, or the flying koopa routine. It just “returns to where it
came from”.

Phew! This was a rather large chapter! We now know how to write basic code. There is only
one problem though: How do we even insert code into Super Mario World? Wouldn’t it be
awesome if we actually started running code in SMW? YOUR code?

From now on, things are going to get r​ eal​. Let me introduce to you...

21
Chapter 7: Gopher Popcorn Stew (also known as
blocktool)
Gopher Popcorn Stew, or GPS for short, is a block inserter made by p4plus2. Why the tool is
named like that remains a mystery even to this day.

Blocks are what the terrain inside levels is made of. Ground, coins, pipes, these are all
blocks (or in SMW terms, “map16 tiles”). What GPS does is inserting your code for specified
map16 tiles. You can, for example, execute code when the player touches a turnblock from
above. This is a perfect opportunity to play around with ASM!

Setting up GPS
This section will be a mini-tutorial on how to get GPS to work, but for more information
please refer to the tool’s readme!

First of all, make sure your SMW ROM has been edited by Lunar Magic at least once. Move
around stuff in a level and hit save. That’s enough to make GPS work. Place this ROM and a
shortcut of Lunar Magic in GPS’ directory.

Second, make a new text file (just right click inside the directory and select a text file from
the new file menu). Name it “insert.bat”. If you can’t see the file extensions, google to find out
how to show file extensions.

Right click the file and select Edit. You can now edit the bat file as any text file. Write down
the following:

gps.exe smw.smc
@pause

Where “smw.smc” is the name of your ROM. I recommend you to change the ROM name
from “Super Mario World.smc” to “smw.smc” so that you don’t have to type a lot when you
use various SMW hacking tools. Ctrl+s to save the file, and exit. You now have your inserter
ready.

Readying an ​ASM file


Navigate to the “blocks” directory. It should be empty. Make a new text file again, but this
time name it “example.asm”. Make sure to associate .asm files with Notepad or any other
text editor of your preference. Open the text file and copy paste the following block of code in
it:

22
db $42
JMP MarioBelow : JMP MarioAbove : JMP MarioSide
JMP SpriteV : JMP SpriteH : JMP MarioCape : JMP MarioFireball
JMP TopCorner : JMP BodyInside : JMP HeadInside

MarioBelow:
RTL
MarioAbove:
RTL
MarioSide:
RTL

TopCorner:
RTL
BodyInside:
RTL
HeadInside:
RTL

SpriteV:
RTL
SpriteH:
RTL

MarioCape:
RTL
MarioFireball:
RTL

Then save it. This is the basic template for ​custom blocks​. Custom blocks always start with
db $42 for reasons you don’t have to know. Then, it is followed by a j​ ump table​ (look at all
those JMPs). Finally, there’s a list of labels with an RTL at the end.

Each label executes under certain conditions. MarioBelow executes when Mario touches the
block from below. MarioAbove is the same, but from above. MarioSide executes when Mario
touches the block from the left or right.

TopCorner executes when Mario touches the block’s top left or right corner. BodyInside
executes when Mario’s body is inside the block, and HeadInside executes when Mario’s
head is inside the block.

SpriteV and SpriteH execute when any sprite touches the block from top/bottom and
left/right, respectively.

Finally, MarioCape executes when Mario hits the block with his cape, and MarioFireball
executes when Mario’s fireball hits the block.

As long as these conditions are met, the code executes ​every frame​. This means that if
Mario stands on a solid custom block, that block’s code will execute every frame. 60 times a
second.

23
Your first custom block!
We will be writing a block which when Mario walks over it, sets Mario’s powerup status to 02,
making him Cape Mario!

Locate the following block of code inside that ASM file:

MarioAbove:
RTL

Change it to:

MarioAbove:
LDA #$02
STA $19
RTL

As we learned earlier in the tutorial, this sets RAM $7E0019 to 02, which changes normal
Mario to Cape Mario. Save the ASM file but don’t close it yet, because we will be editing it
again later!

Go back to GPS’ directory and open l​ ist.txt​. Input the following:

200 example.asm

This defines where the code we just wrote in example.asm should be inserted. This custom
code will be run when Mario touches ​Map16 tile 200. 200 is in hex, by the way, so is the
numbering of the map16 tiles.

We’re almost there. Run the .bat file we made earlier (“insert.bat”). If it mentions that it
inserted the block successfully, congratulations! If not, try finding out what went wrong during
these steps by reading the error logs. These steps are literally perfect though.

Testing the block


So our block has been inserted. It’s time to try it out. If you had Lunar Magic open this whole
time, simply go to the insert objects screen and use the Direct Map16 Access. Then, go to
map16 page 2, it has tiles 0x200 to 0x2FF. If you hover with your mouse over map16 tile
0x200, you will see the following tooltip:

24
If you don’t see that purple tooltip, don’t worry. Simply restart Lunar Magic and try again.
This tooltip indicates the block has now our custom block code.

25
Insert the block into the level:

The blocks will have blue square graphics by default. That’s fine. Give it custom graphics if
you want to, but that’s not relevant to this tutorial. Save the level and run it in your emulator
of choice.

26
This is how the level will look like.

The blocks clearly have different graphics, but don’t let that bother you, it’s just a graphical
glitch because we haven’t given the blocks custom graphics. Next, jump onto the blocks.
You will see the blocks will trigger our code:

And ​that’s it​! That is how you write custom blocks for Super Mario World!

Now you might be wondering, why are we even writing blocks? The thing is, blocks are the
easiest things to code for Super Mario World. ASM for SMW consists of 4 major ‘parts’:
1. Custom blocks
2. Custom sprites
3. Custom patches
4. “UberASM”

27
Custom blocks are the easiest things to write while custom patches are the hardest.

Patches modify the game’s very core, making it run differently. “UberASM” is a patch for
example, described below.

As for sprites, they’re entities with certain behavior and interaction. The difficulty to code
them depend on the complexity of the behaviours the sprites should have.

Blocks are just… blocks inside the level, sitting there, waiting to be touched by the player or
sprite entities. They don’t have any movement behavior, so the code can be incredibly easy
to write. This will allow easy practicing of ASM.

Finally, UberASM is a patch made by p4plus2 which allows you to execute code depending
on the game’s current state, whether it be a certain level or certain game mode. It’s a very
extensive and complete patch, allowing very flexible ASM code.

Back to coding, I want you to make one more block. Edit the ASM file we just inserted
earlier. Replace our code with the following:

MarioAbove:
INC $19
RTL

What this code does is ​increase​ Mario’s powerup status. Save the ASM file, then run the
.bat file we made earlier. There’s no need to delete and re-insert the blocks into the level.
The code is automatically updated for those blocks. Now, run the ROM again.

​ ery​ strange
Enter the level and jump on the blocks. You will notice that something v
happens. Mario will start glitching out:

“Kill me” ~ INC $19 Mario

28
This is because custom block code runs e ​ very frame​, not just once. This is very important to
keep in mind. What our code now does is increase Mario’s powerup status e ​ very frame​.
This means his powerup state increases 60 times per second. As a result, he is mostly
rapidly cycling through invalid powerup states because the RAM address went waaaaay past
value #$03. Once it reaches value #$FF, it wraps back to #$00.

If you want to make a block which increases Mario’s size only once, you will have to change
the block into a blank tile once it’s touched so it’ll never execute again. Because the map16
tile (0x200) changes into a blank tile (0x25), and we don’t have code on the blank tile, our
code basically won’t exist in the level anymore!

Replace our MarioAbove code once again, but with the following code this time:

MarioAbove:
INC $19 ;Increase powerup status
LDA #$02 ;\ Block to be generated: blank tile
STA $9C ;/ Check the RAM map for documentation on 7E009C.
JSL $00BEB0 ; Block change routine
RTL

Here’s the result:

As you can see, when Mario lands on the block, the block disappears and Mario turns into
Super Mario. If Mario landed on this while being cape Mario, he would become fire Mario. If
he were fire mario, he’d become a glitched Mario.

29
If you don’t want Mario to become glitched after fire Mario, you’ll need to add an extra check:

MarioAbove:
LDA $19
CMP #$03
BEQ + ;If fire Mario, skip code and go to +
INC $19 ;Increase powerup status
LDA #$02 ;\ Block to be generated: blank tile
STA $9C ;/ Check the RAM map for documentation on 7E009C.
JSL $00BEB0 ; Block change routine
+
RTL

The block won’t do anything if you’re already fire Mario. If you want the block to disappear
anyway, you’d move the + to after INC instead of JSL.

30
Chapter 8: Patches
Patches are actually one of the more tough ASM-related things you need to write. The
process of writing a patch consists of multiple parts:

- Finding relevant code you want to “hijack”


- Actually hijacking the code without breaking anything
- Writing the patch
- Making sure it doesn’t crash or cause unintended side effects (testing)

Of course, you also need to set up a patching environment and everything to get started.

Getting started
Make a separate directory somewhere and put your SMW ROM and a shortcut to Lunar
Magic there. Next, download ​asar ​from SMWCentral’s tools section:
http://www.smwcentral.net/?p=section&a=details&id=13386

After downloading asar put asar.exe into the same directory. Next, open your SMW ROM
with Lunar Magic, edit it once and save the ROM in order for it to expand to 1 MB. Finally,
rename your ROM to “smw.smc” so it’ll be easier to use in asar. And that’s about it! That’s
how you set up your patching environment for this tutorial.

Finding relevant code to hijack


“Hijacking code” means replacing part of the original code with a c ​ all​ to your new custom
routine. For the sake of this tutorial, we’re going to make a really simple patch: Whenever
Mario dies, set his coin amount to 0.

We know what we want to do now, but where do we begin? You have t​ wo​ options:
- Consulting the ​SMW ROM​ map:
- http://www.smwcentral.net/?p=nmap&m=smwrom
- Consulting ​all.log​:
- Original: ​http://www.smwcentral.net/?p=section&a=details&id=4727
- Improved: ​http://www.smwcentral.net/?p=section&a=details&id=13732

ROM Map
The “ROM map” is basically a map of the ​original, unmodified​ SMW ROM. It tells you
which addresses do what. Here’s an image of part of the ROM map as an example:

31
As you can see, it tells you the ​SNES ROM address (​ NOT PC hex), its size, its type and its
description. By going to SMWCentral’s SMW ROM map and searching for specific keywords,
you can find clues to where in the ROM certain features are located.

“all.log”
All.log is a ​full disassembly​ of the Super Mario World ROM. The first link is to the original
version, the second link is basically the same but is commented much more thoroughly.
Personally I never had the need to use the latter, so for this tutorial we’ll use the original
all.log. Don’t worry, it won’t be as difficult as you may think.

All.log has a simple format. Here’s a few lines of code for the sake of explaining the format:

There are mainly two types of labels you’ll need to know about. DATA_xxxxxx which denotes
tables of raw data, and CODE_xxxxxx which denotes actual ASM code. The “xxxxxx” is the
SNES ROM address of that instruction.

Between CODE_xxxxxx and the actual opcode, you’ll see one or more hexadecimal
numbers. That’s the hex version of the opcode and its parameters. Thanks to those
numbers, you’ll know how many bytes an instruction is made of. ​This is important
information to know in order to make patches which don’t end up crashing the ROM.

Back to finding a hijack


So we have two versions of SMW documentation: the ROM map and all.log. If we want to
hijack the death routine, we need to look for death-related things. In either SMW’s ROM
map, or all.log you could start searching (ctrl+f) for specific keywords, such as “death” or
“kill” (as in, kill the player). Doing this in SMW ROM map points us to “death pose”, but that’s
not something we need. Further searching brings us to ROM address $ ​ 00F606​.

ROM address $00F606 is documented as “Death Subroutine (JSL to it to kill Mario)”. This is
the information we’re looking for. Knowing the address, we can view that subroutine in
all.log. Ctrl+f for “CODE_00F606”. But wait, that doesn’t give any results! This is because
all.log actually gave some routines proper labels. If you can’t find a certain address in all.log,
try searching for adjacent addresses, e.g. 00F607, 00F608, etc. or the relevant keywords
until you’re lucky.

32
Here is the entire Kill Mario routine:

So we located the code which handles the player’s death routine. The question is, what part
of the code will we hijack? In this case, you can pretty much hijack any part of the code, but
generally a good practice is hijacking code which is a simple LDA and STA, away from any
comparison operations (such as CMP, BEQ, BCC, etc.), as well as any stack operations
such as pushes or pulls. CODE_00F60A seems perfect. We have located our hijack
location!

Actually hijacking the code


...without breaking anything. That’s very important.

Remember how I mentioned there’s a code which can call subroutines? Now it’s the time to
use it: ​JSL​. ​JSL is always 4 bytes long, no matter where it points.​ ​You know something
is incredibly important when I color it... purple?

In order to make hijacks which don’t break anything, you’ll need to do some “byte counting”.
All.log is very convenient for this, because you can see how many bytes an instruction uses.
You can see that LDA #$09 uses two bytes, while STA $1DFB uses three bytes. Our JSL
uses four bytes, so ​there will be 1 byte left over​. That’s not stopping us, though!

Writing the patch


We now have all the information we need and we can start writing our patch now. I will
immediately introduce new concepts as well. Here is part of the patch - the barebones:

ORG $00F60A ; The address to be hijacked


autoclean JSL Hijack ; Jump to our custom code
NOP ; Overwrite the leftover byte with an
; opcode which does literally nothing

freecode

Hijack:
RTL ; Always use an RTL after a JSL

33
ORG is not an opcode. It’s rather an instruction for the assembler itself meaning “origin”. It
basically tells the assembler “place the code at this ROM address”. The code o ​ verwrites​ the
existing code, they don’t get “inserted”. This is why we also used the opcode NOP. If the
leftover byte wasn’t dealt with, it would’ve caused the SNES to start reading bogus
instructions once it got there.

“autoclean” is also an assembler instruction and to be perfectly honest I’m not sure what
exactly it does, but I heard it is required to clean up everything below “freecode” in case the
patch was assembled multiple times after making edits.

“freecode” tells the assembler to look for free space in the ROM in order to place your own
custom code there.

What this code basically does is replace part of the original code with a call to your custom
code located elsewhere in the ROM. This patch can actually patch without problems, and I
advise you to actually do that right now.

Make a new text file and name it “patch.asm”. Copy the code above and paste it in there.
Save the file and then run asar.exe. Follow its instructions. Input the patch name and the
ROM name and it’ll tell you that the file assembled without any problems. Now you can try
out our small patch. Run the game and get Mario killed. You’ll notice something.

The death music isn’t playing.

The purpose of the code we hijacked was “Change the music” according to all.log.
Considering how we have overwritten it, it doesn’t exist anymore. This counts as ​broken
despite how trivial it may seem. Our patch isn’t complete if we don’t r​ ecover the original
code​. You basically do this by putting the replaced original code in your new routine:

ORG $00F60A ; The address to be hijacked


autoclean JSL Hijack ; Jump to our custom code
NOP ; Overwrite the leftover byte with an
; opcode which does literally nothing

freecode

Hijack:
LDA #$09
STA $1DFB
RTL ; Always use an RTL after a JSL

Patch this to your ROM and get Mario killed again. You’ll notice the death music will start
playing again. All is well. Now for our final step - removing Mario’s coins upon his death.

Visit SMWCentral’s RAM map, and search for “coin count”. You’ll end up at $7E0DBF. To
remove all the player’s coins, you need to set this address to 00. Include this in our patch:

34
ORG $00F60A ; The address to be hijacked
autoclean JSL Hijack ; Jump to our custom code
NOP ; Overwrite the leftover byte with an
; opcode which does literally nothing

freecode

Hijack:
STZ $0DBF ; Store #$00 to $0DBF
LDA #$09
STA $1DFB
RTL ; Always use an RTL after a JSL

Patch this to SMW and try it out. Gather some coins and then get yourself killed. You’ll
notice that the moment you die, your coin amount becomes 0. We have accomplished our
goal - our patch is ready!

When hijacking code, keep an eye on the register widths of the A, X and Y registers. If you
hijack a code which runs in 16-bit A mode for example, and you start writing code with
8-bit parameters (e.g. LDA #$03), the game will crash. Set the register width to 8-bit at the
beginning, then before you return, set it back to 16-bit mode.

Preventing crashes and testing


In a sense, throughout the entire process, we’ve already put effort in preventing a crash. We
found a suitable hijack location. We made sure to deal with leftover bytes with NOP. There’s
no way this patch can crash in any circumstance ever. However, you should also test for
unintended side effects.

What if, for example, you die as L​ uigi​? Or you die after falling in a pit? The former you’ll be
confident about, because the RAM address we store to is about the ​current player​ so it
naturally includes Luigi. But the pit-death is a different type of death which has no animation
or anything. You could test that.

Just a little spoiler though. The coins still reset to 0, because whether you die by the enemy
or by a bottomless pit, the death music always plays, so our call to the custom code will
always execute.

Testing is an important step when you’re creating a patch. Think outside the box when
testing. Think of very unlikely yet relevant scenarios and try them out.

And that’s it! This is how very simple patches are written. In the event of getting crashes and
not knowing what causes them though, you’ll have to use...

35
Chapter 9: Debugging and crashes
This is where ASM gets really fun and challenging. As you’re learning ASM, you will very
often deal with crashes or unintentional side effects. Earlier in the tutorial we used the
snes9x debugger. It’s a decent albeit incomplete debugger. A better debugger is
“bsnes-plus”. If you search “bsnes-plus” on Google you’ll get a few results. The download
from romhacking.net is a good option. You could also visit this link:
https://github.com/devinacker/bsnes-plus/releases

“Debugging” is the process of finding and fixing bugs and errors in the coding. If Super Mario
World crashes at a certain point in the game after applying a patch, debugging can be used
to find out which patch crashes, where and why. In order to debug a ROM, you will need
decent ASM knowledge, so this chapter essentially contains a huge leap in difficulty
compared to the previous chapters.

To get things started, here’s an example patch which is a slightly modified version of our
example patch from the previous chapters. Upon death, the coin counters reset. However, if
you had any coins at all, the background color also turns blood-red (clearly visible in Yoshi’s
Island 1). If you die with 0 coins, nothing happens.

ORG $00F60A ; The address to be hijacked


autoclean JSL Hijack ; Jump to our custom code
NOP

freecode

Hijack:
LDA $0DBF ; If zero coins, skip the next block
BEQ + ; of code.
STZ $0DBF ; Zero out coins
REP #$20 ; 16-bit A to write our color
LDA #$001F ; 1F is pure red
STA $0701 ; (Format: xBBB BBGG GGGR RRRR)
+
LDA #$09
STA $1DFB
RTL ; Always use an RTL after a JSL

If you apply this patch and die without coins, nothing happens. But if you die with coins, the
game crashes:

36
“...Banzai?”

To access the debugger in bsnes-plus, go to Tools -> Debugger …


Meet the debug console:

37
Where is the crash?
​ referably
The best way to trace a crash is by enabling the first option in the Trace block, p
when the crash is about the occur​. The debugger logs even recurring code, making the
log file easily reach hundreds of megabytes in file size in the matter of a few seconds. It’ll be
incredibly verbose.

The logs are stored in the directory where the ROM is located in. It’s a text file roughly
named “*romname*-trace.log” (e.g. smw-trace.log). This log file contains every instruction
executed from the moment you enabled CPU logging, to the point of crash and perhaps
even beyond. Open it in notepad++ or anything else that isn’t regular notepad - regular
notepad is too slow when it comes to handling hundreds of megabytes.

Scroll down until you see something suspicious. Sometimes it also helps to ctrl+f “BRK”,
because in a crash 95% of the times a BRK is involved. And surely enough, when you die
while possessing coins…

There indeed is a BRK.

38
Breakpoints
Breakpoints define addresses where the debugger should ‘pause’, so you can inspect the
following instructions after that breakpoint piece by piece. There are three kind of
breakpoints: Break upon ​read (R), execute (X)​ or ​write (W)​. The following window is
accessed by Tools > Breakpoint Editor:

In above image, I’ve already inputted the location of our hijack: the debugger will break at
$00F60A. The source is S-CPU. That’s where our ASM always executes. Either that, or
SA-1 (but this tutorial doesn’t cover SA-1). Set the breakpoint, run the game (you can close
this window, it’s saved automatically) and die. See what happens:

A line of code automatically appeared: a JSL. That’s our hijack!

If you press “Step” (Step Into), you execute the next opcode. “Over” (Step Over) is a similar
button, but it doesn’t branch off into jumps or branches. Handy if you know the branch or the

39
subroutine isn’t faulty - it saves time. The code still executes you just don’t see it in the
logger. Finally, “Out” (Step Out) jumps out of the current subroutine. Same concept as
above, the code still executes but didn’t get logged. Press “Step” a few times to see what
happens:

40
Why does it crash?
Looking at above CPU log, the reason for the crash is instantly visible (for the experienced
people) - A is still 16-bit when it’s supposed to be 8-bit again at some point. Therefore, the
CPU started reading bogus instructions we never wrote and ended up with a crash. To fix
this crash, a ​SEP #$20​ is required between S ​ TA $0701​ and the ​+​ sign. The result:

One step closer to the ultimate creepypasta hack, Banzai!

There are many reasons why code can crash and above example was the most simple
reason. Code can crash in the following situations:

- Wrong register width. 16-bit AXY instead of 8-bit and vice versa.
- Is notable for almost always incurring a BRK in the logged code as well as
executing bogus opcodes you never even wrote
- An RTS is used for a JSL. Likewise, an RTL is used for a JSR
- Is notable for executing bogus or BRK instructions after a return in the logged
code
- Pushing x amount of bytes but not pulling them again before a return
- (This is because the return opcodes also use the stack to find its return
address, and unpulled values on the stack mess with that)

41
- Is notable for executing bogus or BRK instructions after a return in the logged
code
- An infinite loop
- Is notable for having a huge wall of repeating code in the logged code. It also
simply freezes the game, rather than crashing it in a spectacular way.

This chapter covered the most important parts of debugging - tracing crashes and stepping
through code. Fixing crashes is up to your ASM knowledge - there’s no “formula” to fixing
crashes.

The debugger has many other features such as logging SA-1 code, a bunch of PPU viewing
tools, a memory editor, etc.and it would take a long time to explain them all (it’d turn into a
whole readme for the debugger). As your ASM and SNES knowledge grows, you’ll be able
to understand the remaining features.

42
Chapter 10: Frequently Asked ASM Questions
Learning ASM is a matter of reading documentation and practice combined. However, some
questions may come up which are simply undocumented. This is especially true for SMW
hacking. It’s not like there’s a document out there which covers every single question one
may have in SMW hacking.

This chapter intends to fill that gap. The following questions (and answers) are straight from
SMWCentral’s stickied ASM & Help thread. I took the liberty to go through most of the pages
to find questions which made me think “Hey, that’s a good one. People new to ASM will
wonder about that and it hasn’t been covered by this tutorial nor the previous one”. Without
further ado…

Custom Sprites
Q: I want to use the X register in my custom sprites, but I heard I’m not allowed to change it
because it’s the current sprite index or something. What now?
A: You can actually modify the X register, but make sure you preserve it beforehand by
using PHX and restore it with PLX. As an alternative, you could also use “LDX $15E9” which
is the current sprite index.

Custom Blocks
Q: How can I make a custom block have a custom “acts like” setting programmatically?
A: Use the following opcode snippet wherever you want inside the custom block:

LDA #$30 ; act like block 130 [Cement Block]


STA $1693 ;
LDY #$01 ; this is the high byte

This can be placed anywhere you want it to be, as long as Y remains unmodified throughout
the rest of the routine (using PHY/PLY might be useful in such situations).

Patch-related
Q: How can I, for example, make a chargin’ chuck’s health variable rather than the static
3-hit on a per-level basis?
A: Because the comparison is done in the ROM (a CMP #$03), you will need to h ​ ijack the
comparison in order to make it read from a RAM address rather than that immediate value
#$03. A good patch to help you out with per-level basis stuff is p4plus2’s UberASM patch.

43
General Coding
Q: When I try to input my ASM file into a command-line tool, it says the file does not exist
even though the file is right there!
A: Chances are your file extensions are hidden by default, and there’s a high chance your
file is actually named filename​.asm.txt​. Look up a tutorial on how to display file extensions
by default. This will allow you to edit the file extensions of your files directly by simply
renaming them.

Q: Am I able to do multiple comparisons with a single opcode, e.g. “CMP #$03,#$04,#$54”?


A: No, such an opcode notation does not exist. You’ll have to do the comparisons one by
one.

Q: What about multiple addresses, can I do something like the following to check multiple
addresses?

LDA $0DB4
LDA $0DB5
LDA $0DBE
CMP #$04
BCC ...

A: No, the LDA instructions would just repeatedly overwrite A, ultimately making CMP only
check the final LDA’s value.

Q: Are “flags” like a true/false thing, where 0 is false and 1 is true?


A: Yes. In most cases it actually goes like “​0 = false”​ and “​anything else = true”​, though.

Q: Can ASM modify ROM?


A: No, ROM stands for ​read-only memory​, meaning it cannot be changed by runtime ASM.

​ egative?
Q: Is there a way to make a hex value n
A: Yes, in cases where negative values are accepted (e.g. speed values), negative values
are the values between #$80-#$FF (inclusive). The higher the value, the smaller the
negative number. #$FF would be -1.

Q: What is the difference between RTS/RTL (besides the obvious final-letter difference)?
A: RTS is used to return from JSR subroutines, while RTLs are used to return from JSL
subroutines.

Q: When I looked through the list of SNES opcodes, I found a rather peculiar one: STP.
Apparently it stands for “Stop the clock” and all it does is freeze the game. Is it supposed to
be… useless like that?
A: Yes. Normally you want to stay away from that opcode. While we’re at it, stay away from
BRK #$xx, COP #$xx and WDM #$xx as well.

44
Q: Reading the description of BIT it says the following: “BIT performs AND with the
Accumulator and the operand; however, the flags are affected but the result is not stored”.
What does this mean?
A: BIT acts like an AND, but the result is not stored in the A register. However, the processor
flags are affected (such as the Zero flag).

Q: XBA stands for Exchange B with A, but I’ve never heard of the register “B” before?
A: Yeah, register B is simply the name for the high byte of the accumulator. This opcode
solely means that the low and high bytes of the accumulator are swapped.

Q: I need a timer in my ASM hack to, for example, increase the player coins by 1 every 5
seconds. Is there some sort of a timer I can set?
A: Your best bet is using the frame counter ($7E0014). The frame counter increases every
frame, and one second equals 60 frames. Considering the maximum frame counter value is
$FF, that means you can work with 256 frames (if you count frame 0 also).

Q: How do I generate a random number?


A: You call the subroutine at $01ACF9 with a JSL, then read the results from RAM $7E148D
and $7E148E.

Q: How can I check if a certain overworld event is passed? The RAM address is in a weird
format!
A: To save time, here’s a short code snippet you can use:

!EventToCheck = #$34

CheckEvent:
LDA !EventToCheck
PHA
AND #$07
TAX
PLA
LSR
LSR
LSR
TAY
LDA $1F02,Y
AND MaskValues,x
BNE EventSet:
;stuff to do if event was not set
RTS

EventSet:
;stuff to do if event was set
RTS

MaskValues: db $80,$40,$20,$10,$08,$04,$02,$01

45
Q: How can I disable the player’s controls so that I can write Mario’s movements
programmatically?
A: Write #$0B to $7E0071. To enable controls again write $00 to $7E0071

Q: So how does BEQ and BNE branch without a CMP?


A: This is relevant to the zero flag.

LDA #$00
BEQ whatever ;BEQ will branch
LDA #$01 ;01 or higher
BEQ whatever ;BEQ won't brach

If you ever came up with an unanswered question in your process of learning ASM, feel free
to contact me with a suggestion for this FAQ.

46
Chapter 11: Final notes
In the end, this tutorial teaches you the basic skills to get you started on ASM hacking Super
Mario World. If you get stuck somewhere in the process of learning ASM, never hesitate to
ask for help. There’s nothing embarrassing about it. Everyone had to start off somewhere,
after all.

This ASM tutorial is a “very short”, Super Mario World-specific version. I have written a more
lengthy, in-depth ASM tutorial called “​Assembly for the SNES​”, also available at Super
Mario World Central. That tutorial covers almost every single (type of) opcode and explains
what happens behind-the-scenes as well whilst certain opcodes are running. I highly
recommend you give it a read once you grasp the basics of ASM in this tutorial. It’s always
exciting to learn something new. The amount of ASM covered in this tutorial is just the tip of
the iceberg.

Also, my in-depth ASM tutorial has a chapter with many useful links to various ASM
documents and websites. Definitely check it out.

If you have any suggestions or improvements for this tutorial, feel free to contact me.

End of ASM tutorial.

47

Вам также может понравиться