Are you familiar with all the previous lessons yet? If so, that's great.. with all of that knowledge, you can do a lot of cool stuff - but you can always get better at ASM and make awesome stuff by learning more. So here's Part 2 of the tutorial, which for now only explains a bit of intermediate stuff. Don't worry, everything will be explained easily.
Lesson 6: Indexing
So... you know you can write to the accumulator or the 'A' register, right? Well, guess what? There's an X and Y register you can work with, too. Isn't that cool? You have 3 registers you can use... and the X and Y registers work similar to the A register.
LDA LDX LDY STA STX STY CMP CPX CPY INC INX INY DEC DEX DEY
Those are the X and Y equivalents of using some opcodes. All of them work in the same way as A, for example:
LDA #$38 STA $00
LDX #$38 STX $00
Same goes for Y. You just need to know that
DEY work a bit differently.
When using A, you can directly increment a RAM Address. Like this:
INC $0DBF. However, you can't do
INX $0DBF or
INY $0DBF as these commands do not exist. The best method would be to use it like this:
LDX $0DBF INX STX $0DBF
And the same goes for the Y register. And note that branching commands work in the same way as A for X and Y, for example:
LDY $00 ;if $00 = 1 .. CPY #$01 BEQ Return ; Return. LDY #$01 STY $19 ; I'm sure you know what this does. Return: RTL
...but now you might be thinking? What's the use of the X and Y registers if there's already a 'A' one? And also, with that
INX code I made above, you also might be thinking that it just wastes more bytes. Well, those aren't the main uses of X and Y. Instead, X and Y are mainly used for tables and indexing.
What's a table? It's basically a label which a bunch of bytes. It's set up like this:
Table: db $04,$08,$0B,$1C
You write a label/symbol normally, but afterwards, you write a "
db" followed by a bunch of values separated by a comma. The values don't need a # in a table. Here's a table with 6 bytes:
Table: db $3F,$1A,$2C,$01,$08,$24 ; 00 01 02 03 04 05
It's 6 bytes long, but you normally regard the first byte to be 00. Also note that in a table, you can write whatever values you want.. I just wrote those random ones. Just note that they need to be set up in the proper method:
Symbol name in front of the table, dollar sign in front of each number (no # sign or spaces, ever), comma separating each number and no comma at the end. If you have set up a table like this:
Table: db #$08, #$06, #$08
That's incorrect. You don't ever need to put a # or a space.
Anyway, so you've made a table? Big deal, where's the X and Y in it? Well, now that we've made one.. we actually need to utilize it.
To use it, you need to load a RAM Address indexed by X or Y. Suppose we want to make the star timer have a different timer depending on Mario's power-up state. That would be simple to create using a table.
From the beginning of our tutorial,
00 - Small Mario 01 - Big Mario 02 - Caped Mario 03 - Fiery Mario
$1490 controls the star timer.
#$FF is the maximum value it can be. With your current knowledge, a code like this will be made:
LDA $19 CMP #$00 BEQ TimerA ; for small mario CMP #$01 BEQ TimerB ; for big mario CMP #$02 BEQ TimerC ; for caped mario CMP #$03 BEQ TimerD RTL TimerA: LDA #$1C ; load a timer BRA SetTimer ; and branch to set it.. for small mario. TimerB: LDA #$28 BRA SetTimer ; for big Mario TimerC: LDA #$38 BRA SetTimer ; for caped Mario TimerD: LDA #$56 SetTimer ; we don't need a BRA here, because the label and code is already present. STA $1490 ; store to star timer. RTL ; and return.
..long isn't it? Not only that, but it's a garbage code which can be optimized using tables and indexing.
Firstly, we need to load Mario's power-up status in the X or Y register. I will be using X for mine:
Now, we need to load a table, which is dependent on "X", which is Mario's power-up. Loading a table is simple:
Table: db $08,$1C,$24,$48 LDX $19 LDA Table
But the thing is, we want to load a different value depending on
$19, so we use different bytes in a table. Remember how I said the first byte is 00, second is 01, and so on? The bytes in our table can control our star timers:
Table: db $08,$1C,$24,$48 ; 00 01 02 03
If we're using
$19 here (I'll explain how you make the table use
$19 later), we're loading
#$08 for small Mario,
#$1C for Big Mario,
#$24 for Caped Mario and
#$48 for Fire Mario. If there was a fifth value, we could extend our table by more byte for that value, but there isn't, so we'll leave this table being 04 bytes long.
Now that we've set up the table, we need to make it use
$19. I've already loaded that into X in my code, and I can make the table easily use X with this code:
Table: db $08,$1C,$24,$48 LDX $19 LDA Table,x ; load the table, indexed by power-up ($19)
See how this works? To get a table indexed by X, you use a comma after the table name, followed by an X. Now this will make the table use values loaded into X, which is the power-up state.
Using Y is similar, you just need to replace
LDA Table,x with
LDA Table,y and make sure that you've loaded
$19 into Y, not X!
So... now that we've made the table use values from
$19 by indexing it with X, we need to store those values to the star timer,
$1490. That's very simple; just add an
STA $1490 after loading the table. The table does the rest of the work:
Table: db $08,$1C,$24,$48 LDX $19 LDA Table,x ; load the table, indexed by power-up ($19) STA $1490 ; get the different values into $1490. RTL ; return, of course.
See how we've made a 30ish line code into a simple 4 line one with a table? That's how awesome the X and Y registers are. So.. what this does, explained once more:
We're actually loading a table loaded with "star timers" for the current power-up,
$19. The first value is for small Mario (
#$08), the second for Big Mario (
#$1C)... and so on. Now, since there's only 4 possible power-up states (00, 01, 02, and 03), our table only has to have 4 bytes. That is, 4 values are only needed in this table.
Suppose you have an address with more values though. Imagine there being 8 power-up states in SMW (00-07). If
$19 would be a value for a hammer power-up, we could extended our table with one more value (byte) long.
Table: db $08,$1C,$24,$48,$60
As you can see, I added a
$60 which would be the value for power-up
#$04. If you're indexing with a RAM Address with more values, you use the amount of values that RAM Address has. Complicated? Look:
$0DBF is the coin counter. How many values do you expect it to have? 100 right? It starts with 00, and goes all the way to 99, and then resets back. If you're using this for your star timers, your table HAS to be 100 bytes long. Yes, that means it will have a 100 values, tables can be that big. Hell, the level table would be even larger than that
So we load our table into "A", indexed by "X". What does this mean? We're loading a table with values for the RAM Address in the X register. If
$19 is in the X register, and we index it with our table (
LDA Table,x), it makes the table pick out numbers from values in that RAM Address. Simple, really.
X will contain the RAM Address the table will refer to, so it's important that this is the right RAM Address.
Y works the same way. Here's the code in case you want to use the Y register:
Table: db $08,$1C,$24,$48 LDY $19 LDA Table,y ; load the table, indexed by power-up ($19) STA $1490 ; get the different values into $1490. RTL ; return, of course.
Now... some important tips to know:
- A table's values are always in hex, not decimal.
- A value in a table cannot go beyond #$FF (unless you're in 16-bit mode, will be explained later).
- A table MUST be set up properly.
- You can use X or Y, whichever one you wish. Just remember that if you're already loading something into X, it's obviously better to use Y instead..
- Make sure that the table has the right amount of values! If you have a table with values for level numbers, it will be huge, around 100 bytes or so! Make sure each and every value is there! If you're using sprites, your table should have
#$FFvalues for each sprite!.
- If you are using the X register, don't load a table with Y. That's silly.
What if you want to add or subtract different values? It works in the same way:
Table: db $01,$02,$03,$04 ;00 01 02 03 LDX $19 ; get Mario's power-up .. into X. LDA $0DBF ; his coins into A. SEC SBC Table,x ; subtract different values depending on X (power-up) STA $0DBF ; and store new value. RTL ; return.
It may look a bit harder, but really it's just as simple as
LDA Table,x. It makes sense that when subtracting, you need to be indexing the
SBC rather than the
When adding, you'll do pretty much the same thing. Replace the
SEC SBC Table,x with
CLC ADC Table,x instead.
Heh... there's a lot of other stuff you can do with indexing, such as
STA $RAM,x. Now... let's learn some more about X and Y.
What if you wanted to load RAM
$04. You could do that through indexing as well.
LDX #$03 LDA $04,x ; this will load RAM $07.
You might not find it useful unless you make some sort of a loop. Here's a code that draws tile
LDX #$02 ; X will contain the value to add. Loop: LDA #$FC ; load tile FC. STA $0EF9,x ; note that this will store #$FC to $0EF9 + 2 because it's indexed with X.. so it's actually storing to $0EFB. DEX ; but now that we've done DEX, it will make X = #$01. CPX #$00 ; if x isn't 00.. loop BNE Loop ; this makes it go back to the loop if X isn't 00. RTL
It may seem a bit confusing.. but this is what we're doing:
First, we're loading 02 into X. Then we load a tile (
#$FC) into A, and store it a RAM Address (
$0EF9). However, since we're storing to
$0EF9,x we are actually storing it to
$0EF9 + 2 which is
$0EFB. So instead of storing to
$0EF9, we are storing
But then, we add a
DEX, meaning decrease X by one. So X is now 01. Since X isn't 00 yet, it'll go back to the loop. What do you realize now? It's actually storing tile
$0EF9 + 1 this time! That's because we added a
Finally, it'll loop until it stores to
$0EF9. Once X becomes 00, the code will end. Simple, I guess? And by the way,
$0EFB are places on the status bar which you can write to.
So... yeah, there's a ton of things you can do with indexing, although at times it can be confusing. Just be sure that you know what you're doing.
Here's some other nice commands you can use:
TAX -> This copies the RAM Address in A to X. TAY -> This copies the RAM Address in A to Y TXY -> This copies the RAM Address in X to Y TYX -> This copies the RAM Address in Y to X. TYA -> This copies the RAM Address in Y to A. TXA -> This copies the RAM Address in X to A.
Should be pretty obvious... (Note: they don't move, but copy the RAM Address). Anyway,
LDA $010A TAX
$010A to X. It's important to know that it won't copy the value of
$010A into X, but
$010A itself. Although you can directly do
LDX $010A here, it's best to use
TAY) when loading a 24 bit RAM Address into X or Y (6 digit address,
Since you can't directly do:
LDX $long LDY $long
$long means a RAM Address with 6 digits, like
$7F8620 for example).
These commands don't exist, but
LDA $long does. So if you wanted to get a 24-bit address into X, load a 24-bit address into A, and
LDA $long TAX
This will now give you:
Another use could be that you might want to "preserve a RAM Address". If you want to load something else, you can always
TAX it, and
TXA when you want it back. Oh.. and there's a small drawback with the Y register.. you cannot do:
This command does not exist, although
LDA $RAM,x (RAM is a 24-bit address) does. In case you want to do that,
TYX is your friend.
Lesson 7: Bitwise Operations
By looking at the lesson name, you'll might think "oh no, bitwise, what the hell is that it's going to be hard". Well, this stuff isn't really a hard at all. Bits are an important part in assembly, and you can use them for many purposes.
One for example, would be this. Imagine you make 3 power-ups - Dash boots, double jump, and Ice Mario. You do this by setting 3 different values of a RAM Address:
$0DC6 00 - No power-up. 01 - Dash boots 02 - Double jump. 03 - Ice Mario.
Yeah, this doesn't really exist in SMW, but imagine with all of the current ASM knowledge you have now, you made these power-ups.
Now, there's a power-up that sets
$0DC6 (your power-up RAM) to 01 - the Dash boots. When you collect them, imagine you have it.
Then, there's power-up 2, the double jump boots (yeah, just imagine it). If you have the dash boots, and then collect the double jump boots, you'll have the double jump ability, but you lose the dash boots ability. Why? Because
$0DC6 is no longer set to 01 but 02 now.. this means that you can't have your dash ability AND your double jump ability together, unless you merge both codes together. But then, that'll totally make the power-up ruin the fun out of playing, because the player can easily use 2 power-ups at once.
To overcome this problem, you can use bits. What exactly are bits? We'll learn them, including binary..
You might know, that in computers, everything is stored as 0s and 1s. This number system is called binary, it only has 0s and 1s, nothing else.
..yeah, you might be thinking now that what the heck do these 0s and 1s have to do with SMW hacking. We'll come to that, in a minute.
Anyway, let's take a random hex value and convert it to binary.
#$02 will become
00000010. There's 8 digits in this binary address. Each of these are called 'bits'. So, you'll realize that there a 8 bits in a byte.
In case you don't know what a byte is yet, it's a value like
#$02. (I'm pretty sure you will by now..)
Yeah, so, 8 bits in a bytes - each consisting of either a 0 or a 1. There's a fine difference between a 0 and a 1 here:
0 - Bit clear. 1 - Bit set.
But what exactly does clear and set mean? Well, clear means that this bit is empty, and it's not being used. 01 means that the bit is being used and is not empty.
Think of it as this 0 as the 'off flag' and 1 as the 'on flag'.
So that should be clear by now.. 8 bits in a byte and each bit can either be an 0 or 1, 0 being a clear bit and 1 being a set bit. Now here's something important to know, the bits are considered in reverse order, for example you might think
00101010. The first bit is the last one, the last bit is the first one. Splitting this up from the beginning, we have:
0 - Bit 8. 0 - Bit 7. 1 - Bit 6. 0 - Bit 5. 1 - Bit 4. 0 - Bit 3. 1 - Bit 2. 0 - Bit 1.
This is very important to know, don't get confused by it. But how do you make a bit set? And what's so special if it's set? Well, let's carry on.
01 - Bit 1 set. 02 - Bit 2 set. 04 - Bit 3 set. 08 - Bit 4 set. 10 - Bit 5 set. 20 - Bit 6 set. 40 - Bit 7 set. 80 - Bit 8 set.
The first digits are hexadecimal values, and come in a rather similar pattern here:
Bit 1 = 2^0 = 1 = $1 Bit 2 = 2^1 = 2 = $2 Bit 3 = 2^2 = 4 = $4 Bit 4 = 2^3 = 8 = $8 Bit 5 = 2^4 = 16 = $10 Bit 6 = 2^5 = 32 = $20 Bit 7 = 2^6 = 64 = $40 Bit 8 = 2^7 = 128 = $80
Yeah, they use the powers of 2, so that should be simple to understand what each value for each bit is (thanks RPG Hacker!). If you don't know about powers, then well... you just need to learn some maths :<
If you convert
#$01 to binary, you'll realize that the first bit (final digit) is 1. If you convert 40 to binary, you'll get a 1 on the 7th bit (second digit). You can add the hexadecimal values to set multiple bits. For example, if you 20 and 10, you'll get 30. Since 20 sets bit 6 and 10 sets bit 5, the hexadecimal value 30 will set bit 5 and 6.
Another example, let's add up all of these values together. 80 + 40 + 20 + 10 + 08 + 04 + 02 + 01 adds up to FF. Since all of these bits are set and you're adding them all up,
#$FF will set ALL bits. Yeah, see for yourself. FF becomes:
Now I'll tell you about the purpose of these bits in SMW. Remember the power-up issue I was talking about earlier in this lesson? You want dash boots and double jump boots to work together. An excellent method of doing this would be using bits; basically in your RAM Address, you can set a bit. Here, we can set 2 different bits, one bit for the dash boots and one for the double jump boots.
We can overcome the issue by setting 2 bits in our RAM Address, one for the dash boots and one for the double jump boots. Let's say we just the first 2 bits, for these two power-ups. When Mario has the dash boots, we should set the first bit. It'll become like this:
00000001 ; What are doing is "setting this bit" (turning it on) when Mario has the dash boots.
When Mario only has the double jump boots, the RAM Address will become like this:
00000010 ; Set the 2nd bit of our RAM Address.
When he has both and when he has none of them are shown respectively:
00000011 ; both bits are set. This means that Mario has the dash boots and the double jump boots. 00000000 ; no bits are set. This means that Mario doesn't have any of the power-ups.
You might already realize this, but when Mario has the dash boots AND gets the double jump boots, he'll have both power-ups and won't lose the one he got first. Why? Because one bit doesn't affect the other. Unless you use the same bit for both power-ups (which of course, is stupid), you can make Mario retain both abilities without having to worry about one affecting the other. This is really where a bit comes to be useful. Furthermore, you won't need to waste 2 RAM Addresses if you had the idea of doing that, to save RAM bytes.
...Now binary is pretty much over. Now, suppose you actually want to code a power-up which uses the 'bit system'. You'd need to know the following things:
- How to check if a bit is set or clear.
- How to 'activate' a bit.
- How to clear a bit.
- There are no opcodes for doing all of the above for the X and Y registers. You'd need to transfer X or Y to A, and when you're done with the following, you can transfer them back to X. Remember the
TXYetc. opcodes, right?
For the first part, when Mario has the power-up, you'd only execute your power-up's code when he has the power-up of course. That's where the opcode AND becomes useful.
AND can be used for two purposes - one is to check if a bit is clear or set, and it can be used to clear a bit.
AND work? Pretty straightforward, really. Firstly, we'll talk about how
AND can check bits.
All you need to do is load your address into the 'A', and then use the
AND opcode followed by the bit you want to check, and then perform a branching command (either
Hard? Not at all. Here's a code that will check if bit 1 is set in RAM Address
LDA $1F2C ; first, load our RAM Address. It must be into A, not X or Y! AND #$01 ; bit 1 is #$01. I am using the AND opcode, followed by the bit to check. BEQ NotSet ; branch to not set if this bit is not set. ; When a bit is set, BEQ will branch. BNE will do the opposite; ; BNE here will branch if the bit is not 0, meaning it'll branch when it's 1.
That's all you need to do when checking if a bit is set. Load into A, then use
AND followed by the bit to check for. Then there's either
BNE which you have to use:
BEQ branches when the bit is 0. BNE branches when the bit is 1.
To check if bit 8 was set, what would I have to do? Replace the
What if you wanted to branch when 2 bits are set? That's as simple as adding both hexadecimal values and branching:
LDA $1F2C AND #$30 ; this will branch if either bit 5 or 6 is set (because they are 10 and 20, so I added them together). BNE BitsSet
To branch if all bits are clear, I would need to replace the
BEQ (and possibly the label, to avoid confusion) and the
AND #$30 with
Clearing a bit with
AND is quite similar. Firstly, instead of using a branching command, you store again to that address. BUT, you must NOT work from 00, but instead from FF. For example, you might think this would clear bit 4:
LDA $1F2C AND #$08 ; clear bit 4. STA $1F2C
This is incorrect. Although BIT 4 is 08, subtract it from FF instead:
LDA $1F2C AND #$F7 ; clear bit 4 (FF - 08 = F7). STA $1F2C
The reason you subtract from FF is because bits work the other way around. As bit 1 is the last digit, you'll want to go backwards to reach another bit, not forward.
Again, you can clear multiple bits by subtracting their hexadecimal values together and using
AND for that. There's also an efficient method for clearing bits, but we'll come to that in a bit.
Now, here's something you might be interested in knowing in. Checking a bit could also be done with
CMP, don't you think? Well, yes, that's partly correct, and partly wrong.
A good example for this would be the controller data. The controller data uses bits, as you can see:
7E:0015 1 byte I/O Controller data 1 - 01=Right, 02=Left, 04=Down, 08=Up, 10=Start, 20=Select, 40=Y and X, 80=B and A Bit 1 = Right Bit 2 = Left Bit 3 = Down
etc.. And yes, bit 7 (value
#$40) both the Y and X buttons.
So anyway, as I said.. you could use
AND to check for a bit as well. But that can cause some problems. Suppose you want to check if Mario is pressing up. With
CMP, you would use:
LDA $15 ; load controller data CMP #$08 ; compare to value 08.. BEQ PressingUp ; if Mario is pressing up, branch.
This works, but what if you press up and another button? Suppose you're pressing up and the right button. This code won't branch then, and will be useless. Can you guess why?
If you haven't figured, when pressing up, the controller data sets bit 4 and becomes 08. The code right now, will work good if Mario presses up. However, when pressing right, bit 1 will also be set. Remember that I said when 2 bits are set, they're added up? This means that the controller RAM Address, $15, will no longer be 08, but 09 (since bit 4 and bit 1 are set, meaning 08 + 01). Now you'll realize, the code won't work, because the value isn't 08 anymore... this is where
AND is better than
AND ignores every other bit in a byte, except the one being checked for. If I check for the up button here, I don't have to worry about the other keys being pressed because
AND only concentrates on one bit.
When you press up, bit 4 will be set. This means it becomes 01. So, to check if Mario is pressing up, here's a better code:
LDA $15 ; load controller data AND #$08 ; check for bit 4... BNE Bit3Set ; Also known as Mario is pressing up. This command branches if Mario is pressing up, because the bit will be 1.
Here's a bad code. I'll leave it to you to figure out why:
LDA $15 AND #$04 BEQ PressingDown
Tip: Never ever get confused between
AND is way better to use when checking for bits.
Find AND to be a simple opcode? Let's move onto to our second and third ones -
TRB - Used to clear a bit of a RAM Address. TSB - Used to set a bit of a RAM Address.
To set a bit, you can do a rather cheap way. Assuming that
$0DC6 is 00 at the beginning of this code:
LDA $0DC6 CLC ADC #$04; this will add 4, and make $0DC6 become #$04. This means that we're setting bit 3. STA $0DC6
Yes, you can do that. But why not use
LDA #$04 ; load the bit needed to set.. TSB $0DC6 ; and store it to that RAM Address.
This is how
TSB works - first you load the bit (or bits, if you want to add 2 bits together), and then
TSB it to the RAM Address you want the bit set on. The code above does the exact same thing as the
ADC one, but it's better of course.
TRB - clearing a bit. It works in the exact same way as
LDA #$04 ; load the bit needed to clear.. TRB $0DC6 ; and clear it from that RAM Address.
Likewise, you can clear multiple bits by adding both bits together and loading that.
You probably realize that unlike the
AND function, you work from 00 instead of FF. This is how
TSB work. For instance:
LDA #$04 ; load the bit needed to clear.. TRB $0DC6 ; and clear it from that RAM Address.
LDA $0DC6 AND #$FB STA $0DC6
It works the other way around.
Here's an important note to know now - you know like I talked about long addresses, ones with 6 digits right?
TRB won't work on those addresses. This code fails:
LDA #$40 TSB $7007FF
If you want to clear out a bit with a 24-bit address, you'll need to use the
AND method, I posted before. The usage is:
LDA $ADDRESS AND #$xx ; bit(s) to clear ; Remember to subtract from FF instead. STA $ADDRESS
What about setting a bit to a 24-bit address though?
TSB won't work. Time to learn one that works -
ORA, first of all, can set a bit (or bits) from a RAM Address. The usage is similar to
LDA $ADDRESS ORA #$xx ; bit(s) to set STA $ADDRESS
AND can both set/clear 2/4 digit and 6 digit addresses as well.
TRB are used for those, however, as they can optimize and shorten the code. Another difference is that and
AND starts from FF) while
TSB start from 00.
ORA can set a RAM Address, but it can also be used for another purpose. It can be used to check if any of a bunch of address are zero or not zero:
LDA $187A ; Yoshi? ... ($187A is the Yoshi flag. If zero, Mario is not on Yoshi). ORA $73 ; Ducking? ($73 is the ducking flag. Once again, zero means not ducking) CMP #$00 ; this line can be considered irrelevant. ; If you want to compare to zero, you can directly use BEQ. ; I just wrote it here so that it's clear to understand. BEQ EitherOfThemIsZero
The way this works is that we first load a RAM Address normally and can compare it directly.
LDA $187A BEQ NotOnYoshi ; As I said before, a CMP #$00 is irrelevant. If using BNE, this branches if it's anything other than 0.
In case you want to check if another address is zero, instead of loading and comparing to 00 again, you could add an
ORA in between the
LDA $187A and the
BEQ NotOnYoshi as I did above. Here's a code that will make Mario big only if he's in water or on Yoshi:
LDA $187A ; load first address with an LDA.. this one loads the Yoshi flag (00 = not on Yoshi) ORA $75 ; water flag. Is Mario in water? 01 = yes. BNE OnYoshiOrInWater ; branch if Mario is in water, or on Yoshi. (CMP #$00 = irrelevant.) RTL ; just end the code if not. OnYoshiOrInWater: LDA #$01 ; the code that STA $19 ; makes Mario big.
You can use
ORA as many times as you want here, just note that you always load the first one into A (
LDA $RAM). So remember, for
ORA, the following applies:
BEQ (label) - Branch if any of the addresses are 0. BNE (label) - Branch if any of the address are NOT 0 (can be anything other than 0.)
So far, you should understand
ORA. Now, we'll learn about one more -
EOR is an opcode that can invert or 'flip' bits around. What it does is get the value into A, compares it to something, and applies some rules to get a result. The rules apply in binary, so first convert the hexadecimal value to binary:
For example, let's
EOR #$FF on the value 00.
LDA #$00 EOR #$FF in binary are: 00000000 11111111
Using some rules, we generate a result. The rules are as follows:
1 EOR 0 = 1 - If the first digit is 1, and the second is 0, the result for this digit will be 1. 0 EOR 1 = 1 - If the first digit is 0, and the second is 1, the result for this digit will be 1. 1 EOR 1 = 0 - If both digits are 1, the result becomes 0. (Similar) 0 EOR 0 = 0 - If both digits are 0, the result becomes 0. (Similar)
So let's apply these rules with our values:
00000010 ; 02 11111111 ; FF 11111101 ; FD
This concludes that:
LDA #$02 EOR #$FF
Now makes the value in A FD. But, all we're doing is inverting values? How is that going to help us in SMW hacking? Well, here's a good example of how it's used. Mario's X speed in stored in $7B. When going right:
00 = Not moving. 7F = Maximum.
7F is the maximum speed you can have right, but the fastest you can be in the game is around 28 (at full speed.) But anyway, when going left:
00 = Not moving. 80 = Maximum. (This goes backwards, meaning FF is the slowest going left, and it goes back to 80)
Again, maximum speed when going left is roughly around D7. Now what if you want to code something that increase Mario's speed? Like dash boots. You could just set a new X speed for Mario - let's say
LDA #$34 STA $7B
There's a problem with this code. As you can see, this will make Mario faster when he's right, when he's going left, it's going to cause problems. Why? because 80 is the maximum speed when can have when going left.
To overcome this problem, we can flip Mario's X speed around when he goes left, so that it shares the same values as right.
LDA $76 ; if $76 is = 01. BNE GoingRight ; Mario is going right. LDA $7B ; load the X speed. EOR #$FF ; flip it around STA $7B ; and store GoingRight: LDA #$34 ; set the X speed, which also works when going left. STA $7B
Let me explain how this works. These are the equivalents for Mario's speed when going left and right.
RIGHT LEFT 00 00 01 FF 02 FE 03 FD 04 FC 05 FB ... ...etc.
As you can see, when going right it increases, and when going left it decreases. If I
EOR #$FF, I'll get 01. If I
EOR #$FE, I'l get 02. So what I've done is gotten Mario's X speed, and made his "left speed" have the equivalents of his "right speed". But why
EOR #$FF? It's because Mario's X speed starts from 0. If I did for example,
EOR #$FB instead, it would ignore converting the first few values.
Here's another example. Here is how you might imagine how pausing works - If Mario is pressing START, it'll pause. Then it checks if he press START again, and unpauses if he does:
Code: LDA $15 AND #$10 ; if start is being pressed.. (if the bit is set) BNE Pause RTL Pause: LDA #$01 ; set the pause flag on.. STA $13D4 ; so the game pauses. LDA $15 AND #$10 ; if Mario is pressing start again.. BNE UnPause ; then branch to UnPause. RTL UnPause: STZ $13D4 ; clear the pause flag again. BRA Code ; repeat our code.
In fact, this is a very inefficient code. By using
EOR, we can easily do the above code in a better way:
LDA $15 AND #$10 ; check normally if pressing start.. BEQ Return ; return if not pressing start (bit clear) LDA $13D4 ; load the pause flag.. EOR #$01 ; flip it STA $13D4 ; and store the new pause flag status Return: RTL ; Return. Note that I don't need to add a return after STA $13D4 because there already is one here.
How this works is that it normally checks if you're pressing START. When doing so,
EOR will make the value 00 if it's 01, and vice versa (01 if it's 00).
EOR is basically "flipping" the pause flag from 0 to 1 when you press start in this code.
Let's apply the binary formula again. Let's suppose we're not pausing (00), and we
EOR #$01 to the pause flag.
00000000 ; 0 00000001 ; 1 Result = 00000001; 1
This means that the pause flag will be 1, meaning it's on. Now imagine the pause flag is on, and we
EOR #$01 again.
00000001 ; 1 00000001 ; 1 Result = 00000000 ; 0
This means that the pause flag is off again. So we've made that garbage code into a really simple one by effectively using
That's all you'll basically need when working with bits. There are other bitwise operations, such as
BIT, but those ones you won't find very useful when working with code, so you should just learn these ones instead. They are pretty handy at times.
Let's move onto Lesson 8, The Stack
Lesson 8: The Stack - Preserving values and RAM
The Stack is really useful when you want to "preserve" your values or addresses, and then "pull" them later. Let's give an example:
You have a stack of 9 books, one book on top of the other:
-- Book 1 -- Book 2 -- ......... -- Book 9.
Now I want to add 10 more books here:
-- Book 10 -- Book 11 -- ........... -- Book 19.
Okay, so there's 15 books on our shelf. The problem: there's a pile of 19 books there, and I want to take out the 11th one. It's obviously going to be hard as I'm going to have to count to 11 and then take out that book, isn't it?
A better way would be to "preserve" book 11 instead. With preserving it, just imagine that it's kept in another area temporarily, so that I can easily take the book when I want to.
The Stack works the same way - it's like a bookshelf. If you want to preserve your values and load them back later (pull them back), you can easily so, as many times as you want to.
To work with pushing/pulling, you load your value or address, and them pull it back when needed. You can push with the A, X, and Y register.
- The A register will preserve a value/RAM Address in A.
- The X register will preserve a value/RAM Address in X.
- The Y regster will preserve a value/RAM Address in Y.
PLAare used to preserve and pull a value in A. PHA pushes it, and PLA pulls it back.
For X and Y,
PLY are used.
Here's another thing to note. Even with you load A with something, and then preserve it, it's still in the accumulator! That means you can still use it for stuff, like comparing, without having to worry if it got preserved. The following example will show you what I meant, with pushing a RAM Address into the accumulator.
LDA $0660 ; Load some RAM Address (Imagine this is some custom address which we're using). PHA ; preserve it for later. LDA #10 STA $0DBE ; do some other stuff. (Guess what this does ) PLA STA $0DBF ; .. store whatever we preserved into $0DBF ($0660 was preserved, now the value in that will be in $0DBF)
Of course, we could have easily done this:
LDA #10 ; .. make the lives counter STA $0DBE ; become 10. (We could also use #$0A instead of #10). LDA $0660 ; .. store $0660 STA $0DBF ; .. to $0DBF ; This means that the value in $0660 will now be in $0DBF, the lives counter. ; If it was 10, we'll be storing 10 into $0DBF)
That code is even shorter! So what was the point of preserving there?
Well, that was just a lame example to show how preserving works. Don't try to preserve it in useless cases, like the example above because it will just waste more space (bytes) in your code and does the exact same thing. I'll give you a good example later.
Now, here's a really important thing to remember. Let's imagine the book example I stated before.
You preserved book 11 elsewhere right? Let's say book 13 is also important. So I put that one in our "reserve area" too. The stack works by putting stuff on top of each other. So book 13 will be right above book 11. Let's imagine I've pushed values 11 and 13:
LDA #11 PHA ; We pushed 11 first, so that comes before value 13. LDA #13 PHA -- Book 13 -- Book 11
For a small while, I need to take out book 11. If I take out the first book, I'll take out (
PLA) the top one, right? But that's book 13, not the one I want. So I'll need to pull (
PLA) again to get book 11. What do you notice?
As a general rule, the number of times you push must equal to the number of times you pull to load the proper value you want.
What if, suppose, I pull again? There's nothing in the stack anymore, so if I pull, it won't get any book? Well, what happens worse in SMW coding is that your game will crash! Make sure that the pushes and pulls are equally balanced, and at the end of your code, nothing is on the stack anymore!
- Push once, and you must pull only once.
- Push 5 times, and you must pull twice times.
- Push 30 times, and you must pull thirty times. (Of course, no one needs to push that much )
What about in comparing though? Here's an example:
LDA $0DBF PHA CMP #$05 ; compare $0DBF to 05 (remember, even though I pushed it, it's still loaded into the accumulator) BCC Return ; return if I don't have 5 coins. PLA STA $0EF9 ; store $0DBF to the ($0EF9 is an area on the status bar, to be specific, the 'M' on Mario. RTL Return: PLA ; .. pull back and return directly. RTL
Note that there's 2 pulls, but only one push. Can you guess why? At first, I always push
$0DBF at the beginning of code. Then I compare it to 05. If it isn't 05, I'll just directly end my code. However,
$0DBF is still on the stack. Remember that I said before ending your code, nothing must be on the stack? That's why I pull this one separately and end my code.
$0DBF is 5 or more, it's still on the stack. So I pull it back and store it to
$0EF9. I must put an RTL after the
STA $0EF9 because if I didn't, it would pull again (The return label has another
PLA) and thus crash the game. Remember this. Usually when comparing, the number of pulls and pushes can vary, but this shouldn't happen in the actual coding. Make sure that the pushes and pulls in your code are correct.
Here's a real example, where preserving is almost always used:
A common use is in sprites. Sprites use the X Register for tables/indexing (I know we haven't yet covered how to make sprites, but just note that when making them, they use the X Register to work). This means that if you're loading/storing/whatever with the X register which does NOT modify the sprite tables (they're just tables and stuff used with indexing in sprites), then your sprite won't function. Alternatively, you could use the Y register, right? But what if you're already using that too?
A better idea would be to preserve the X register. We can preserve the sprite tables, and pull them back after we're done with our work with the X register. Just assume this in a sprite code (note: don't try to understand it, because it'll be confusing, so just look at the code):
PHX ; Push the X register LDX $19 LDA Table,x ; Imagine I'm using some sort of table. ; This table doesn't have anything to do with the sprite main tables, it's for my own code. STA $0660 ; Store it somewhere. PLX RTL ; Pull back X and terminate.
As I said, imagine that this code is in a sprite. The sprite tables ALWAYS use the X register while a sprite is running, so if I modify X in someway with preserving it, the sprite probably won't appear on the screen and function properly.
So what are the sprite tables anyway? Well, they're used for various purposes, such as as the sprite's X/Y speed, the number of times to go through a loop, etc. You'll have a better understand of this when we reach the sprite-creating tutorial later.
Now remember, I can't really give good examples of pushing/pulling (aside from the sprite one), but normally you can easily tell yourself when it's appropriate to push and pull a value or address. However, do note that when working with custom codes, it's always a good idea to preserve the contents of the accumulator RIGHT before your code starts, and pull it back at the very end. This sometimes is useful when hacking a routine. (We'll come to that later.)
Don't try doing something stupid though, like this:
LDX $0DBF ; load $0DBF into X. PHA ; Push A. LDA $0DBE STA $0EF9 ; store $0DBE over here. PLX RTL
Can you guess what's wrong? Firstly after loading
$0DBF into X, I preserve A, the contents of the accumulator. I didn't load anything into A, but whatever is in there is still preserved so there is wrong with that.
Then I just do some random other code; store
$0EF9. Again, there's nothing wrong with that.
Thirdly, I pull back X and return. Here's the problem: I actually pushed A, not X! So I'm pulling something from X which didn't even get pushed, and I'm also pushing A without even pulling back a value. I should just changed the
Here's another short sample code before I move on to something about
JSLs and the stack (don't worry, it's nothing too difficult):
LDA #$04 PHA LDA #$06 PHA LDA #$08 STA $0EFA PLA STA $0EFB PLA STA $0EFC
Can you guess which gets stored where? This will help us:
06 <- I pushed this one after 04, so it goes on top of it. 04 <- I pushed this one first, so it goes at the bottom.
PLA, get the first value back (the top one) and stores it into
$0EFB. The second value, in then loaded into
06 -> $0EFB. 04 -> $0EFC.
(Edit: The 06 will be pulled off the stack first, since it on the top. This originally said 04 -> $0EFB, and 06 -> $0EFC, but based on the code it refers to, 06 should be in $0EFB)
08 -> $0EFA (08 was never in the stack. Stored immediately after loading ) 06 -> $OEFB (06 was pushed to stack last. Pulled off first because it was on the top.) 04 -> $0EFC (04 was pushed to stack first. Pulled off last because it was on bottom.)
So the last value you preserve is always loaded when you do your first pull. Okay, let's learn something quite important. This code will fail:
PHA JSR Code LDA #$04 STA $0EF9 RTL Code: PLA RTS
Nothing looks wrong in it though, right? There's the correct number of pushes/pulls (01), and the subroutine ends with an RTS like it's supposed to do (
The problem here, is the
JSL will also cause a problem here, so imagine it's for both of them.) ..Wait, how is the
JSR causing the code to crash?
These commands use the Stack to store what part of the code to jump back to. So, if you Push to the Stack, then
JSR, then Pull again before the
JSR code is finished, well, your code will make itself into some delicious scrambled eggs.
JSR commands store what part of the code you jump to (in this case, Code subroutine) to the stack. It stores 2 bytes (imagine they're like values..) to the stack, like this:
-- Byte 1 -- -- Byte 2
JSR commands will also use these to jump back to the main code.
In my code, I pull as soon as the subroutine starts:
-- Byte 3 <- The value (or address) we preserved. -- Byte 2 ;\ -- Byte 1 ;/ These 2 are for the JSR.
Now, after the
PLA, there's an
RTS (but we can also imagine there's some other code before the
RTS). So at the end of the subroutine, the
JSR will use the first two bytes on the stack (the first top two) to return to the main code again. Now can you guess what's wrong here?
JSR is supposed to use byte 1 and byte 2, but it's actually using byte 3 and byte 2! Byte 3 was for our code and the
JSR is using it! This means your game will crash because the
JSR doesn't know where to return to!
Always keep that in mind when using pushing/pulling commands (also applies for X and Y of course, as they also share the stack with A) and the
JSR commands. You'll need to keep this in mind when using them, because you don't want to your game to crash!
A good code for the above example could be:
PHA JSR Code LDA #$04 ;\ STA $0EF9 ;/ Random code. I am obsessed with $0EF9-$0EFC, aren't I? PLA RTL Code: RTS
It's always the best idea to push your value(s), and then pull it (them) back after the subroutine finishes. For example:
PHX JSR Code PLX RTL Code: RTS ; You can put whatever code you want here.
Well, that's all you need to know for the stack. These commands use them:
PHA - Push A. (It can push a value, or an address) PHX - Push X. (It can push a value, or an address) PHY - Push Y. (It can push a value, or an address) PLA - Pull A. (It can pull a value, or an address) PLX - Pull X. (It can pull a value, or an address) PLY - Pull Y. (It can pull a value, or an address) JSL - This pushes 2 bytes to the stack and remember the code to jump/return to. It pushes 2 bytes on the stack. RTL - This uses the stack in the sense that it must be in a subroutine which is accessed through a JSL. JSR - This pushes 3 bytes to the stack and remembers the code to jump/return to. RTS - We covered this in lesson 4 as well. This must be used in a subroutine accessed through a JSR.
JSR work the same way, as I said in lesson 4, but they use
Now, you can tell why: they push different bytes to and pull different bytes from the stack, and if you mix a
RTS, you'll get the wrong number of bytes and thus, the code fails.
Of course, the stack is also used elsewhere for more complexed stuff, but we won't be going through them in this tutorial as they are obviously advanced and not really needed.
ASM Tutorial Part 2: Lost? Start from Part 1.