ROM hacking like a Pro - Tutorial #2
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6502 assembly:
Continuing 6502 assembly, we now teach adding, subtracting, the stack, and
conditional statements.
Tutorial #2:
In 6502 assembly, the ADC instruction is used for addition, and SBC for
subtraction. Before we cover that, lets talk about arethemetic in binary.
Lets do a sample addition problem in binary
01101110
+ 01010101
To do this problem, wherever there is a '0' and a '0' we add to get 0 if there
is a '0' and a '1' we add to get 1. When there is a '1' and a '1' though, we get
2, but we can't write 2 in binary so we have to carry the 1 to the next digit.
Just like in decimal arethmetic, we carry anything over 9 to the next digit.
Here is the above problem worked out with the carried numbers written on top.
1 111
01101110
+ 01000101
--------
10110111
Lets do another one
1 1
1100010
+ 1011010
-------
10111110
If you'll notice, the result couldn't fit into 8 bits this time! It had to go
into 9. This is a rule you must know, anytime you add 8 bits to 8 bits, the
result has to go into 9 bits. The 9th bit is called the carry bit and it has
great signifigance in assembly programming as we will see.
Another way of thinking about this to understand why we need an extra bit, is to
not use binary at all, just understand that 8 bits can hold a maximum of 0 to
255 while 9 bits can hold 0 to 511. if you add 255+255 you get 510. 510 can no
longer fit in 8 bits, it must go into 9 bits.
At any rate, we need a carry bit, since any time you add 8 bits with 8 bits, the
answer can only fit in 9 bits. In 6502 there is a register called the processor
status register. One if the bits in it is where the extra carry bit is. That
carry bit is also used when any new addition instructions are carried out. So,
before you do an addition you must make sure it is clear. The CLC instruction
clears it.
So enough talk, lets look at some code. This small program adds 2 and 8.
.ORG $8000
LDA #$08
CLC
ADC #$02
BRK
Assemble and step through this program in the 6502 simulator. When it finishes,
A should contain 0A which is the hex equivalent of 10. 2+8 is 10, so it works!
The program itself is very simple It loads A with 8, then clears the cary flag,
and adds 02 to the accumulator, resulting in 0A being in the accumulator.
(NOTE: The A register is referred to as the accumulator, when I say "The
accumulator" I mean the A register)
If you look up ADC it means add to A with carry. The carry flag itself
gets included in the add, so that is why we must clear it first with a CLC.
As a quick review exercise, I'm going to ask a question. What is the difference
between:
ADC #$02
and
ADC $02
--- Try to guess before reading the answer
The answer is that the first one adds the value 2 to what is in the accumulator,
and the second one adds what is at the memory location $02 to the accumulator.
I told you earlier to remember that #$ means the value of, while $ means what is
at the address. What I didn't tell you is that this is referred to as addressing
modes. The 6502 has 13 total addressing modes. I'll go through several right
here
First, here's some terminology, the operator is the instruction itself.
Like 'LDA', 'ADC', 'CLC', 'TXA', those are all operators. The operand is the
stuff that comes after the operator, like #$02 or $F4.
1. Implied
This addressing mode is where only the instruction is given, no operands are
needed or given. Some instructions you have encountered that use this are:
TXA
CLC
INX
INY
2. Immediate
In this addressing mode, the value is given directly with a #$ put before it.
examples
LDA #$02
STA #$83
3. Relative
The branch instructions use this, we haven't covered them yet, so I can't tell
what this does, but it is worth mentioning because we will be covering it later
in this tutorial.
4. Zero Page
In this addressing mode, a 1 byte memory address is given. This is what we have
used so far for giving addresses. Examples are:
LDA $02
STA $02
5. Absolute
This addressing mode is just like zero page, except 2 bytes are given for the
address. Examples are:
LDA $074D
STA $08A4
note that if you used absolute addressing and wrote
LDA $0002
it would be completely equivalent to the zero page instruction
LDA $02
You might ask, why have zero page at all? Well, it executes faster, and results
in smaller code. This makes the 255 bytes of memory at $00 to $FF very valuable
memory, because you can access it quickly, and with smaller code.
The other instruction we'll look at is SBC. This is for Subtracting. It also
uses the carry flag, but because of the way subtraction works (I won't bore you
with the details) The carry flag must be set to 1. Whereas with addition we had
to clear it to 0. You set it to 1 with the SEC flag. Always remember to set the
carry to 1 before subtracting.
here's some sample code to step through.
.ORG $8000
LDA #$08
CLC
ADC #$02
SEC
SBC #$05
CLC
ADC #$10
BRK
This bit of code Loads A with 8, adds 2 to it, then subtracts 5, then adds 16.
the result is 8+2-5+16 or 21. 21 in Hex is 0x15 (note -- I'll be prefixing
hex values with 0x from here on) so A is 0x15 whenever execution ends.
--The Stack--
Now, lets move to the Stack. First, let me give you the idea. Think of a stack
of books, where you can only place one book on top, or take one book off. You
cannot actually take a book out of the middle. So whatever you take off comes
off in reverse order of what you put on it. Say the books were different colors,
and I wrote some pseudocode to do this
Place a yellow book on the stack
Place a red book on the stack
Place a blue book on the stack
Take book off of the stack, What color is it?
Take book off of the stack, What color is it?
Take book off of the stack, What color is it?
The first time we take a book off, the color is blue. The second time it is red.
The third time it is yellow. This is because, as I said, they come off in
reverse order.
PHA is the instruction to push A onto the stack. PLA takes the previously pushed
value off of the stack. With that in mind, Assemble and run this program in the
simulator
.ORG $8000
LDA #$05
PHA
LDA #$03
PHA
LDA #$09
PHA
PLA ;A should be 09 after running this
LDA #$12
PHA
PLA ;A should be 12 after running this
PLA ;A should be 03 after running this
PLA ;A should be 05 after running this
BRK
Carefully step through the program. One nice thing about this simulator is it
actually shows the stack to you in the "Registers and Status" window. What I
want you to note is what number gets put in A after running each PLA
instruction. It gets put back in reverse order of what was put in. I put in
comments the actual value A should be after running each PLA. Can you see this
is the reverse order?
The stack has many uses, it allows you to make a backup copy of A just with a
single instruction, PHA, and to restore it with PLA. The stack also has other
uses that I will go into later. One bad thing about the stack on the 6502 is
that you can't directly store the X and Y registers into it. Only the A
register has an instruction to push and pull numbers.
--Conditionals--
Now, moving on, we cover conditional statements. Before understanding them
though, you have to know more about the status register. I mentioned it in
passing earlier when I said the carry bit was stored there. There are other bits
stored there as well. The status register pretty much has different flags that
get set and unset to tell the result of the last operation. Here is a list of
the flags we will focus on now
Z - The Zero Flag. This tells if the result of the last operation was 0. If so
then it is set. This flag can actually become very confusing, at least it was to
me at first, because I would look at it, and think that it being zero means that
last operation was zero. This is totally wrong, so just think of it as answering
the question: "Was it Zero?" If it is 1 then the answer is true. 0 means false.
C - The Carry Flag. We know what this is, the extra bit in an addition or a
subtraction goes here.
N - The Negative flag. This matches the high bit of the last operation that was
carried out. So, if you do
LDA $#82
N will be 1 because 0x82 is the same as 10000010 in binary, thus the high bit is
1, so N is 1.
There are more, but these are the only ones you should be concerned with right
now. the others are rarely ever important anyway.
The 2 conditional instructions we'll start with are BEQ and BNE. They change the
flow of execution in the program depending on the state of the Z flag. BEQ means
Branch on Equals. But the best way to read it is "Branch on Zero" because it
branches if the zero flag is set. BNE means Branch on Not Equals. It does just
the opposite of BEQ and branches when the zero flag was unset. Read it as
"Branch on Not Zero"
The branch instructions use an addressing mode all of their own called
"Relative," as I mentioned earlier. In an assembler you have to use labels as
the operands to tell them where to go. So, you might have this
labelsomewhere:
some code
... more code...
BEQ labelsomewhere
That is just like a goto statement in C, and in most other languages. It says to
go to the label, if the branch condition is true.
Here is some code to assemble and step through in the simulator
.ORG $8000
LDA #$00
goback:
BNE theend
LDA #$01
BNE goback
theend:
BEQ goback
BRK
What I want you to carefully notice is the order that the statements are
executed. So, notice the yellow arrow as you step through. Also, notice the
checkbox for the Z flag in the Registers & Status window. When it is checked
the Z flag is 1, unchecked means 0.
What is happening is fairly simple. first, A is loaded with 00. The result of
that operation was 0, so the BNE theend (read it as "Branch on Not Zero")
doesn't really skip to the end. Next, A is loaded with #$01. The result of that
operation was 01, which is not 0, so the BNE goback moves execution back. The
zero flag is still set, so the BNE theend causes the program to go to theend.
The BEQ goback instruction at the end does nothing, because the zero flag is
still set.
You may have to step through this several times, and reread that last paragraph
to really get a feel for what is going on. Take your time. No hurry.
I think it is time we finish the 2nd tutorial. In tutorial #3 I will cover more
on branching, and I will cover loops.
Exercises:
In addition to the practice exercises below, I also want you to play around,
and write some of your own code to expirement with branches and the stack.
1. Practice adding in binary! make up several 8 bit numbers, and add them on
paper in binary. Be sure to carry as needed.
2. Write a program to add 2+2, then push the answer onto the stack.
Then add 4+4 and push the answer on the stack. Then, subtract 3 from 8 and push
it onto the stack. Then, pull them all off at the end.
copyright BBITMASTER (Ben Goodrich) Last Revision 01-10-2007