Part 2: Some Basic Commands


Lesson 2-1: Loading, Storing, and Returning

Lesson 2-2: Arithmetic Commands

Lesson 2-3: Transferring

Lesson 2-4: Branching and Conditional Statements

Lesson 2-5: Jumping

*Lab 1: BTSD Blocks

Lesson 2-6: Indexing

Lesson 2-7: Bitwise Operations


Lesson 2-1: Loading, Storing, and Returning

Opcodes: LDA, LDX, LDY, STA, STX, STY, STZ, RTS, RTL

Addressing modes: imm8/16, dp, abs, long

Progress:

0/99

0/15

ADC AND ASL BCC BCS BEQ BIT BMI BNE BPL BRA BRK BRL BVC BVS CLC CLD CLI CLV CMP COP CPX CPY DEC DEX DEY EOR INC INX INY JML JMP JSL JSR LDA LDX LDY LSR MVN MVP NOP ORA PEA PEI PER PHA PHB PHD PHK PHP PHX PHY PLA PLB PLD PLP PLX PLY REP ROL ROR RTI RTL RTS SBC SEC SED SEI SEP STA STP STX STY STZ TAX TAY TCD TCS TDC TRB TSB TSC TSX TXA TXS TXY TYA TYX WAI WDM XBA XCE

imm8/16 dp abs long dp,x abs,x abs,y long,x (dp) [dp] (dp),y [dp],y (dp,x) sr,s (sr,s),y


p2.1.0

The first opcode we'll learn is...*drum roll*...LDA. This stands for Load Data into Accumulator. Now, there are a few different ways to load said data. This line of code:


LDA #$02


loads the hexadecimal value 02 into A. Whatever A was before this, it is now equal to 02. Note the #$, though; you can't just LDA 02. The $ sign indicates that it is a hexadecimal value we are loading, and the # sign tells us that it is a specific number, not a RAM address. If it were this instead:


LDA $02


...you would be loading the value of RAM address $02 into A. Now, LDA $02 is perfectly valid. Just remember that LDA $02 loads the value of RAM address $02 into A. So, if $02 happens to be equal to 44, then LDA $02 loads 44 into A. If $02 = 2C, then LDA $02 loads 2C into A. Using our box analogy, “LDA #$02” would be kind of like saying “I want to put a box of rice onto the cart”, and “LDA $02” would be kind of like saying “I want to put whatever item is on the second shelf onto the cart”. By the way, you can load decimal and binary values as well. LDA #$30, for example, loads 30 in hexadecimal, but LDA #30 (without the $) loads 30 in decimal. So, to load a decimal value, simply remove the $. For binary, replace the $ with %. LDA #%10010111, for example, is equivalent to LDA #$97. I never found much use for either, but some people like to use them. I suppose loading decimal values could be nice when working with multiples of 5, such as when coding blocks that give you a certain number of coins, and loading binary values could be nice when working with bitwise commands, covered in section 2-7. So, really, although the common misconception is that a $ sign indicates a RAM address, while #$ is a number, the reality is that the $ sign merely marks the operand as being hexadecimal. I wouldn't recommend trying to load RAM addresses in decimal or binary, though...”LDA 40” might work (it would be the same thing as LDA $28), but it would do nothing but cause confusion.


p2.1.1

Now, there are a couple different addressing modes for LDA. So far, you've seen #$xx and $xx. The “#$xx” mode, where you load a specific number, is called immediate mode, shortened to imm8/16. The 8 stands for 8-bit, and the 16...well, we'll worry about that later. (Section 3-4, if you want to know.) The “$xx” mode, the one where you load a number from an 8-bit/1-byte RAM address, is called direct page mode. There are a couple others. You can also load a value from a 16-bit, or 2-byte, RAM address. For example, LDA $0DC2 loads the value of the current player's item box. LDA $14AD loads the blue P-switch timer. Now, notice the first of these two examples: $0DC2, not just $DC2. In ASM, addresses and values should always be an even number of digits. In the first part of this, we loaded $02 and #$02, not $2 or #$2. Similarly, we load $0DC2 and not $DC2. Anyway, the addressing mode that loads 16-bit addresses is called absolute mode. The address has 4 digits, whereas in direct page addressing mode, the address had only 2 digits. You can go even further, though; you can even load a 24-bit address. (This would be 3 bytes or 6 digits.) You can, for example, LDA $7F8183. ($7F8183 is unused RAM, by the way.) This is called long addressing mode. The rule for even digits applies here as well; if you happen to be loading from an address like, say, $0530C7, then you need that 0 in front. It isn't $530C7, it's $0530C7.


p2.1.3

Besides LDA, which loads a value into A, there are opcodes for X and Y as well, which are LDX and LDY. You can LDY $14AF, LDX #$84, and many of the other things you can do with LDA...BUT you can't use LDY or LDX for long addresses. If you try to LDY $7F8183, for example, it won't work. There is no long addressing mode for LDY or LDX. You can get the value of a long address into X or Y (see section 2-3), but you can't load it directly.


p2.1.4

So, to summarize:

-LDA loads a value into A.

-LDA #$xx (where xx is a value) loads a specific number. (Example: LDA #$50 loads 50 into A.)

-LDA $xx loads the value of an 8-bit/2-digit/1-byte RAM address. (Example: LDA $1A loads the value of $1A into A.) This is called direct, or direct page, addressing mode.

-LDA $xxxx loads the value of a 16-bit/4-digit/2-byte RAM address. (Example: LDA $13BF loads the value of $13BF into A.) This is called absolute addressing mode.

-LDA $xxxxxx loads the value of a 24-bit/6-digit/3-byte RAM address. (Example: LDA $7FAB10 loads the value of $7FAB10 into A.) This is called long addressing mode.

-Addresses must always have an even number of digits, 2, 4, or 6. If an address starts with a 0, such as $09, $0DBF, or $0F1348, then the initial 0 must be kept there.

-LDX and LDY load values into X or Y rather than A.

-There is, however, no 24-bit addressing mode for LDX or LDY.


p2.1.5

Well, great! You now know three opcodes (LDA, LDX, and LDY) and four addressing modes (immediate, direct page, absolute, and long). Now what? Once you load a value into a register, what do you do with it? Feed it to your dog and see if the dog burps out a Shyguy? Well, not exactly. Once you've loaded a command, in most cases, if you want to actually do anything with it, you'll need to store it. To do this, you use STA, STY, and STX. STA stands for “Store Accumulator” (or something). You can, for example, STA $19 (affect the player's powerup) or STA $0DC2 (affects the item box). An example code that uses STA would be:


LDA #$03

STA $19


See that? This code does two things. First, it loads the value “03” into A. Second, it takes the value in A and puts that into RAM address $19, which is the player powerup. If you have Super Mario World's RAM map open right now, you can figure out that this would make the player have firepower.


p2.1.6

You can STA to almost anything you can LDA from, but there are a few exceptions. For one, there is no immediate addressing mode for STA. You can't STA #$xx. (How would that even work? You're storing A into a value?) You also can't STA to ROM data, although you can load from it. By the way, once you STA to a RAM address, A still has the same value it had before the STA. So, in my example code (LDA #$03 STA $19), after you put the 03 in $19, it doesn't “go away” as far as A is concerned. A is still equal to 03. In fact, you can do more than one STA in a row without having an LDA before each one. You could, for example...


LDA #$03

STA $19

STA $0DC2

STA $13CC


do that. This loads 03 into A, then it puts that value into $19, then $0DC2, and then $13CC. That is, it gives the player firepower ($19 = 03), puts a star in the item box ($0DC2 = 03), and then gives the player 3 coins ($13CC = 03). (I suppose I should mention that if you don't have the RAM map open right now, I'd open it if I were you. It will help quite a bit.) Also, in case you were wondering, yes, you can store X and Y as well. You use STX and STY for that. But beware: just like LDX and LDY, STX and STY are much more limited than STA is. (STA has 14 different addressing modes, while STX and STY have only 3, to put it into perspective.) You can't use STX and STY with long addresses, for one thing. STA $7EC800 exists, for example, but STX $7EC800 does not.


p2.1.7

At this point, you might want to know that there is another command related to storing addresses that you will find useful: STZ. STZ is for “Store Zero”, and when you STZ a RAM address, you set that address equal to 00, no matter what it was before. For instance, without STZ, if you wanted to make the player small, you would do this:


LDA #$00

STA $19


If you wanted to erase the item from the item box, you would do this:


LDA #$00

STA $0DC2


With STZ, however, you can save yourself a bit of code. You can change that first code to:


STZ $19


and you can change the second code to:


STZ $0DC2


. It does the same thing, but it's shorter. Unfortunately, you can't use STZ on long addresses either. If you want to clear $7F9C80 to 00, you can't put STZ $7F9C80; it has to be LDA #$00 STA $7F9C80. Ah, the tragedy of life.


p2.1.8

By now, you almost know enough code to actually make something with ASM. But...BUT...there's one more thing necessary to learn: returning. You can load and store to your heart's content, but it won't do much good if you don't know how to return. To return, you use RTS or RTL. RTS stands for “Return from Subroutine”, and RTL is “Return from Subroutine, Long”. The million-dollar question is, which one should you use? Well, it depends on what it is you're coding. If it's a block for the old Blocktool, use RTS to finish the code. If it's a block for Blocktool Super Deluxe (BTSD), use RTL. If it's the main code in a patch, you usually end with RTL. If it's the INIT or MAIN routine of a sprite, use RTL, and if it's pretty much anything else in a sprite, use RTS. (You'll probably be using RTS more often, unless you decide to make a whole bunch of BTSD blocks and/or patches.) Let's have an example...


LDA #$03

STA $19

RTS


This is now a complete code. Load 03 into A, then store A (which is now 03) into $19, giving the player firepower, and then end the block of code. You don't need to put anything after RTS or RTL, by the way. There is no, for example, RTS #$01 or RTL $1540; these are two opcodes that stand alone.


p2.1.9

All right! You've successfully made it through your first ASM lesson. Let's move on to the second, shall we?.


Lesson 2-2: Arithmetic Commands

Opcodes: INC, DEC, INX, INY, DEX, DEY, ADC, SBC, CLC, SEC, ASL, LSR

Addressing modes: none

Progress:

9/99

4/15

ADC AND ASL BCC BCS BEQ BIT BMI BNE BPL BRA BRK BRL BVC BVS CLC CLD CLI CLV CMP COP CPX CPY DEC DEX DEY EOR INC INX INY JML JMP JSL JSR LDA LDX LDY LSR MVN MVP NOP ORA PEA PEI PER PHA PHB PHD PHK PHP PHX PHY PLA PLB PLD PLP PLX PLY REP ROL ROR RTI RTL RTS SBC SEC SED SEI SEP STA STP STX STY STZ TAX TAY TCD TCS TDC TRB TSB TSC TSX TXA TXS TXY TYA TYX WAI WDM XBA XCE

imm8/16 dp abs long dp,x abs,x abs,y long,x (dp) [dp] (dp),y [dp],y (dp,x) sr,s (sr,s),y


p2.2.0

Now, there are various ways you can affect RAM addresses without actually storing anything to them, and similarly, you can affect the three variables (A, X, and Y) without actually loading something into them per se. One way to do this is to use arithmetic commands. These are opcodes that add, subtract, multiply, and divide. The first one we'll learn is INC. This is short for “increment”; what INC does is simply increase the value of a RAM address or register by 1. So, if we wanted to give the current player an extra life, one way to do it would be:


INC $0DBE


$0DBE holds the current player's lives, so INC $0DBE gives the current player one more life. Now what about the other way around? What if we want to make the player lose a life, but without dying? For that, we use DEC. This code:


DEC $0DBE


would decrease the current player's lives by 1. But wait...what if we wanted to give the player ten more lives? With our current knowledge, you'd think that


INC $0DBE

INC $0DBE

INC $0DBE

INC $0DBE

INC $0DBE

INC $0DBE

INC $0DBE

INC $0DBE

INC $0DBE

INC $0DBE


would be the way to go. However, while that would work, it's not very efficient. There is a much shorter way. For this, we'll need two more opcodes: CLC and ADC. Using CLC and ADC, this same code would be


LDA $0DBE

CLC

ADC #$0A

STA $0DBE


That simply loads the current player's lives into A, adds 10 (10 decimal, which is 0A in hexadecimal, remember) to that vallue, and stores the result back into $0DBE, the RAM for lives. ADC stands for “Add with Carry”; the value you want to add goes directly after the ADC. But notice something else here...CLC. There is nothing after it. And what do you think it does? Wasn't ADC what we use for adding? Well, you could put ADC alone, but it wouldn't be a very good idea. CLC stands for “Clear Carry Flag”, and you almost always need it right before an ADC. What exactly is the carry flag? You don't really need to know yet, although it is covered in lesson 3-4. The carry flag, when used with ADC, determines whether to add the value as-is or to add one to it. If the carry flag was already clear before the ADC, then all is well. ADC #$0A adds 0A. But if the carry flag, by chance, happened to be set before you put the ADC there, then ADC #$0A would actually add, not 0A, but 0B! So ADC really adds the specified value plus the carry flag, and if you don't clear that carry flag before you add something, you could very well end up accidentally adding one more than you mean to. Just remember that CLC should almost ALWAYS be put before ADC.


p2.2.1

Of course, the next question that will come up is, what do we do if we want to give the player negative ten lives (subtract ten lives, make him/her lose ten lives)? You could “DEC $0DBE” ten times, but you've probably figured out that there must be a quicker way. Aaaaannnndd...there is! To subtract a number greater than 1, use SEC and SBC. To make the player lose 10, or x0A (hexadecimal numbers are sometimes preceded by “x” or “0x” to indicate that they are hexadecimal) lives, you would use this code:


LDA $0DBE

SEC

SBC #$0A

STA $0DBE


Looks almost like our previous code, doesn't it? But wait...what the? There's another $#@%! opcode out there in the middle of nowhere! The first, third, and fourth lines look okay: opcode, value/address. But there's that SEC without anything after it.... As you might imagine, the relationship of SEC to SBC is similar to the relationship of CLC to ADC. The difference is that SEC—which stands for “Set Carry Flag”—sets the carry flag instead of clearing it. SBC stands for “Subtract With Carry”. Emphasis on “with carry”...just like ADC, you can get some unwanted results if you use SBC alone. This time, when the carry flag is set, SBC subtracts exactly what you'd expect it to, which, in this case, happens to be 0A. But what if the carry flag is clear before the SBC? What if, just for testing purposes, we put CLC SBC instead? When the carry flag is clear, SBC actually subtracts one more than the specified value. So “CLC SBC #$0A” would subtract 0B instead of 0A. Just remember: ADC is almost always preceded by CLC, and SBC is almost always preceded by SEC. (Although there is that “almost”...we'll worry about that later, in section

3-4.) In case you were wondering, yes, you can ADC or SBC a RAM address as well as a value, just like you can INC or DEC a RAM address. (ADC $05, for example, or SBC $066A, are acceptable.), However, although ADC and SBC work okay with 24-bit addresses, INC and DEC do not. Keep that in mind.


p2.2.2

Oh, I almost forgot to mention something: You can increment or decrement A, X, and Y as well as RAM addresses. If you want to increase the value in A by 1, use either this:


INC A


or this:


INC


What's the difference between INC and INC A? Not much. If you use INC without anything after it, it counts as INC A. The same goes for DEC; either “DEC A” or simply “DEC” works. X and Y don't work quite the same way, but they are equally easy. To increment or decrement X or Y, you use one of these four opcodes:


INX

INY

DEX

DEY


You can probably guess at their meanings. INX = increment X. INY = increment Y. DEX = decrement X. DEY = decrement Y. No, you can't put something like “DEC X” or “INC Y”; ASM doesn't really work that way. By the way, INC A and DEC A are the best way to subtract 1 from a long address, since you can't directly INC or DEC the address. If you want to subtract one from a long address, you can use LDA $insertaddresshere DEC A STA $insertaddresshere. Similarly, if you want to add one to a long address, you can use LDA $whatever INC A STA $whatever.


p2.2.3

Alrighty, then. We have the addition and subtraction down okay...what about the other two basic operations of arithmetic, multiplication and division? Don't worry, these aren't too hard; in fact, they may be easier than addition and subtraction in some cases. For multiplication, use ASL. This stands for “Accumulator Shift Left” (no, notage, sex, and location” or even “American Sign Language”), which likely stems from the base command, multiplying A. ASL multiplies something by 2. You can ASL a RAM address, or you can ASL the accumulator. These codes are all valid:


ASL $09

ASL $0DBE

ASL A

ASL


Notice that last one especially. Just like with INC and DEC, if you use ASL without anything after it, it is the same as ASL A. To divide something, you use LSR. LSR stands for “Logical Shift Right.” Substituting LSR for ASL in our four examples, we get:


LSR $09

LSR $0DBE

LSR A

LSR


Once again, when an LSR occurs by itself, “LSR A” is implied. Just as ASL multiplies something by 2, LSR divides something by 2. You can even string several ASL's or LSR's together. To multiply by 4, for example, use:


ASL

ASL


To divide by 8:


LSR

LSR

LSR


To multiply by 16 (10 in hexadecimal):


ASL

ASL

ASL

ASL


...and so on and so forth. Now, you'll probably ask, what if you want to multiply by something that isn't a power of 2? What if you want to multiply something by 3, 5, or 6, for example? Well...you can multiply a RAM address by 3 with this code:


LDA $00 (we'll use $00 as an example)

ASL A

CLC

ADC $00


A now has the value of $00 times 3. But just try that with a number like, say, 27. Actually, don't. You'll just give yourself a headache, and I didn't include a free bottle of aspirin with this tutorial due to budget cuts. There is a way to multiply or divide by any number you desire, but it is more complicated and much less well-known. You can't do it with just an opcode alone. (Lesson 3-5 covers that, if you're curious.) So you'll usually be multiplying and dividing only by powers of 2. Also, you can't ASL or LSR long addresses, either. If you want to multiply $7007FF by 2, you'd have to use


LDA $7007FF

ASL (A)

STA $7007FF


. Also, ASL and LSR have no opcodes for X and Y. You can't ASL X or LSR Y, for example. To multiply X or Y...well, in Lesson 2-3, all will be revealed.


p2.2.4

In summary:

-INC raises the value of a RAM address by 1; DEC lowers it by 1.

-INC A, INX, and INY increase the value of A, X, and Y by 1; DEC A, DEX, and DEY decrease them.

-ADC and SBC add or subtract a specified value or the value of a RAM address.

-CLC must come directly before ADC, and SEC must come directly before SBC.

-ASL multiplies an address by 2, and LSR divides it by 2.

-ASL and LSR can also be used to multiply or divide A by 2.

-If INC, DEC, ASL, or LSR are used without an address or anything else after them, “INC A”, “DEC A”, “ASL A”, and “LSR A” are assumed.

-You can't use INC, DEC, ASL, or LSR on 24-bit addresses, although you can use ADC and SBC.


Lesson 2-3: Transferring

Opcodes: TAY, TYA, TAX, TXA, TYX, TXY

Addressing modes: none

Progress:

21/99

4/15

ADC AND ASL BCC BCS BEQ BIT BMI BNE BPL BRA BRK BRL BVC BVS CLC CLD CLI CLV CMP COP CPX CPY DEC DEX DEY EOR INC INX INY JML JMP JSL JSR LDA LDX LDY LSR MVN MVP NOP ORA PEA PEI PER PHA PHB PHD PHK PHP PHX PHY PLA PLB PLD PLP PLX PLY REP ROL ROR RTI RTL RTS SBC SEC SED SEI SEP STA STP STX STY STZ TAX TAY TCD TCS TDC TRB TSB TSC TSX TXA TXS TXY TYA TYX WAI WDM XBA XCE

imm8/16 dp abs long dp,x abs,x abs,y long,x (dp) [dp] (dp),y [dp],y (dp,x) sr,s (sr,s),y


p2.3.0

The good news is, this lesson is short and easy. The bad news is...hmm, what was the bad news? Your grandmother just got run over by a Mack truck? Oh, never mind. Well, we only have six opcodes to worry about here, and as luck would have it, they all do practically the same thing...and they all have only one addressing mode.


p2.3.1

Ahem...in ASM, there are opcodes called transfer commands. These are easiest to understand with examples. SO...remember back at the end of paragraph 2.2.3 when I pointed out that there are no opcodes to multiply X or Y? I mean, you can ASL A, and you can LSR A, but you can't do that with X or Y. Wouldn't it be nice if we could just...use A instead or X or Y? Well, in a way, we can! Let's say you have a value in Y that you want to multiply by 2. You can't ASL Y, obviously. You can ASL A, but how can you possibly change Y into A? Easy. You transfer. To multiply Y by 2, do this:


TYA

ASL A

TAY


The command TYA (with nothing after it) takes whatever value is in Y and copies it to A. (TYA: Transfer Y Register to Accumulator.) Similarly, the command TAY (again, nothing after it) takes whatever value is in A and copies it to Y. (TAY: Transfer Accumulator to Y Register.) Besides TYA and TAY, you also have TAX, TXA, TYX, and TXY. As you probably guessed, TAX transfers A to X, TXA transfers X to A, TYX transfers Y to X, and TXY transfers X to Y. There are actually a couple other transfer commands, but they are much less common. The main ones are TAY, TYA, TAX, TXA, TYX, and TXY. Something to note is that transfer commands copy; they don't cut. If you transfer Y to A, for example, the value that was in Y isn't lost. Y still has the same value it did before the TYA.


p2.3.2

See? Easy stuff, right? Well, watch out...the next lesson isn't quite as simple. But don't worry. I'm here to explain.


Lesson 2-4: Branching and Conditional Statements

Opcodes: CMP, CPX, CPY, BEQ, BNE, BCC, BCS, BPL, BMI, BVC, BVS, BRA, BRL

Addressing modes: none

Progress:

27/99

4/15

ADC AND ASL BCC BCS BEQ BIT BMI BNE BPL BRA BRK BRL BVC BVS CLC CLD CLI CLV CMP COP CPX CPY DEC DEX DEY EOR INC INX INY JML JMP JSL JSR LDA LDX LDY LSR MVN MVP NOP ORA PEA PEI PER PHA PHB PHD PHK PHP PHX PHY PLA PLB PLD PLP PLX PLY REP ROL ROR RTI RTL RTS SBC SEC SED SEI SEP STA STP STX STY STZ TAX TAY TCD TCS TDC TRB TSB TSC TSX TXA TXS TXY TYA TYX WAI WDM XBA XCE

imm8/16 dp abs long dp,x abs,x abs,y long,x (dp) [dp] (dp),y [dp],y (dp,x) sr,s (sr,s),y


p2.4.0

Okay, first off, here's a question: What if you want some code to run only when a given condition is true? What if, say, you want a block to give the player 20 coins only if he/she has a cape? Then there's that “subtracting ten lives” code in paragraph 2.2.1. What happens if the player has ten lives or fewer when that code runs? His/her life count would end up at zero...or in the negatives? To deal with situations like these, we need some new opcodes: the compare commands and the branch commands. Let's take our first example and make a code that gives the player 20 coins (that's 14 in hexadecimal) when he/she has a cape, but does nothing otherwise.


LDA $19

CMP #$02

BNE Return

LDA #$14

STA $13CC

Return:

RTS


Let's go through this code line by line. The first line is simply “LDA $19”, which you're already familiar with. This simply loads the player's powerup. Now, the next line...CMP #$02. Any guesses what that does? Well, I'm gonna break the suspense: CMP stands for “Compare”. You put a value after CMP, and it will compare that to whatever is in A. In this case, you're using CMP to check if the powerup status is “cape”. The next opcode here is BNE, which stands for “Branch if Not Equal”. This means that, if $19 is not equal to 02, the code will jump to another spot. But notice what comes after the BNE...it's a word instead of a number. Why BNE Return, not BNE #$04 or something? That's just how branch commands work. The word or words after the BNE—in this case, “Return”—are called a label or symbol. You can use pretty much anything you want as a label. Instead of BNE Return, that could have just as easily been BNE EndCode, BNE NoCoins, BNE Label1...heck, I even used “FlatulentOrangutans” as a label in a code once. However, THIS IS IMPORTANT, you need to make sure that the label actually exists. If I had written the code like this:


LDA $19

CMP #$02

BNE Return

LDA #$14

STA $13CC

RTS


it wouldn't work. The BNE has nowhere to branch to, since the label “Return” doesn't appear at another point in the code. That “Return:” has to be somewhere else in the code, or your game will crash...and in video games, the only time you want “Crash” is when he's Coco's brother. Do you need the colon after the label? Well, depends. Sometimes you don't, but it's always best to put the label there anyway. Some things, such as patches, always require it. (It depends on what you're using to assemble your code...TRASM is the assembler that mikeyk's Sprite Tool uses, and xkas is used for pretty much everything else.) Anyway, in the next line of the code, we see LDA #$14. We know what this does, ditto the next line: STA $13CC. But remember what came before this part of the code: a conditional statement, which in this case is a CMP paired with a BNE. The lines after the BNE but before the label “Return:” will run ONLY if the BNE did not cause the code to branch. That is, if the player had a cape, which means that $19 would equal 02.


p2.4.1

Now let's vary things up a bit. We've made a code that gives the player 20 coins only if he/she has a cape. But let's reverse the effect and make a code that gives the player 20 coins unless he has a cape. (You know what, I'll just use “he” from now on. My protagonist may be female, but Mario and Luigi aren't.) To do this, we only have to change one line, one opcode:


LDA $19

CMP #$02

BEQ Return

LDA #$14

STA $13CC

Return:

RTS


We have changed BNE to BEQ, which stands for “Branch if Equal”. While the conditional statement in the previous code branched if the player did not have a cape, the one in this code branches if the player does have a cape. Another thing I should note is that branch statements like BEQ and BNE do not always have to be preceded by CMP. The following code, for example, is perfectly valid:


LDA $19

BEQ Return

LDA #$14

STA $13CC

Return:

RTS


With no CMP there, the BEQ simply branches if $19 was equal to 00. We could have added a CMP #$00 there, but there would be no need. This code, therefore, would give the player 20 coins unless he is small. (Small Mario: $19=00.) You can do that with BNE, too: replace the BEQ in that code with BNE to get a code that gives the player 20 coins if and only if he is small. (The branch statement skips the give-coins part of $19 is anything other than 00.)


p2.4.2

Now, I must note something: CMP, BEQ, and BNE aren't the only opcodes you can use in conditional statements; there are a couple related opcodes. Take CMP, for example. CMP actually compares the value in A. If I had put LDX $19 or LDY $19 instead of LDA, then none of the above codes would have worked correctly because CMP only compares the value with A, not X or Y, no matter what comes before the CMP. To compare something with X or Y, you use CPX and CPY. What CMP is to LDA, CPX is to LDX. The same goes for CPY and LDY. Also, there are more branch commands out there, 8 to be exact. Six of those come in pairs: one branches if a given condition is true, and the other branches if that same condition is false. The branch command BCC, for example, branches if the loaded value is less than the compared value. In this code:


LDA $19

CMP #$02

BCC Return

LDA #$14

STA $13CC

Return:

RTS


...the BCC makes the code branch if $19 is less than 02. If $19 is either 00 or 01, the code will branch. If it is anything else, the code will not branch. This code, therefore, gives the player 20 coins if he has either a cape or firepower, which correspond to the values 02 and 03 of $19. ($19 never takes on any value other than 00, 01, 02, or 03, and if you tried to make it do so, the game would probably screw up.) Let's change this one more time:


LDA $19

CMP #$02

BCS Return

LDA #$14

STA $13CC

Return:

RTS


BCS is sort of the opposite of BCC. BCS makes the code branch if the compared value is greater than or equal to the loaded value. Can you guess what this code does? Yep, that's right. It gives the player 20 coins if $19=00 or 01, or in other words, if he does not have either a cape or firepower. (By process of elimination and common knowledge of Mario games, that means that he is either small or big without a powerup.) By the way, you'll notice that I didn't mention what BCC and BCS stand for. Okay, they stand for “Branch if Carry Clear” and “Branch if Carry Set”, which can be kind of confusing. That's not really important, though.


p2.4.3

BEQ, BNE, BCC, BCS...what's next? That's only four, which leaves six more. The next branch statement pair that you may find useful is BPL/BMI. These stand for “Branch if Plus” and “Branch if Minus”. BPL and BMI are, oddly enough, rarely used in combination with CMP (or CPX, or CPY), and when they are, they pretty much act the same way as BCC and BCS do. BPL branches if the result of the previous operation was 00-7F (considered to be “positive”), and BMI branches if the result was 80-FF (considered to be negative). I can't give any good examples for BPL and BMI with $19, since $19 is never anything over 03 and therefore is never negative. However:


LDA #$40

BPL Label


You can do that. Since the value you loaded was between 00 and 7F, the BPL causes the code to branch. If you do something like this instead:


LDA #$90

BPL Label


The BPL will not branch because 90 is not between 00 and 7F. Similarly, if you have something to this effect:


LDA #$A8

BMI Label


The branch will be taken. If you do this:


LDA #$74

BMI Label


It won't. One interesting thing you can do with BPL is make loops. These are sections of code that repeat a specified number of times. Of course, you can use BNE, too, but for the purposes of this lesson, let's use BPL.


LDX #$03

Loop:

(insert code here)

DEX

BPL Loop


Whatever code you put in between “Loop:” and “DEX” would run exactly 4 times, unless you inadvertently changed X and didn't restore it during the code. How this works is, before the loop, X starts out at 03. You run the code the first time, and then you decrement X by 1. X is now 02. However, 02 is still positive, so the BPL makes the code jump back to the beginning of the loop. (Yes, you can branch backward.) The loop runs a second time, and X is decremented again, this time to 01. But guess what, 01 is still positive, so the BPL jumps back again. The loop runs a third time, and X is decremented again. X is now equal to 00. (Pay attention; this is the good part.) Since 00 is indeed within the range 00-7F, the BPL branches once more, and the loop runs a fourth time. X is decremented again...but wait! What do you get when you subtract one from zero? Well, negative one in most math. In ASM, however, the value simply wraps around. X is now equal to FF. Then we come to the BPL...but wait! FF isn't between 00 and 7F. X is no longer positive, so the BPL does not branch, and as a result, the code does not loop again.


p2.4.4

Coming up next will be two of the most useless opcodes in the entire list. We have one more pair to discuss, which is BVC/BVS. These stand for “Branch if Overflow Clear” and “Branch if Overflow Set”. Yes, I know that the word “overflow” does not start with V. Please file your complaint during office hours. What is overflow, you ask? Well, aside from BIT, which you haven't even learned about yet, the only, the ONLY opcodes that even affect overflow are ADC and SBC. Overflow happens when you add or subtract two numbers and end up with a number that would be outside the range -128 to +127 (decimal). For example, C8 plus A0 is 68, but if you treated these values as being signed numbers, then it would be -56+-96=-152. But you can't go beyond -128, so it wraps around and becomes +104, or 68 in hexadecimal. Similarly, if you start out with 84 and then subtract, say, 07, that would be like taking -124 and subtracting 7 to get -131. It's kind of difficult to understand...yeah, I know. Sorry. At least it's something relatively unnecessary. If you look through all.log (Super Mario World's complete source code; very useful document, even if it is a big honkin' 7500+ KB text file), you'll see a couple BVC's and BVS's, but 99% of the time, these two opcodes are about as useful as an air conditioner in the Antarctic. If you can find a good solid use for BVC and BVS, then you should be teaching me ASM. The final two branch commands, BRA and BRL, are much more useful. Well, BRA is, anyway. BRA branches all the time, no matter what you do before it. You don't need CMP, LDA, or anything. BRL does the same thing, but it has a longer range. BRL, though, is kind of useless as well; although its function is a heck of a lot more useful than that of BVC and BVS, the opcode itself isn't, because there is another opcode called JMP that does almost exactly the same thing. In fact, what a perfect transition from this lesson to the next, which is another easy one. To summarize, if not the lesson, at least the opcodes, though:


CMP: Compares the value loaded in A with the value following the CMP.

CPX: Compares the value loaded in X with the value following the CMP.

CPY: Compares the value loaded in Y with the value following the CMP.

BEQ: Branches to a specified label if the loaded value is equal to the compared value.

BNE: Branches to a specified label if the loaded value is not equal to the compared value.

BCC: Branches to a specified label if the loaded value is less than the compared value.

BCS: Branches to a specified label if the loaded value is greater than or equal to the compared value.

BPL: Branches if the loaded value is 00-7F.

BMI: Branches if the loaded value is 80-FF.

BVC: Branches if the code did not just add or subtract two numbers to produce a result that, if you converted the hexadecimal numbers to signed decimal numbers, would be less than -128 or greater than 127, but no one really gives a darn.

BVS: Branches if the code did just add or subtract two numbers to produce a result that, if you converted the hexadecimal numbers to signed decimal numbers, would be less than -128 or greater than 127. Big flippin' deal.

BRA: Branches all the time, no matter what happened beforehand.

BRL: Branches all the time, but has a longer range than BRA. Redundant, as you'll see in the next lesson.


Lesson 2-5: Jumping

Opcodes: JMP, JML, JSR, JSL

Addressing modes: none

Progress:

40/99

4/15

ADC AND ASL BCC BCS BEQ BIT BMI BNE BPL BRA BRK BRL BVC BVS CLC CLD CLI CLV CMP COP CPX CPY DEC DEX DEY EOR INC INX INY JML JMP JSL JSR LDA LDX LDY LSR MVN MVP NOP ORA PEA PEI PER PHA PHB PHD PHK PHP PHX PHY PLA PLB PLD PLP PLX PLY REP ROL ROR RTI RTL RTS SBC SEC SED SEI SEP STA STP STX STY STZ TAX TAY TCD TCS TDC TRB TSB TSC TSX TXA TXS TXY TYA TYX WAI WDM XBA XCE

imm8/16 dp abs long dp,x abs,x abs,y long,x (dp) [dp] (dp),y [dp],y (dp,x) sr,s (sr,s),y


p2.5.0

Guess what? This lesson is probably almost as easy as Lesson 2-3, maybe even more so. We only have four opcodes to worry about this time. To start...JMP. JMP is similar to BRA, which, if you remember from the last lesson, “always branches”. JMP pretty much does the same thing; it jumps to another part of the code specified with a label. However, with JMP, you can also use a 16-bit address. “JMP Continue”, “JMP Label01”, and “JMP $8430” are all valid. JMP can jump to any point within a ROM bank (i.e. within a range of 0x8000 bytes), but it cannot go outside a bank. If we want to jump to a place that is not within the current bank, we use JML. You can JML to a label, or you can JML to a 24-bit address. “JML MainCode” works, as does “JML $028008”. (JMP and JML, if you haven't guessed, stand for “Jump” and “Jump Long”.)


p2.5.1

Besides JMP and JML, there are two more jump instructions that are actually more useful than the first two. These are JSR and JSL. JSR and JSL are similar to JMP and JML, but there is one important difference. When you JMP/JML somewhere, it's almost as if you'd never jumped at all. The RTS/RTL at the end terminates your code entirely. When you JSR or JSL, the code ends up at the point right after the JSR/JSL itself. So, for example, take these two codes (with lines numbered for easier recognition)...


1. LDA #$01

2. STA $19

3. JMP Label

4. INC $0DBE

5. RTS


6. Label:

7. LDA #$10

8. STA $1490

9. RTS


and


1. LDA #$01

2. STA $19

3. JSR Label

4. INC $0DBE

5. RTS


6. Label:

7. LDA #$10

8. STA $1490

9. RTS


. The first code would run the lines 1 and 2, run line 3 and jump to line 6, run lines 7 and 8, and then end at line 9. The second code, on the other hand, would run lines 1 and 2, run line 3 and jump to line 6, run lines 7 and 8, run line 9, and then jump back to line 4 and end at line 5. See the difference? In the first code, lines 4 and 5 are never even run because of the jump. But since the second part of the second code jumps back, the first part of it continues and finishes. JSR and JSL can come in handy, especially in sprites. JMP and JML, on the other hand...well, you can't make a BTSD block without JMP, and that's exactly what we'll do next...in your first lab!


*see imamelia's ASM tutorial – Lab 1.html for lab*


Lesson 2-6: Indexing

Opcodes: none

Addressing modes: dp,x abs,x long,x abs,y

Progress:

44/99

4/15

ADC AND ASL BCC BCS BEQ BIT BMI BNE BPL BRA BRK BRL BVC BVS CLC CLD CLI CLV CMP COP CPX CPY DEC DEX DEY EOR INC INX INY JML JMP JSL JSR LDA LDX LDY LSR MVN MVP NOP ORA PEA PEI PER PHA PHB PHD PHK PHP PHX PHY PLA PLB PLD PLP PLX PLY REP ROL ROR RTI RTL RTS SBC SEC SED SEI SEP STA STP STX STY STZ TAX TAY TCD TCS TDC TRB TSB TSC TSX TXA TXS TXY TYA TYX WAI WDM XBA XCE

imm8/16 dp abs long dp,x abs,x abs,y long,x (dp) [dp] (dp),y [dp],y (dp,x) sr,s (sr,s),y


p2.6.0

We now come to one of the most important lessons in this tutorial. You won't learn any new opcodes in this lesson, but you will learn some new addressing modes. Indexing is extremely useful; in fact, it is vital for coding sprites. If you've ever looked through a sprite's code, you've probably seen some RAM addresses with “,x” or “,y” tacked onto them, like $14C8,x. X and Y, combined with some numbers...is this ASM or algebra class?


p2.6.1

Indexing...what basically happens when you index, instead of your opcode affecting a specific RAM address, it affects a RAM address a certain distance from where you specify depending on the value of X or Y. Confusing? Don't worry. We have examples, and we will use them. Let's say we want to index...$0695 by X. We'll go ahead and use LDA for this.


LDA $0695,x


That is the proper way to write an indexed RAM address. You put the RAM address, then a comma (no space), and then an X or Y right afterward (again, no space). I'm pretty sure it doesn't matter if the X or Y is uppercase or lowercase, although the convention is usually lowercase. LDA $0695 always loads $0695, no matter what the value of X or Y is. But LDA $0695,x...? In this case, the RAM address that is loaded depends on the value of X at the moment that you load it. If X = 00, then LDA $0695,x just loads $0695. No problem. But if X = 01, then LDA $0695,x will actually load from $0696. If X = 02, then LDA $0695,x actually loads from $0697. If X = 03, then LDA $0695,x loads from $0698. See that? The value of the register you're indexing by is, in a way, added to the base RAM address before determining which address to load. Now, since we're indexing by X here, Y has no effect on this. Y could be equal to 00, 01, 04, 18, 8C, FF, anything, and it wouldn't affect “$0695,x”, because we're indexing this by X and only by X. Of course, we could certainly index it by Y if we wanted to. LDA $0695,y would be pretty much the same thing as LDA $0695,x, except that the RAM address that would be loaded from would depend on the value of Y rather than X. If Y = 00, LDA $0695,y just loads from $0695. If Y = 01, LDA $0695,y loads from $0696, and so on and so forth.


p2.6.2

You may want to ask at this point, what is the point in this? If X = 02, LDX $0695,x loads from $0697. Okay, so why not just load $0697 in the first place? The simple answer is that when you index, you may not—in fact, you rarely will—know the exact value of X or Y. Besides, indexing is a lot quicker than doing it another way, as you will see. Imagine if we did this:


CPY #$00

BEQ Load0

CPY #$01

BEQ Load1

CPY #$02

BEQ Load2

CPY #$03

BEQ Load3



Load0:

LDA $0695

BRA StoreValue


Load1:

LDA $0696

BRA StoreValue


Load2:

LDA $0697

BRA StoreValue


Load3:

LDA $0698


StoreValue:


Ugly, right? You think that would be a pain with four values, imagine doing it with, say, 60. That's why—well, one reason, anyway—we have indexing. As for the other reason...well, now we come to some practical applications. Let's make a teleport block that teleports you to a specific level, and we'll use indexing to do so. Okay, well...what do we know about teleporting in Super Mario World? If you've looked at any levels in Lunar Magic (and i certainly hope you have), then you may have noticed that you can press F1 to display the screen exits in a level. So, great, right? We'll change the screen exit of whichever screen the player is in. Take out your handy-dandy RAM map and go to $7E:19B8. Voilá—screen exit stuffses. But, uh-oh...we have a problem. $19B8 isn't a single RAM address...it's 32 of them. Why? Easy. Remember, most levels have screens numbered from 00 to 1F. If you do the conversion correctly, that makes 32 total screens in decimal. That explains why we need 32 bytes instead of 1. So how do we know which screen that is? Heck, that could be any number from 00-1F. If we store to, say, $19C0, which would represent screen 08, then our block would only work if the player happens to be in screen 08 when he touches the block. Blergh. But all is not lost. It's not immediately obvious from the RAM map, but $95 holds the current screen number. What do we do with this? That's where the indexing comes in. We want to load $19B8 plus whatever screen the player is in, right? $19B8 = screen 00, $19B9 = screen 01, $19BA = screen 02, and so on. SO...let's do this, using level C2 as an example:


LDA #$C2

LDY $95

STA $19B8,y


If we were just storing to $19B8, this code would be only two lines long. But since we're indexing, we need something else...something to set our index. We start the code by loading our screen exit value, C2. Nothing weird there, right? “LDA #$C2” is something we've seen before, or similar to it in any case. But now that we have the right value in A, we need to figure out where it goes. We do this by loading the current screen into Y. Now look at the STA and how it's formatted...STA $19B8,y. $19B8,y. The “,y” afterward tells us that the address is an indexed one. We're taking C2 and storing it to $19B8 plus whatever the value of Y is. So, if the player is in screen 00, then $95 = 00 and Y = 00. $19B8 + 00 = $19B8, so the C2 goes into $19B8. If the player is in screen 01, then $95 = 01 and Y = 01. $19B8 + 01 = $19B9, so the C2 goes into $19B9. if the player is in screen 06, then $95 = 06 and Y = 06. $19B8 + 05 = $19BE, so the C2 goes into $19BE. See how that works? By the way, if you saw this code, it would more likely be set up like this:


LDY $95

LDA #$C2

STA $19B8,y


Notice that Y is loaded first in this case. Either way works. (Also, we technically only set half of the screen exit...the rest is determined by $19D8, another 32-byte section of RAM that would be indexed in the same way. But this was, after all, an example.)


p2.6.3

Let's try another example. We'll use a label this time! Let's make a block that makes the payer invincible! But let's add an extra twist. We'll make the invincibility last for different lengths of time depending on what the player's powerup is. We can use a table for this. But let's use X instead of Y this time, shall we?


db $42


JMP MarioBelow : JMP MarioAbove : JMP MarioSide : JMP SpriteV : JMP SpriteH : JMP MarioCape : JMP MarioFireBall: JMP TopCorner : JMP HeadInside : JMP BodyInside


MarioSide:

MarioBelow:

MarioAbove:


LDX $19

LDA InvincibilityTimer,x

STA $1490


SpriteV:

SpriteH:

MarioCape:

MarioFireball:

TopCorner:

HeadInside:

BodyInside:


RTL


InvincibilityTimer:

db $FF,$7F,$3F,$1F


When Mario touches this block from the side, below, or above, the code first loads the player powerup, then it loads a value depending on what the powerup is, and then it sets the invincibility timer to that. We'll go through this code bit by bit, but notice a couple of things. First of all, in the previous code, we stored to an indexed address. In this code, on the other hand, we're loading an indexed address. Also, the indexed address was just a plain old RAM address in that code. This code uses a label...and after the label, there are four bytes at the bottom. By now, I should explain that an indexed address is usually called a table. For example, $19B8,x is a screen exit table. Now let's DIVE INTO THE CODE! First, we load the powerup, which, if you recall, should never be any value other than 00, 01, 02, or 03. Next, we load “InvincibilityTimer,x”. What is “InvincibilityTimer,x”? It's that little thing at the bottom, the part that says:


InvincibilityTimer:

db $FF,$7F,$3F,$1F


The “db” just means that these are raw bytes of ROM, not opcodes or anything like that. (It's “dcb” in TRASM format, but since this is a BTSD block, it's xkas format, therefore db.) To visualize this better, let's number the bytes of the “InvincibilityTimer:” table:


FF = byte 00

7F = byte 01

3F = byte 02

1F = byte 03


If the player is small, then $19 = 00, so:


FF = byte 00

7F = byte 01

3F = byte 02

1F = byte 03


...we load FF. If the player is big, then $19 = 01, so:


FF = byte 00

7F = byte 01

3F = byte 02

1F = byte 03


...we load 7F. If the player has a cape, then $19 = 02, so:


FF = byte 00

7F = byte 01

3F = byte 02

1F = byte 03


...we load 3F. If the player has firepower, then $19 = 03, so:


FF = byte 00

7F = byte 01

3F = byte 02

1F = byte 03


...we load 1F.


In other words, if the index is 00, then the first byte in the table is loaded; if the index is 01, then the second byte in the table is loaded; if the index is 02, then the third byte in the table is loaded, etc. This works with any table, no matter what how long it is. And since our table value goes into $1490 in this case, that means that if the player is small, he will get a star timer of FF, which is about 16 “seconds” on the game timer. If the player is big, he gets 7F invincibility time, which is about 8 “seconds”. If the player has a cape, he gets only 3F time, or about 4 seconds, and if he has firepower, he gets even less, 1F time or about 2 seconds. Cheap. Now, of course, these values aren't the be-all, end-all; if you want a less drastic change in time, you could use something like this instead:


InvincibilityTimer:

db $B0,$98,$80,$68


You could also give the player more time when he has a cape or firepower and less when he is just plain little or big, such as...


InvincibilityTimer:

db $30,$54,$78,$9C


That's indexing for you. It comes in handy for a lot of things, believe me. In fact, it is vital for sprites...but we won't learn about that until later. By the way, there are four different addressing modes for indexed addresses:


dp,x

abs,x

long,x

abs,y


In other words, you can use X to index a direct (8-bit), absolute (16-bit), or long (24-bit) address, but only an absolute address can be indexed by Y. You can sort of index a direct address by Y by simply adding 00 at the beginning, like so:


LDA $00C2,y (or if you're doing something that requires xkas, LDA.w $00C2,y)


You cannot, however, do anything like that with a long address. If you need to index a 24-bit address, you have to use X. As I said before...that's indexing for you.


Lesson 2-7: Bitwise Operations

Opcodes: AND, ORA, EOR, TRB, TSB, BIT, ROL, ROR

Addressing modes: none

Progress:

44/99

8/15

ADC AND ASL BCC BCS BEQ BIT BMI BNE BPL BRA BRK BRL BVC BVS CLC CLD CLI CLV CMP COP CPX CPY DEC DEX DEY EOR INC INX INY JML JMP JSL JSR LDA LDX LDY LSR MVN MVP NOP ORA PEA PEI PER PHA PHB PHD PHK PHP PHX PHY PLA PLB PLD PLP PLX PLY REP ROL ROR RTI RTL RTS SBC SEC SED SEI SEP STA STP STX STY STZ TAX TAY TCD TCS TDC TRB TSB TSC TSX TXA TXS TXY TYA TYX WAI WDM XBA XCE

imm8/16 dp abs long dp,x abs,x abs,y long,x (dp) [dp] (dp),y [dp],y (dp,x) sr,s (sr,s),y


p2.7.0

People seem to make bitwise commands more difficult than they should be, I think. They may seem daunting at first, but they really aren't that bad if you can get the concept down. Bitwise operations all have one thing in common, though: they use the individual bits of the operand (the address or value that your opcode affects, remember?) to “decide” what to do. You can think of it as 8 separate CMPs, one for each bit, if you wish. If you laid out the bits, they might look something like this:


0 1 0 1 1 0 1 0 (=5A)

1 0 0 1 0 0 1 1 (=93)


Now, there are three main bitwise opcodes. The first one is AND. AND takes the bits of the value of A, compares them with the bits of the value after the AND, and puts the new value back into A. With our two examples, this would be:


LDA #$5A

AND #$93


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

? ? ? ? ? ? ? ?


Taking each pair of bits individually, if either bit is 0, then the result will be a 0. The resulting bit will be a 1 if and only if both bits in the original pair were a 1. So, if we compare the first bit:


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

? ? ? ? ? ? ? ?


Here, we have a 0 and a 1, so that bit in the result will be 0.


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

? ? ? ? ? ? ? 0


If we compare the second bit:


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

? ? ? ? ? ? ? 0


We have a 1 and a 1, so the result is 1.


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

? ? ? ? ? ? 1 0


Comparing the third bit:


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

? ? ? ? ? ? 1 0


We have a 0 and a 0, so the result is 0.


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

? ? ? ? ? 0 1 0


If you do that with all right bits, then you end up with this:


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

0 0 0 1 0 0 1 0


00010010 = 12, so #$5B AND #$93 = #$12.


p2.7.1

So, you may ask, how can AND be useful? Well, I have to admit one thing about our example...it was pretty useless. ANDing two specific values wouldn't do you much good. However, you can AND a value and a RAM address. Let's make a code that checks if the player is touching the ground. The RAM address $77 lets us know on which sides the player is blocked. It is SxxMUDLR, but the UDLR is the only part we're concerned about here. If the U bit is set, then the player is touching the ceiling. If the D bit is set, then the player is touching the ground. If the L bit is set, the player's left side is touching a wall, and if the R bit is set, the player's right side is touching a wall. If the U, D, L, and R bits are all set, the player is feeling extremely claustrophobic. Okay...we want to check if the player is touching the ground, right? So we're only concerned about the D bit. The value if this bit is 04 in hexadecimal, so we can use this code:


LDA $77

AND #$04


The AND #$04 clears out all bits but the D bit, and if you look at the binary, you'll see why:


? ? ? ? ? x ? ?

0 0 0 0 0 1 0 0


Bits 0, 1, 3, 4, 5, 6, and 7 are all 0 in the value 04, so those bits will all be 0 in the result no matter what any of the ? bits are. But bit 2 (the one marked “x”) on the other hand...well, let's say the player is touching the ground. Then the “x” bit is 1, and since that bit in the AND value is also 1, the result will be 1. If the player is in the air, he is therefore not on the ground, so the “x” bit is 0. But if the “x” bit is 0, then the resulting bit will be 0, because, remember, both bits have to be 1 for the result to be 1. So LDA $77 AND #$04 can have only two possible outcomes: A = 04 and A = 00. It will be 04 if the player is on the ground and 00 if he isn't. At this point, I should add that BEQ and BNE have another function: They can also be used to check if the result of an operation is equal to zero. So if we do this...


LDA $77

AND #$04

BNE IsTouchingGround


...then the BNE will jump to “IsTouchingGround:” if the player was touching ground. The player was touching ground, therefore the result of the AND was 04, and 04 is not zero, therefore the BNE branches. If the player was in the air, then the result of the AND would be 00, so the BNE would not branch. If you change the BNE to BEQ, you can have it the other way around: the branch command branches if the player is not touching ground. AND works with any number; although 04 just happened to be a single bit, we could just as easily, for example, check the player's contact with the walls instead of the ground.


LDA $77

AND #$03

BNE IsTouchingWall


Same as above, but now we have four possible results instead of two because we're leaving two bits in the AND value. If the player is touching no walls at all, the result will be 00; if the player is touching a right wall, the result will be 01; if he is touching a left wall, the result will be 02; and if he is touching both (although that seldom, if ever, happens), the result will be 03. In any case, whatever value we get out, it is guaranteed not to be zero if the player is touching a wall on either side. Finally, one last example: AND can be used to clear bits of an address. We could, for example, make a LevelASM code that disables spin-jumping for the entire level. One way to do this would be simply to disable the A button. $17 and $18 hold the data that determins whether or not the A button is being pressed; if bit 7 of either is set, then A is being pressed. (10000000 = 80.) So we can clear that bit by doing the following:


LDA $17

AND #$7F

STA $17

LDA $18

AND #$7F

STA $18


7F in hexadecimal is 01111111 in binary, so when we AND #$7F, we leave all bits intact except for bit 7, which represents A being pressed. Now, there is, in fact, another way to do this, using a different opcode, which I will discuss later in this lesson. But anyway, that's AND for you. One nice thing about AND is it works with all addressing modes. If you can LDA something, you can AND it.


p2.7.2

The next common bitwise opcode is ORA. ORA is almost the opposite of AND; when you compare the bits, if either bit is a 1, the result will be 1. The result will be 0 if and only if both of the original bits were 0. If we use the example from before...


LDA #$5A

ORA #$93


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

? ? ? ? ? ? ? ?


Looking at bit 0:


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

? ? ? ? ? ? ? ?


We have a 0 and a 1, so the resulting bit will be a 1.


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

? ? ? ? ? ? ? 1


Looking at bit 1:


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

? ? ? ? ? ? ? ?


We have a 1 and a 1, so the resulting bit will also be a 1.


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

? ? ? ? ? ? 1 1


Looking at bit 2:


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

? ? ? ? ? ? ? ?


We have a 0 and a 0, so this time, the result will also be 0. If you go through all eight bits, you get this:


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

1 1 0 1 1 0 1 1


11011011 = DB, so LDA #$5A ORA #$93 gives us a resulting value of #$DB.


p2.7.3

Now, what fun things can we do with ORA? Not many, unfortunately...I heard it sucks at Super Smash Bros., it doesn't know how to play poker, and it has too little stamina to play baseball. ORA is, however, very good at doing certain things, even if winning a hula-hoop contest isn't one of them. You can, for example, use ORA to check if any of a group of addresses are equal to zero. For instance...


LDA $1490

ORA $1497

BNE Invincible


$1490 is the star invincibility timer, and $1497 is the flashing invincibility timer (you know, when you flash right after getting hurt). We don't know what the heck either of these are when we run this code, but we do know that if either of them are not 00, the BNE will branch. Remember what ORA does...if even one bit in either of those addresses is 1, then at least one of the bits in the result will be 1, so of course, the result will not be zero...so the BNE branches. Another thing we could do is this:


LDA $73

ORA $187A

BNE ThisLabelHasAWeirdName


$73 is the flag that determines if the player is ducking, and $187A determines if the player is riding Yoshi. So if either of those conditions are true, the result of the ORA will be a non-zero value and the BNE will branch. Mind you, ORA is not limited to one address. We can, for example, make a sprite that hurts the player unless he is invincible, ducking, or riding Yoshi, or the level is ending. ($1493.)


LDA $1490

ORA $1497

ORA $73

ORA $187A

ORA $1493

BNE LeaveHimAloneYouJerk


If any of those five initial conditions are true, then the BNE will branch. The most important thing about ORA, though, is that it can set certain bits. Remember how I said AND could be used to clear bits? Well, ORA can be used to set bits. Let's say we're making a level in which we want the player to be constantly running to the right. We can use $15 for this. Bit 0 indicates whether the player is pressing Right, and bit 6 indicates whether the player is pressing Y. We want both of those set, so we can do this:


LDA $15

ORA #$41

STA $15


That will make the player act like he is pressing Right and Y all the time. As with AND, there is another way to do it in this case, but we'll discuss that later in this section. ORA, like AND, can use all addressing modes that LDA can. This means that you can ORA #$05, ORA $1471, ORA $1540,x, ORA $7F8190, and a lot more.


p2.7.4

There is one more main bitwise opcode: EOR. After AND and ORA, EOR can look kind of weird, but it really isn't too difficult to understand. When you EOR two bits, the resulting bit will be 1 if one of the original two bits was 1 but not both. A 0 and a 0 gives us 0, a 0 and a 1 gives us 1, and a 1 and a 0 gives us 1, which makes it look like ORA...but a 1 and a 1 also gives us a 0. If we use our example...


LDA #$5A

EOR #$93


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

? ? ? ? ? ? ? ?


Looking at bit 0:


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

? ? ? ? ? ? ? ?


We have a 0 and a 1, so the result will be 1. Looking at bit 1:


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

? ? ? ? ? ? ? 1


We have a 1 and a 1, so the result will be 0. Looking at bit 2:


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

? ? ? ? ? ? ? 1


We have a 0 and a 0, so the result will be 0. If you do that with all eight bits, you get:


0 1 0 1 1 0 1 0

1 0 0 1 0 0 1 1

1 1 0 0 1 0 0 1


11001001 = C9, so LDA #$5A EOR #$93 gives us a result of C9. Now, you probably guessed I would ask this: What is EOR useful for? FLIPPING! For example, let's say you want to make a block that switches the on/off value. The on/off switch value is determined by $14AF. If it is 00, the switch is on, and if it is anything else (usually 01), it is off. So we can do this:


db $42


JMP MarioBelow : JMP MarioAbove : JMP MarioSide : JMP SpriteV : JMP SpriteH : JMP MarioCape : JMP MarioFireBall : JMP TopCorner : JMP HeadInside : JMP BodyInside


MarioBelow:


LDA $14AF

EOR #$01

STA $14AF


MarioAbove:

MarioSide:

SpriteV:

SpriteH:

MarioCape:

MarioFireBall:

TopCorner:

HeadInside:

BodyInside:

RTL


So, if $14AF was 00 before (read: the switch was on), it will now be 01. But if it was 01 before (the switch was on), it will now be 00. In fact, with EOR, you can even flip all the bits of something.


LDA ---

EOR #$FF


After this, whatever was in A...every bit that was 0 before the EOR is now 1, and every bit that was 1 before the EOR is now 0. You'll absolutely flip out once you know how to use EOR, believe me. [/badpun]


p2.7.5

There are more bitwise commands, but some of them aren't as straightforward as AND, ORA, and EOR are. The first two are TRB and TSB. These simply stand for “Test and Reset Bits” and “Test and Set Bits”. They act kind of like AND and ORA, but not quite. But they can be quicker in certain situations. For example, if you wanted to clear bit 3 of an address (say, $0F40), you could do the following:


LDA $0F40

AND #$F7

STA $0F40


#$F7 is #%11110111, so that “0” in bit 3 means that after the AND, bit 3 of whatever value is in A will invariably be clear. Then you store that value back into the address. But there is a slightly shorter way to do this:


LDA #$08

TRB $0F40


See? #$08 is #%00001000. We use A to specify which bit or bits we want to clear, and then we TRB an address to clear those. Want to clear bit 2 instead? Then do


LDA #$04

TRB $0F40


Want to do bits 0 and 7 simultaneously?


LDA #$81

TRB $0F40


TSB is similar. In fact, it is identical...except that it sets the bits instead of clearing them. So


LDA #$08

TSB $0F40


would set bit 3.


LDA #$81

TSB $0F40


would set bits 0 and 7. With both TRB and TSB, whichever bits are set in A, those are the bits of the address that the TRB or TSB will affect. Unfortunately, while TRB and TSB are quicker than AND and ORA for clearing/setting bits, they have a pretty big disadvantage: they each have only 2 addressing modes. You can TRB/TSB a direct page address (e.g. TRB $15), or you can TRB/TSB an absolute address (e.g. TSB $010D)...and that's IT. No long addresses, no indexed addresses...it's a shame.


p2.7.6

Another bitwise command you can use is BIT. BIT #$-- acts sort of like AND #$--, except that it doesn't affect the value of A. If we compare these two codes:


LDA $7FAB10,x

AND #$04

BNE BitSet


and


LDA $7FAB10,x

BIT #$04

BNE BitSet


...the first code would load the value of $7FAB10,x (which, by the way, is the address holding the extra bits for custom sprites), but then the AND #$04 clears all bits of A except bit 2, so now A is either 00 or 04. If it is 00, the BNE does not branch, and if it is 04, the BNE branches. The second code, on the other hand, loads the value of $7FAB10,x and...well, if the result from the BIT #$04 was 00, the BNE doesn't do anything, and if it was 04, the BNE branches, but there is an important difference: A still holds the same value it had before the BIT. This might be 04 or 00, but it could also be 0C, 88, or 06, for example. Can you use BIT with an address rather than a value? Is, for example, BIT $77 valid? Yes, but it's a little BIT harder to use. In fact, it is so weird that having a good understanding of it would require skipping ahead to section 3-4. But don't worry. BIT isn't terribly useful; most people just use AND, which works just as well or better nine times out of ten.


p2.7.7

The last two bitwise operations aren't terribly useful; they mostly just make some things shorter. It's kind of the same principle as TRB and TSB sometimes making a code slightly shorter than it would be if we used AND or ORA. These are ROL and ROR. ROL and ROR stand for “Rotten Lemons” and “Rotten Rutabagas”...I mean “Rotate Left” and “Rotate Right”. ROL and ROR are very similar to ASL and LSR, but with one important difference. You know how, when you ASL or LSR something, if a set bit goes beyond bit 7 or 0 (depending on the direction it is being shifted), it just...disappears? Eh, maybe not. Another example we shall have.


CLC

LDA #%01101010 ; #%01101010 = #$6A

ASL ; A is now 11010100, or #$D4; the carry flag is clear

ASL ; A is now 10101000, or #$A8; the carry flag is set

ASL ; A is now 01010000, or #$50; the carry flag is set

ASL ; A is now 10100000, or #$A0; the carry flag is clear

ASL ; A is now 01000000, or #$40; the carry flag is set

ASL ; A is now 10000000, or #$80; the carry flag is clear

ASL ; A is now 00000000, or #$00; the carry flag is set


Notice how the multiplication worked fine when A was between 00 and 7F (6A x 2 = D4, 50 x 2 = A0, 40 x 2 = 80), but when A was between 80 and FF, we got some rather odd results. Why does ASLing D4 give us A8, which is less than D4? Well, if you open up Windows Calculator (or whatever program you have, if you don't use Windows) and multiply D4 by 2, you get 1A8. This is a three-digit number, but right now, A can be only two digits...so the initial 1 is lopped off and we get A8. Now what's this about the carry flag? What the heck is the carry flag anyway? Once again, section 3-4 answers that, but right now, all we need to know about the carry flag is that ASL, LSR, ROL, and ROR affect it. In the case of ASL, whatever was in bit 7 (the highest bit) before goes into the carry flag. When we had #$D4, for example, bit 7 was a 1, so the carry flag ended up set after the ASL. When we had #$50, on the other hand, bit 7 was 0, so the ASL cleared the carry flag. LSR would do the same thing, only the lowest bit, bit 0, goes into carry instead of bit 7. So, assuming that the carry flag was clear beforehand (which it is, since I put a CLC there)...


First round:

76543210 C

01101010 0


Second round:

76543210 C

11010100 0


Third round:

76543210 C

10101000 1


Fourth round:

76543210 C

01010000 1


Fifth round:

76543210 C

10100000 0


etc. The bit shown in bold goes into carry, and the bit in italics disappears completely. Now, what if we used LSR instead?


CLC

LDA #%01101010 ; #%01101010 = #$6A

LSR ; A is now 00110101, or #$35; the carry flag is clear

LSR ; A is now 00011010, or #$1A; the carry flag is set

LSR ; A is now 00001101, or #$0D; the carry flag is clear

LSR ; A is now 00000110, or #$06; the carry flag is set

LSR ; A is now 00000011, or #$03; the carry flag is clear

LSR ; A is now 00000001, or #$01; the carry flag is set

LSR ; A is now 00000000, or #$00; the carry flag is set


LSR, if anything, is easier than ASL; you don't get any funny results, just pretty straightforward division by 2, with the carry flag set if the original value was an odd number. If you use the same kind of diagram that I had before, you get this:


First round:

76543210 C

01101010 0


Second round:

76543210 C

00110101 0


Third round:

76543210 C

00011010 1


Fourth round:

76543210 C

00001101 0


Fifth round:

76543210 C

00000110 1


etc. The bit in the carry flag disappears into oblivion just like it did with ASL, but this time, the lowest bit goes into carry instead of the highest.


p2.7.8

Okay, so what does all this have to do with ROL and ROR? Well, if we had used ROL in our first example instead of ASL, we would get this:


CLC

LDA #%01101010 ; #%01101010 = #$6A

ROL ; A is now 11010100, or #$D4; the carry flag is clear

ROL ; A is now 10101000, or #$A8; the carry flag is set

ROL ; A is now 01010001, or #$51; the carry flag is set

ROL ; A is now 10100011, or #$A3; the carry flag is clear

ROL ; A is now 01000110, or #$46; the carry flag is set

ROL ; A is now 10001101, or #$8D; the carry flag is clear

ROL ; A is now 00011010, or #$1A; the carry flag is set


WHOA! What the heck? The highest bit went into carry, all right...but instead of disappearing, it came out the other side! Yep, you heard me correctly. With ASL, the bit that goes into carry then disappears, but with ROL, it keeps going. With ASL, whatever was in bit 7 before is now in the carry flag and whatever was in the carry flag before vanishes, but with ROL, whatever was in bit 7 before is now in the carry flag and whatever was in the carry flag before is now in bit 0.


First round:

76543210 C

01101010 0


Second round:

76543210 C

11010100 0


Third round:

76543210 C

10101000 1


Fourth round:

76543210 C

01010001 1


Fifth round:

76543210 C

10100011 0


The bold bit still goes into carry, but the italicized bit goes into bit 0 rather than disappearing. Now, what if we did it the other way? What if we used ROR instead?...


CLC

LDA #%01101010 ; #%01101010 = #$6A

ROR ; A is now 00110101, or #$35; the carry flag is clear

ROR ; A is now 00011010, or #$1A; the carry flag is set

ROR ; A is now 10001101, or #$8D; the carry flag is clear

ROR ; A is now 01000110, or #$46; the carry flag is set

ROR ; A is now 10100011, or #$A3; the carry flag is clear

ROR ; A is now 01010001, or #$51; the carry flag is set

ROR ; A is now 10101000, or #$A8; the carry flag is set


The same thing happens, but instead of bit 7 going into the carry flag and the carry flag going into bit 0, the reverse happens: bit 0 goes into the carry flag and the carry flag goes into bit 7. An interesting thing you may notice is that some of the hexadecimal values in the list we made with ROR are exactly the same as those from the list with ROL. If you ROL or ROR 9 times, the result will be exactly the same as the original value, since there are 9 bits involved.


First round:

76543210 C

01101010 0


Second round:

76543210 C

00110101 0


Third round:

76543210 C

00011010 1


Fourth round:

76543210 C

10001101 0


Fifth round:

76543210 C

01000110 1


Now you see why they are referred to as “rotating” commands. If you could put the nine bits in a circle, with C (carry flag) at the top and the 0-7 clockwise around the circle, then ROR would “turn” the bits clockwise, and ROL would “turn” them counterclockwise.


p2.7.9

Bitwise commands! They look a bit scary when you first see them; you might have been a bit alarmed when you first saw them, eh? But you don't have to fear them one bit...they're not a bit difficult to use once you're a bit more familiar with them. Just give them a bit of love; they can be quite a bit useful in some situations. I hope you've learned a bit about them. Well, with that 8-bit string of bad puns, we are officially finished with section 2. Thanks for reading!