Remember that in a previous lesson, I said that the Program Counter is a special address register that points to the current instruction and that this register can't be changed directly. Today, I'm going to show you two instructions that can change PC, jmp and bra.
The jmp instruction replaces the value in PC with another address. This can be accomplished using any of the addressing modes except for ordinary PC relative addressing and register direct addressing.
Examples:
jmp $FF0000 ;this would cause a jump to memory address$FF0000
jmp $F000.w ;this would cause a jump to memory address $FFF000
movea.l #$300, a0
jmp (a0) ;this would cause a jump to memory address $300
move.w #$40, d0
jmp $4(d0, PC) ;this would cause a jump to $44 bytes past the current PC
The bra instruction is similar to jmp, except that it only works with simple PC relative addressing.
Examples:
bra #$100 ;branches to $100 bytes past the current pc
bra #$6.b ;brances to $6 bytes past the current pc
Having to calculate memory addresses and offsets are a real pain so assembler authors came up with a simple way around all this nonsense. They're called labels. A label is simply an alphanumeric string without any spaces. It must start with a letter. To define a label simply type it in at the first position on a line and add a colon. Once you've defined a label, you can treat it like a normal address. Also note, that instructions need to have whitespace in front of them, unlike what I've done so far in the tutorial
Example:
Mylabel:
bra Mylabel ;this would stick the processor in a never ending loop
Don't do this:
Mylabel:
bra Mylabel
Now you know how to make programs that don't just go in a straight line, but there is something that makes this even more useful, flags and conditions. The flags are just a bunch of bits in a special register called the Condition Code Register or CCR for short. These bits are effected by most instructions. For this section of the tutorial I'm only going to introduce you to one flag, the zero flag. This flag is set to 1 (true) if the result of the last operation was zero or more accurately if the destination in the last operation was set to zero.
Examples of when the zero flag is set:
move.w #0, d0
move.w #1, d0
sub.w #1, d0
move.b #$FF, d0
add.b #1, d0
Examples of when the zero flag is not set:
move.w #1, d0
move.w #3, d0
sub.w #1, d0
move.b #$FF, d0
add.b #$2, d0
Well that's all well and good, but how do we use these flags once they've been set? Simple, with branch instructions. Remember bra? That stands for BRanch Always. There are a number of variations on bra that only branch when certain flags are set. I'm going to introduce you to two right now, beq and bne.
beq stands for Branch if EQual. What this really means is that the processor will take the branch if the zero flag is set. If not, then execution continues as normal. You may be wondering where the EQual part comes from. It makes sense in subtraction and compare(we haven't touched on compare yet) operations. If both values are equal and you perform one of these operations, then the result will be zero, thus the name of the instruction.
Example of branch getting taken:
move.w #1, d0
sub.w #1, d0 ;1-1 = 0, zero flag set to 1 (true)
beq Skip: ;zero flag is set so the branch is taken
move.w #5, d0 ;this instruction is skipped
Skip: ;executiong jumps to here
addq #3, d0 ;d0 now equals 3
Example of branch not getting taken:
move.w #2, d0
sub.w #1, d0 ;2-1 = 1, zero flag set to 0 (false)
beq Skip: ;branch is not taken
move.w #5, d0 ;this instruction is executed
Skip:
addq #3, d0 ;d0 now equals 8
bne stands for Branch if Not Equal. It's the exact opposite of beq. If the zero flag is zero (false) than the branch is taken otherwise execution continues as normal. I won't do an example, since it should be easy enough to figure out given the examples for beq.