Category : Assembly Language Source Code
Archive   : ASMTUT2.ZIP
Filename : CHAP5.DOC

 
Output of file : CHAP5.DOC contained in archive : ASMTUT2.ZIP







*****************************************************************
REGISTRATION

Hey, Chuck, I'm no chump!

I'm using your programs/manual, and I want to pay my fair share.
Please make me a registered user of "The PC Assembler Tutor" and
"The PC Assembler Helper". Enclosed is a check for $9.95 (plus
6.5% tax or $10.60 for California residents). Say, that's cheaper
than a large pizza!


Name_________________________________________________________
Last First Initial

Address______________________________________________________
Street Address

_______________________________________________________
City, State, and Zip Code

I got my copy from ___________________________________________

Make checks payable to NELSOFT and send your registration to:

NELSOFT
P.O. Box 21389
Oakland, CA 94620

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
REGISTRATION BENEFITS

As a registered user of "The PC Assembler Helper" and "The PC
Assembler Tutor" you are entitled to:

1) Use asmhelp.obj and helpmem.com for personal use.
2) Make 1 (one) printer copy of "The PC Assembler Tutor".
3) Use all programs in "The PC Assembler Tutor" for personal use.
4) Make an archival copy of the disks.
5) Distribute UNALTERED disks to friends for their perusal.
6) Use any updates to either "The PC Assembler Helper" or "The PC
Assembler Tutor" under the same registration conditions.

Though copies of the disk may be given away if there is no
charge, it is illegal to charge for redistribution of the disk or
its contents without permission of the author. Under no
circumstances may you distribute printed copies of "The PC
Assembler Tutor". If you intend to charge for distributing the
disk or its information, please read and sign the distribution
agreement which is in INTRO1.DOC.

*****************************************************************



31

CHAPTER 5 - ADDITION AND SUBTRACTION


The first arithmetic operations we will look at are addition and
subtraction, but before we do that, we need to look at one
instruction that controls program flow.

LOOP

We already have JMP which sends you to a label:

jmp label3

sends the program to label3, wherever that is in the code.
Sometimes we want to repeat a section of code a specific number
of times and then go on. For this, we have LOOP. LOOP decrements
the CX register by 1. If CX is not zero after being decremented,
LOOP jumps to the label indicated. If CX is zero after being
decremented, LOOP falls through.

The 8086 does not have general purpose registers. A general
purpose register is a register that can be used for ALL
instructions. There are a number of instructions on the 8086
which must be done with specific registers, and LOOP is the first
one we meet. LOOP always looks at the CX register.


This first program lets you enter a number and then loops that
many times so you can watch the CX register. As usual, you exit
the program by hitting Control-C. We use temp2.asm.

temp2.asm
; - - - - START CODE BELOW THIS LINE
call show_regs ; initialize

outer_loop:
call get_unsigned
mov cx, ax ; number to cx

inner_loop:
call show_regs_and_wait
loop inner_loop

jmp outer_loop

; - - - - END CODE ABOVE THIS LINE

A very simple program. As always, link it with asmhelp.obj.
Get_unsigned gets a two byte number (less than 65536) and puts it
in AX. We put that number in CX, and then watch the program loop.
Make sure you use show_regs_and_wait, or everything will happen
too fast for you to see. Try entering 0. On the first pass, loop
will decrement CX from 0 to 65535. If CX is 0 when you enter, you

______________________

The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson




The PC Assembler Tutor 32
______________________

have to repeat the loop 65536 times before you exit the loop. Hit
Control-C now to exit.

Throughout the book, I will use label names that end in '_loop'
to indicate that they are the destination of a jump or loop
instruction. The single word "loop" is a reserved word and may
not be used as a label - it can only be used as an instruction.

The addition program will have 4 sections and LOOP will give us
the ability to do each section a limited number of times before
going on to the next section.


ADDITION

If you read the introductory section on numbers carefully, you
know that it is the same instruction for both signed and unsigned
addition. The 8086 sets the flags correctly for both signed and
unsigned addition. For signed addition, the following flags are
set:

OF the overflow flag is set (1) if the result is too
negative or too positive, that is, if the result in the
register does not show the correct result of the addition.
It is cleared (0) otherwise.

ZF the zero flag is 1 if the result is zero, and is
cleared (0) if the result is non-zero.

SF the sign flag is set (1) if the result is NEGATIVE and
is cleared (0) if the result is POSITIVE. Zero is considered
a positive number.

For unsigned addition, the following flags are set:

CF the carry flag is set (1) if the result is too large
(over 255 for byte and over 65535 for word operations). It
is cleared (0) otherwise.

ZF the zero flag is the same as above.

In addition, there are two more flags (PF the parity flag and AF
the auxillary flag) which will be set or cleared; we will learn
about them later.

Show_regs shows all the flags. The setting for each flag is
underneath its name. For the flags OF, ZF and CF, there is an 'X'
if the flag is set and a blank if the flag is cleared. SF, the
sign flag, is '-' if the flag is set and '+' if the flag is
cleared.

The addition program is fairly long because there are four things
to look at - unsigned word addition, unsigned byte addition,
signed word addition and signed byte addition. For that reason,
it has already been typed in for you. It is called ADD1.ASM and
its pathname is \XTRAFILE\ADD1.ASM. Print out a copy of it.





Chapter 5 - Addition and Subtraction 33
____________________________________

There are four blocks of code which are almost identical except
the calls are a little different and two blocks refer to whole
registers while the other two refer to half registers. At the
head of each block is code to set the appropriate register styles
for show_regs. SI, DI, and BP are not used and are set to 0 to
make the screen easier to read. Here is the first block of code,
which is typical.

; - - - CODE
; UNSIGNED WORD ADDITION
mov ax_byte, 2 ; ax, bx, dx unsigned
mov bx_byte, 2
mov dx_byte, 2
lea ax, ax_byte ; call set_reg_style
call set_reg_style

mov cx, 3 ; 3 iterations
unsigned_loop:
mov ax, 0 ; clear the registers for visibility
mov bx, 0
mov dx, 0
call show_regs
call get_unsigned ; first number to ax
call show_regs
push ax ; temporarily save ax
call get_unsigned ; second number to bx
mov bx, ax
pop ax ; get ax back
mov dx, ax ; copy of ax to dx
add dx, bx ; dx (=ax) + bx
call show_regs_and_wait
loop unsigned_loop

; - - - CODE

First, we set AX, BX, and DX for the appropriate register style.
Here it is unsigned full register. We then put 3 in CX so we can
have 3 iterations with loop. Upon entering the loop, AX, BX, and
DX are cleared for reasons of visibility. We don't want the
screen cluttered up with numbers. Get_unsigned gets a two byte
unsigned number and returns it in AX. We want the first number to
be visually on the top (which is AX), but there is a problem
here. In order to get the second number we need to call
get_unsigned again, and it is going to put another number in AX.
We need to temporarily store the first number while we bring in
the second number and transfer it to bx.

There is a special 8086 instruction to do this, it is called
PUSH. Push temporarily stores a word. The word can be either a
full register or a word (two bytes) in memory. You can have
either:

variable1 dw 10000

push ax
push variable1





The PC Assembler Tutor 34
______________________

These are stored in a special place called the stack which we
will talk about much later. When you want it back, you use the
instruction POP. POP gets back the LAST thing that you pushed
onto the stack. Things come off the stack in REVERSE order of how
they were put on.

push variable1
push variable2
push variable3
push variable4
pop variable4
pop variable3
pop variable2
pop variable1

is the correct order. This is used for temporary storage only,
and the only thing which is accessable is the last thing which
you PUSHed on the stack.

We push AX to store it temporarily, call get_unsigned again and
transfer the number to BX. We then pop AX to get the number back.
The situation now is: the first number is in AX, the second
number is in BX. For the actual addition, we transfer AX to DX
and then add DX and BX. AX and BX contain the two numbers, and DX
contains the result. Then you must press ENTER to continue. LOOP
will jump to 'unsigned_loop' two times. The third time it will
fall through to the next section of code.

This program illustrates a hallmark of assembler code. It
normally takes scads of code just to do something simple.

Assemble add1.asm and link it with asmhelp.obj. Run it:

******************** SCREEN SHOT ******************************

AX 17428 SI 00000
BX 19755 DI 00000
CX 00003 BP 00000
DX 37183 SP 00508

CS 0AA5H DS 0A55H ES 0A25H SS 0A35H IP 004DH

OF DF IEF TF SF ZF AF PF CF
x + x - E COUNT 00004
----------------------------------------------------------------

The PC Assembler Helper Version 1.0
Copyright (C) 1989 Chuck Nelson All rights reserved.
Enter a number from 0 to 65535 17428
Enter a number from 0 to 65535 19755
Press ENTER to continue

*****************************************************************

This is the screen after the first addition. I have added 17428
(AX) and 19755 (BX). The result 37183 is in DX. CX is still 3
because it hasn't LOOPed yet.




Chapter 5 - Addition and Subtraction 35
____________________________________


Notice that even though it is the same assembler instruction:

add

in all four blocks of code, it is doing both signed and unsigned
addition correctly. When you are doing signed addition, you want
to look at OF, the overflow flag, SF, the sign flag, and ZF, the
zero flag after each addition to see how they are set. When you
do unsigned addition, you want to look at CF, the carry flag, and
ZF, the zero flag to see how they are set. Play around with this
for a while, and then it is time for the next program.

As in all 8086 instructions, the order is:

add destination, source

We add both numbers, and put the result in the destination, the
thing on the left.


There are five different types of addition you can do, (just as
there are five different types of moves). They are:

1. add two registers
2. add a register to a variable (memory)
3. add a variable (memory) to a register
4. add a constant to a variable (memory)
5. add a constant to a register

Here's a program that does all 5 things. Use template.asm to make
this program. template.asm is almost the same as the other ones
we have used. It has a few changes. First, it now lists ALL the
subroutines you can call in asmhelp.obj.{1} Appendix 1
(\APPENDIX\APP1.DOC) contains a description of all the
subroutines, what they do, and how they are called. Second, the
size of STACKSEG is larger. We don't need this large of a stack
now; it is for later. Finally, there is a section:

; + + + + + + + + + + PUT SUBROUTINES BELOW THIS LINE

; + + + + + + + + + + PUT SUBROUTINES ABOVE THIS LINE

for subroutines. Ignore this. This is for later.


From now on, we will always use template.asm unless it is
explicitly stated that something else is being used. Here's the
program:

template.asm
; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE
____________________

1 This does not change the size of the .EXE file by even
one byte, but it adds a lot of information to the .OBJ file, so
they are much larger.




The PC Assembler Tutor 36
______________________

variable1 dw ?
variable2 dw ?
; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE

; + + + + + + + + + + + + + + + START CODE BELOW THIS LINE
call show_regs
outer_loop:
call get_unsigned ; first number to ax
push ax ; store ax
call get_unsigned ; second number to bx
mov bx, ax
pop ax ; restore ax
mov variable1, ax ; first number to variable1
mov variable2, bx ; second number to variable2
; add 2 registers
mov cx, ax ; cx + bx
add cx, bx
; add register to memory
add variable1, bx
mov dx, variable1 ; put in dx for display
; add memory to register
mov si, ax
add si, variable2
; add a constant to memory
add variable2, 25
mov di, variable2 ; put in di for display
; add a constant to a register
mov bp, bx
add bp, 25
call show_regs
jmp outer_loop

; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE


The program puts the first number in AX and the second number in
BX. It then proceeds to do the same addition (first number plus
second number) three times. These are:

1. CX = add two registers (CX + BX)
2. DX = add a register to memory (variable1 + BX)
3. SI = add memory to a register (SI + variable2)

Finally it adds a constant (second number + 25). These are:

4. DI = add a constant to memory (variable2 + 25)
5. BP = add a constant to a register (BP + 25)


On the 8086, it is not possible to add two things in memory. That
is:

add variable1, variable2

is an illegal instruction. Instead, you need to write:

mov ax, variable2




Chapter 5 - Addition and Subtraction 37
____________________________________

add variable1, ax


SUBTRACTION

It is now time to do some subtraction. The instruction is SUB:

sub destination, source

subtracts source from destination and stores it in destination,
the thing on the left.

sub ax, cx ; (ax - cx) -> ax

In order to do subtraction we are going to modify add1.asm, so
make a copy and call it sub1.asm:

>copy add1.asm sub1.asm

How many instructions do we need to change to modify the program?
Four.

add dx, bx -> sub dx, bx
add dl, bl -> sub dl, bl

Each of these is changed twice, and we are ready to roll.
Assemble it, link it, and run it. Once again we want to look at
the flags at the end of each subtraction. For unsigned
subtraction, look at ZF, the zero flag, and CF, the carry flag.
This time, CF will be set if the result is below zero. For signed
subtraction, look at OF, the overflow flag, SF, the sign flag,
and ZF, the zero flag. As with addition, subtraction changes PF,
the parity flag and AF the auxillary flag. They don't concern us.

As with addition, there are five possibilities for subtraction.
They are:

1. subtract one register from another
2. subtract a register from a variable (memory)
3. subtract a variable (memory) from a register
4. subtract a constant from a variable (memory)
5. subtract a constant from a register

the code for these is:

sub cx, bx ; (cx - bx) -> cx
sub variable1, bx ; (variable1 - bx) -> variable1
sub si, variable2 ; (si - variable2) -> si
sub variable2, 25 ; (variable2 - 25) -> variable2
sub bp, 25 ; (bp - 25) -> bp

You can copy add2.asm to sub2.asm if you want and change the five
ADD instructions to SUB instructions. It will then do those five
types of subtraction.





The PC Assembler Tutor 38
______________________

SIGNED AND UNSIGNED NUMBERS

What should you do if you are doing unsigned addition or
subtraction and the carry flag gets set? It depends. Sometimes it
makes a difference, sometimes it doesn't. If you have an error
handling routine, then you can call it with the following code:

add ax, bx
jnc go_on
call error_handler
go_on:

JC and JNC are conditional jump instructions. JC (jump on carry)
jumps if the carry flag is set (1) and JNC (jump on not carry)
jumps if the carry flag is not set (0). Using reverse logic here,
we skip the error handler if everything is ok.

For signed numbers, it is certainly an error if there is
overflow. You are making mathematical calculations and you now
have invalid data. One possibility is to do the same as above but
with the overflow flag.

add ax, bx
jno go_on
call error_handler
go_on:

JO and JNO are two more conditional jump instructions. JO (jump
on overflow) jumps if the overflow flag is set (1) and JNO (jump
on not overflow) jumps if the overflow flag is not set (0). We
use the same logic here.

However, there is one special instruction for signed numbers, and
that is INTO (interrupt on overflow). It is possible to have an
error handler external to your program. It sits permanantly in
memory. When you make a signed arithmetic error, INTO interrupts
your program and goes to the external error handler. The code
looks like this:

add ax, bx
into

You probably don't have an error handler in your computer right
now. In that case, INTO simply goes looking for it and returns
when it can't find it.

Let's find out if you have an error handler installed. Once
again, use template.asm

; + + + + + + + + + + + + + + + START CODE BELOW THIS LINE
mov ax_byte, 1 ; signed register style
mov bx_byte, 1
mov cx_byte, 1
lea ax, ax_byte
call set_reg_style
call show_regs



Chapter 5 - Addition and Subtraction 39
____________________________________

outer_loop:
call get_signed
push ax
call get_signed
mov bx, ax
pop ax
mov cx, ax
add cx, bx
into
call show_regs
jmp outer_loop
; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE

This is basically the same thing as before, but using AX, BX, and
CX. They are set for signed style, and then we get two signed
numbers and add them. The result is in CX. Right after the
addition instruction is INTO. If the result is too positive or
too negative, OF will be set and INTO will look for the error
handler.

Assemble this program and link it with asmhelp.obj. Try both
numbers that do not set the overflow flag and numbers that do set
the overflow flag. Did anything different happen when the
overflow flag was set? If nothing different happened, you don't
have an error handler for INTO.

Included on the disks is an error handler. It is called INTO.COM,
and it's pathname is \XTRAFILE\INTO.COM. When you run it:

>into

it will install itself and then return to the command prompt:

>

INTO.COM will stay in memory until you reboot or shut off the
machine. INTO.COM provides the type of sophisticated error
handling that you might want to use in a real program. Install
(run) INTO.COM, and then try the previous program again, both
with numbers that cause an overflow and numbers that don't cause
an overflow.


















The PC Assembler Tutor 40
______________________

SUMMARY


ADD performs both signed and unsigned addition. It can:

1. add two registers
2. add a register to a variable (memory)
3. add a variable (memory) to a register
4. add a constant to a variable (memory)
5. add a constant to a register


SUB performs both signed and unsigned subtraction. It can:

1. subtract one register from another
2. subtract a register from a variable (memory)
3. subtract a variable (memory) from a register
4. subtract a constant from a variable (memory)
5. subtract a constant from a register


The flags affected by both ADD and SUB are:

CF the carry flag (for unsigned). Set if the 0/65535
(0/255) border was crossed.

ZF the zero flag (for signed and unsigned). Set if the
result is 0.

SF the sign flag (for signed). Set if the result is
negative.

OF the overflow flag (for signed). Set if the result was
too negative or too positive.

PF the parity flag and AF, the auxillary flag

The following jump instructions are conditional on the setting of
the flags:

JC jump on carry, JNC, jump on not carry
JO jump on overflow. JNO, jump on not overflow

LOOP
LOOP decrements cx by 1. If cx is then not zero, it jumps to
the named label. If cx is zero, it falls through to the next
instruction.

INTO
If the overflow flag is set, INTO (interrupt on overflow)
interrupts the program and goes to an external error handler
if one exists. It returns immediately if one doesn't exist.

PUSH and POP
PUSH stores either a register or a word (in memory) in a
temporary storage area. POP retrieves the last word PUSHed.