Signed and unsigned comparisons in Motorola 6800 assembly

Ben Zotto
5 min readDec 4, 2023

--

Just the most incredibly niche post about something that is not very clearly explained anywhere.

This is a Motorola 6800 computer chip. Photo: Michael Holley.

If, like me, you spend any time attempting to write machine code for ancient 8-bit Motorola 6800 systems, you will eventually have a need for conditional branches besides just BEQ and BNE. (See how niche this post is already! Haha!) Perhaps, like me, you might also think “well, this one called Branch If Less Than Or Equal To” (BLE) must be the one to use for less than or equal to, right? But then you find “Branch if Less Than Or Same” (BLS) and you think, uh oh, what’s this one, and are they different?

Yeah, they are different, but in most documentation it’s not obvious which one does what you want, which is why it’s super confusing. This brief post will explain the handful of 6800 conditionals and how to use them.

Signed vs unsigned comparisons

The most important thing to understand is that some of the conditionals are designed to be used for signed number comparisons and others for unsigned comparisons, and the names of the instructions do not indicate which is which.

Unsigned comparisons are what you’re doing when the things you are comparing are 8-bit numbers ranging from 0 to 255 in decimal value. In other words, 250 is greater than 100, which is greater than 50, which is greater than 0. In hex that looks like $FA > $64 > $32 > $00. So far so simple, right?

Signed comparisons are done when you are treating your eight bits instead as twos-complement signed values. They are still just 8-bit values, of course, but you are interpreting them as having instead a valid range of -128 to +127. The magic of this form of representation is that you can just do normal addition and subtraction on them. As long as you close your eyes and believe hard enough that all of the values in your sequence of operations are signed in this way, then the results will be correct. In this representation, a byte with its high bit set is always negative. $FF has the value of -1, for example. If you compare two 8-bit values that you understand to be signed, then 50 > 0 > -1, obviously, but in hex notation these three values are $32, $00, and $FF! You can see how an unsigned comparison will not be the same here.

So, the 6800 has a set of branching instructions for each representation, and it’s important to use the one that actually corresponds to what you’re doing. In some cases (small positive numbers), they will behave identically, since the representations will work out the same. But not always!

8-bit only!

Important note! The conditional branches themselves of course do not do the comparison. They all are to be used following a CMP (compare) instruction. And they are only valid for an 8-bit CMP! The 16-bit CPX instruction is hobbled somewhat in that it sets the flags to allow equality testing (BEQ/BNE) only. You might think you can also use the relative branches but you cannot!

Unsigned conditionals

OK, when you’re using unsigned 8-bit values, which is what you do most of the time if you’re not doing “math” in your program, here are the instructions you want:

BCS aka BLO (Branch if carry set): <
BLS (Branch if less than or Same): <=
BCC aka BHS (Branch if carry clear): >=
BHI (Branch if higher): >

This is sort of a weird grab bag of names which is why I like to put them all here in this order as a cheat card. Note that two which refer to the carry flag (BCS, BCC) are sometimes seen in an unofficial synonym form: BLO and BHS respectively, for “branch if lower” and “branch if higher or same.” These are more sensible mnemonics when used for numeric comparison! But I’m not sure if all assemblers understand them.

So, for example, if you have some value in the A register, and you want to know if it’s strictly larger than decimal 200 ($C8), you can say:

CMP A #$C8    ; compare A to $C8
BHI ISBIGR ; branch if greater than $C8

The same form applies to all the others.

Signed conditionals

If you are comparing two values that you are treating as signed, you want to be using one of the following relative branches:

BLT (Branch if less than, or sandwich): <
BLE (Branch if less than or equal): <=
BGE (Branch if greater than or equal): >=
BGT (Branch if greater than): >

These have more nicely symmetrical names than the unsigned versions. I almost never use them because I am so rarely doing signed math.

Where do BEQ and BNE fit?

Branch-if-equal and Branch-if-not-equal are used for any representation, because they simply rely on equality of all the bits. It doesn’t matter whether you think of those bits as signed or unsigned.

And because zero is the same ($00) in all representations, you can always safely use BEQ/BNE to check for zero following an accumulator load, say.

Can you put all of the above into a simple chart that I can print and tape to my wall for reference?

Clip ’n’ Save!

What about BMI and BPL?

These two branching conditionals are not for use with the CMP instruction — instead these interpret an actual value, like the result of a mathematical instruction. They are useful in both signed and unsigned contexts, but mean sort of different things. They both examine only the high/sign bit (bit 7).

BMI (Branch if minus) takes a branch if the high bit is set. It is so named because if you’re looking at a signed value, the high bit indicates a negative (“minus”, I guess) number. But checking the high bit also shows up in all kinds of bitwise operations.

BPL (Branch if plus) does the opposite: takes a branch if the high bit is clear. In signed contexts that means a positive-or-zero value.

How does it all work?

All the CMP instruction does is subtract the operand from the appropriate accumulator. In fact it does exactly the same operation that the SUB instruction does, except it just throws away the numeric result of the subtraction. But all of the status flags are set as if the subtraction happened. Then the conditional branches work by looking at one or more of these status flags: sign, carry, zero and overflow. These contain all the information necessary to make decisions about relative values, both signed and unsigned.

The specific formulas used by each branch instruction can be found in the documentation. It’s a useful exercise to try to understand how particularly the signed comparisons are determined.

I hope this fun journey into the world of Motorola 8-bit relative branching has been enjoyable, or at least useful. This is the post I kept looking for when learning and never found, so I’ve just written it. Happy computing!

Among various other things, I’m an engineer and historian writing a book about the obscure Sphere microcomputer, which is a 6800 machine. You can find more information on that, plus contact information, at my site https://sphere.computer

--

--