A Note on Byte Widening and Scratch Registers
on the 6800, 6801, and
6809
I briefly mentioned, in the introduction to byte arithmetic chapter on the 6800, 6801, and 6809, the necessity of expanding byte data to 16-bit to use the 6801 and 6809's
ADDD and SUBD instructions. I gave a more concrete example of byte widening in
the
68000's chapter on byte arithmetic
that we just finished.
In the 68000, the data registers are plenty wide enough. All we need to do, in the case of unsigned byte widening, is to make sure that, before we load the byte, enough of the register is cleared out to hold the target width.
In the case of signed byte widening for the 68000, we can use the sign-EXTend
instruction after loading the byte value, which we will take a look at
later.
(Other 16-bit and wider CPUs have load instructions that automatically
sign-extend or zero-extend registers on load, instead.)
Let's look at how we can widen a byte on the 6800.
Hmm? What? The 6800 doesn't have a 16-bit add, you say?
Well, yeah, but, (mutter, mumble, ...) actually, when we cleared A out before we used ADCA #0 and SBCA #0, we were effectively performing a byte widening. We just used an immediate zero byte as the high byte of the widened source.
Why are you looking at me like that?
Sigh. Okay, remember, add with carry (ADC) allows us to expand the width of
the ADD instruction. So we can synthesize a sixteen-bit add quite easily:
NLFT FDB 132 ; 132 in two bytes, high byte zero
NRT FDB 188 ; 188 in two bytes, high byte zero
RES RMB 2 ; 2-byte result
...
LDAB NLFT+1 ; Get the left low byte.
LDAA NLFT ; Get the left high byte.
ADDB NRT+1 ; Add the right low byte.
ADCA NRT ; Add the right high byte.
STAB RES+1 ; Store the result low byte.
STAA RES ; Store the result high byte.
And there's your 16-bit add. Just takes 6 instructions. >:-)
Comparing that to the 6801 and 6809, same declarations as above:
NLFT FDB 132 ; 132 in two bytes, high byte zero
NRT FDB 188 ; 188 in two bytes, high byte zero
RES RMB 2 ; 2-byte result
...
LDD NLFT ; Get the left 16-bit word.
ADDD NRT ; Add the right 16-bit word.
STD RES ; Store the result 16-bit word.
3 instructions.
Okay, it takes a few more than 6 on the 6800 if we need to get the flags perfectly right, but we don't usually need to get the flags perfect for addition. Hardly ever, really. The Carry is correct, and so is the Negative. The Zero only reflects the high byte, which is wrong, but we have ways to handle that. So we're good for most purposes on the 6800 -- for addition.
If we need to test for zero results, we can check that Carry is clear first,
then OR the result low and high bytes together. If carry was set, we know the
result was $10000 or greater, so, non-zero. ORing the low and high bytes
together will leave non-zero if either was non-zero, so that completes the
test. (I can make it sound simple, right? It's simple here because we have two
copies of the result, one in memory and one in the accumulators. There is more
to this, but I don't want to get too distracted. We'll come back to it.)
And in another point of fact, because loads and stores don't affect the carry flag, we could do all that math in 6 instructions just using a single accumulator instead of using both.
So, let's take that as a jumping-off point, declare the values we're adding as
bytes again, and expand before adding. We'll start by declaring a byte of
scratch RAM, preferably in the direct page:
SCRTCH RMB 1 ; preferably in DP
...
NLFT FCB 132 ; 132 in one byte
NRT FCB 188 ; 188 in one byte
RES RMB 2 ; 2-byte result
...
CLRA ; A high byte of zero
STAA SCRTCH ; Another high byte of zero
LDAB NLFT ; Get the left byte.
ADDB NRT ; Add the right byte.
ADCA SCRTCH ; Add the widening bytes.
STAB RES+1 ; Store the result low byte.
STAA RES ; Store the result high byte.
So, do you believe me now?
Let's look at that on the 6801 (and 6809, by translating LDAB to LDB):
SCRTCH RMB 2 ; preferably in DP
...
NLFT FCB 132 ; 132 in one byte
NRT FCB 188 ; 188 in one byte
RES RMB 2 ; 2-byte result
...
CLRA ; A high byte of zero
LDAB NRT
STD SCRTCH ; widened 16-bit word
LDAB NLFT ; Get the left byte, A still clear.
ADDD SCRTCH ; Add the widened 16-bit word.
STD RES ; Store the result high byte.
Because the D register operators need 16-bit operands, widening for the 6801 and 6809 requires two bytes of scratch RAM instead of one -- unless you're doing the math one byte at a time, in which case you can still use the immediate zero argument to ADC as the source.
The above sources are slightly incomplete, so you might need to edit them a little to check what I am saying. Not much, but a little.
Subtraction is pretty similar, although we do care more about the flags after subtraction.
By widening before we add or subtract, the addition and subtraction sequences
can be made quite parallel. You might want to simply substitute SUBtract for
ADD and SuBtract with Carry for ADd with Carry in the above code to see what
happens.
I keep trying to go a bit further on the flags and such, but that is a
distraction.
One thing that I need to make clear before we go, where the 68000 with lots of
accumulator-equivalet data registers shows how natural byte widening is,
having only one 16-bit accumulator makes byte widening on the 6801 and 6809
use scratch registers.
And I'm thinking we can now, after you've played with the subtraction code, move forward to doing proper 16-bit arithmetic.
>:->
No comments:
Post a Comment