All the mathmatical operations you'll need to start programming can be broken down into four groups:
You can test these and your own math routines using MathTest.asm. In its current form it only supports the answer in the 8 bit registera
;
but, you can follow the directions in the comments
to change it to use hl
in 2 seconds.
Remember also that we are only working with integers here. Floating point math is taken care of in the Binary Coded Decimal section. You can always write your own routines which will probably be faster than TI's, but why reinvent the wheel?
I wouldn't go into any of these routines
if either of your operands is zero. Infinite loops
and bogus answers are sure to follow.
Addition
Seems simple enough, right? You're using the accumulator
(a
) so you load it with one operand and load the other
into another 8 bit register. After you've
got that setup, go ahead and add them. Let's add 4 and 5 to get 9.
Using add
, the result will be stored in a
.
ld a,4 ;one operand ld b,5 ;second operand add a,b ;a+b (saved in a)
Don't forget the 16 bit registers. You can appply
the same methods to them; but, instead of revolving
around the accumulator (a
), they revolve around
hl
. The advantage of using 16 bit registers
is that you can have way bigger operands.
ld hl,4000 ;4,000 ld de,15000 ;15,000 add hl,de ;4,000+15,000=19,000
Things can get a little more complicated when you start using the carry flag. The general idea is, add the two operands and, if the carry flag is set, add one to the answer. Don't add one if the carry flag was reset.
This is helpful, let's say, if you want to account
for an overflow in the previous calculation. You
have a calculation (
add
,
sub
,
dec
,
inc
, etc.)
which may or may not have resulted in an answer that was too big
for it to store. The carry flag will be set if so.
ld a,200 ld b,150 add a,b ;200+150=350 ; way too big ; to store in ; 8 bit register ;max it can hold ; is 255 ;carry set! ld a,4 ld b,5 adc a,b ;4+5=9 ;carry is set ; so 9+1=10 ;10 is stored in ; aThe practicality of this feature can be seen in the Simulated 16 bit Addition section, if you want to get more indepth.
Subtraction
Similar but opposite,
sub
is used for subtraction. The value you want subtracted from
is loaded into the accumulator (a
), the value you
want to subtract is put into another register, and then you
are ready to perform the actual subtraction.
ld a,5 ;one operand ld b,4 ;second operand sub b ;a-b=5-4=1 ;1 stored in aYou can get into a mess if you try subtracting a big number from a little number. You won't get the answer you expect and the carry flag will be set.
ld a,4 ;one operand ld b,5 ;second operand sub b ;a-b=4-5=-1=255From the Two's Compliment section, you can see that a value of -1 means the same as 255. You can go ahead and add this to another 8 bit register and it will be the same as adding -1.
ld c,6 ld a,4 ;one operand ld b,5 ;second operand sub b ;a-b=4-5=-1=255 add a,c ;a+c=-1+6=5 ;5 is stored in aAs with
adc
, there is sbc
.
It works the same way but adds one to the
number being subtracted if the carry is
set (not the
number subtracted from).
ld a,200 ld b,150 add a,b ;200+150=350 ; way too big ; to store in ; 8 bit register ;max it can hold ; is 255 ;carry set! ld a,5 ld b,3 sbc a,b ;5-(3+1)=1 ;carry is set ; so 3+1=4 ; 5-4=1 ;1 is stored in a
Multiplication
If you haven't figured it out yet, there aren't
any instructions to
directly multiply. You have to mix some
add
's, sub
's,
shifts, and rotates to get what you want.
The first few examples assume
the answer doesn't excede the capacity
of an 8 bit register (255).
You've been through elementary school, you know how the basics of multiplication: add repeatedly.
Since it's always better to use code
that you understand, this routine
is about as simple as it gets. B
is one
operand and c
is the other with
the answer in a
sub a ;zero a or b ;is b=0? ret z ;exit if b=0 so ret multiply_loop: add a,c ;add c to total so far djnz multiply_loopThe effect is that
c
is repeatedly
added to itself, the very idea of
multiplication in 6 bytes!
There are two drawbacks to this method.
Time is of the essence, you need
a fast routine. You need to multiply
a number (x
) loaded in
a
to a constant--let's
try 2 first. The advantage of
using a constant is that you
don't need loops, it goes through
the code once and only once.
You know exactly how long it's
going to take.
add a,a ;a=a=2*aThat simple! Try times 5.
ld b,a ;save it away temporarily add a,a ;a*2 add a,a ;a*4 add a,b ;a*4+a=5*aEight is a good number too.
add a,a ;*2 add a,a ;*2 add a,a ;*2=2*2*2=8Try another odd number like 13 using
sub
too. Just
subtract the
original a few times.
ld b,a ;save it away temporarily add a,a ;a*2 add a,a ;a*4 add a,a ;a*8 add a,a ;a*16 sub b ;a*16-a sub b ;a*15-a sub b ;a*14-a=13*aNone of these will hold an answer over 255. Try using 16 bit register pairs to multiply 35 and
hl
.
ld d,h ;save it away temporarily ld e,l ;save it away temporarily add hl,hl ;hl*2 add hl,hl ;hl*4 add hl,hl ;hl*8 add hl,hl ;hl*16 add hl,hl ;hl*32 add hl,de ;hl*32+hl add hl,de ;hl*32+hl+hl add hl,de ;hl*32+hl+hl+hl=32*hl+3hl=35*hl
In some cases, it's easier to add in that saved away original at an earlier date, like when the constant is not near a power of 2. 20's like that.
ld b,a ;save away original add a,a ;a*2 add a,a ;a*4 add a,b ;a*4+a=a*5 add a,a ;a*10 add a,a ;a*20If we didn't add in the original early like above, our code would be a lot longer!
ld b,a ;save away original add a,a ;a*2 add a,a ;a*4 add a,a ;a*8 add a,a ;a*16 add a,b ;a*16+a add a,b ;a*16+a+a add a,b ;a*16+a+a+a add a,b ;a*16+a+a+a+a=a*20It's a good thing we thought ahead. Sometimes you might have to add in the original a couple times along the way.
Let's divide Let's divide
Division
Similiar to multiplication,
just use subtraction.
a
by b
(a
/b
).
In this routine, the answer is stored into a
. Remember
that this is for integers that divide evenly. If you
have two numbers that you know do not divide
evenly, you will get an infinite loop usually (Your
calculator has crashed).
ld c,$00
divide_loop:
inc c
sub b
jr nz,divide_loop
ld a,c
ret
If you don't mind the final answer stored in c
,
and want to save a byte.
ld c,$00
divide_loop:
inc c
sub b
ret z
jr divide_loop
This is similiar to the basic
multiplication routine: subtract
repeatedly. This routine will
also exit if a
is zero and
become slower the larger the
operands are (more loops).
a
by a constant
of 8 using srl a
.
srl a ;/2
srl a ;/4
srl a ;/8
I don't recommend using
shift
logically
because it's two bytes long. It
is nice because it shifts the
bits right out of here.
Rotate
is faster but it just circulates those
bits to the other side. If you're
going to use rotate, make sure
to mask off those
bits using
and
.
This is one byte shorter. You'll
save more bytes if you divide
by higher powers of 2 (8=2^3=2*2*2)
and %11111000 ;mask off (get ; rid of) last ; three bits which ; will be rotated ; out ;we don't want them ; coming back in ; on the other side rrca ;/2 rrca ;/4 rrca ;/8