The HC11 has a very conventional set of branch instructions; you will find something very similar on almost any computer that uses condition codes. The HC11's branch instructions can be divided into unconditional branches, branches on the state of any individual condition code, branches based on unsigned arithmetic, and branches based on signed arithmetic. Every branch appears in both a "positive" and a "negative" form, in the sense that each has a form that takes a branch if a condition evaluates to true, and another form which takes the branch if that same condition evaluates to false.
Something else the HC11 does, which is very common, is to use the C bit to mean "Borrow" for a subtract or a compare.
Put them together, and it means you do branches by somehow getting the relevant information into the condition codes (ideally as a side-effect of some other computation that was necessary anyway), and then use an appropriate branch instruction.
Here's an example: let's assume we have a pair of signed integers in
the a
and b
accumulators, and we want to end
up with the smaller of the two in the a
accumulator. We
don't care what ends up in the b
accumulator; this isn't
a swap. We can do it like this:
cba * a - b
ble noswap * branch based on results of compare
tba * copy b into a
noswap
Here's another example: we want to compute the sum of the integers
from one to b
, where b
is the integer in the
b
accumulator. We'll assume b
is non-zero.
We can do this with a loop:
clra * a = 0
loop * do {
aba * a = a + b
decb * b = b - 1
bne * } while (b != 0)
Notice that this time, we didn't explicitly do a compare. We relied
on the fact that the decb
instruction would set the
condition codes for us.
Here's a table showing the full set of branch instructions on the HC11:
Positive Form | Negative Form | ||||
---|---|---|---|---|---|
Mnemonic | Description | Boolean Expression | Mnemonic | Description | Boolean Expression |
Unconditional | |||||
bra |
Branch Always | 1 | brn |
Branch Never | 0 |
Single Condition Code | |||||
bcs |
Branch Carry Set | C = 1 | bcc |
Branch Carry Clear | C = 0 |
bmi |
Branch Minus | N = 1 | bpl |
Branch Plus | N = 0 |
bvs |
Branch oVerflow Set | V = 1 | bvc |
Branch oVerflow Clear | V = 0 |
beq |
Branch Equal (to 0) | Z = 1 | bne |
Branch Not Equal (to 0) | Z = 0 |
Signed Branches | |||||
blt |
Branch Less Than | N ^ V = 1 | bge |
Branch Greater Than or Equal | N ^ V = 0 |
ble |
Branch Less Than or Equal | (N ^ V) | Z = 1 | bgt |
Branch Greater Than | (N ^ V) | Z = 0 | Unsigned Branches |
blo |
Branch Lower | C = 1 | bhs |
Branch Greater Than or Equal | C = 0 |
bls |
Branch Less Than or Equal | C | Z = 1 | bhi |
Branch Higher | C | Z = 0 |
Notice a few things: bpl
is misnamed - it's really on
non-negative. There are two instructions in the table that appear
twice each! I don't really know why the brn
instruction
exists. My suspision is that, since all the other branches have both
a positive and a negative form, it was just easier to build it into
the instruction set than to leave it out. It can also be useful in
timing loops; since a nop
takes two cycles and a
brn
takes three, you can delay for any number of cycles
you want (greater than one).
We can implement the If-Then-Else and While fundamental forms easily with the branch instructions.
If-Then-Else | |
HLL Code | Corresponding Assembly Code |
---|---|
|
|
note: you have to branch on the opposite of the condition you're looking for | |
Example HLL Code | Resulting Assembly code |
|
|
While | HLL Code | Corresponding Assembly Code |
|
|
As with conditionals, you have to branch on the opposite of the condition that keeps you in the loop | |
Example HLL Code | Resulting Assembly code |
|
|