Math
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 someadd
'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.
- The larger the numbers, the more loops executed, the slower the routine.
- The answer can't excede 255 or there's an overflow. You need to use 16 bit registers.
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.
Division
Similiar to multiplication, just use subtraction.Let's divide 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 retIf 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_loopThis 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).
Let's divide a
by a constant
of 8 using srl a
.
srl a ;/2 srl a ;/4 srl a ;/8I 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
More from z80 » Beginner
Aliases // Convert from decimal to hexadecimal or binary // Flags and Conditions // Format and Compiling // Instructions // Math // Number Bases // Oh, No! It Crashed! // Registers // TI-BASIC to Asm Comparisons // TI86 Specifications // Two's Compliment // z80 Processor