The Art of
ASSEMBLY LANGUAGE PROGRAMMING

Chapter Nine (Part 7)

Table of Content

Chapter Ten 

CHAPTER NINE:
ARITHMETIC AND LOGICAL OPERATIONS (Part 8)
9.9 - Sample Programs
9.9.1 - Converting Arithmetic Expressions to Assembly Language
9.9.2 - Boolean Operations Example
9.9.3 - 64-bit Integer I/O
9.9.4 - Packing and Unpacking Date Data Types
9.9 Sample Programs

This chapter's sample programs demonstrate several important concepts including extended precision arithmetic and logical operations arithmetic expression evaluation boolean expression evaluation and packing/unpacking data.

9.9.1 Converting Arithmetic Expressions to Assembly Language

The following sample program (Pgm9_1.asm on the companion CD-ROM) provides some examples of converting arithmetic expressions into assembly language:

; Pgm9_1.ASM
;
; Several examples demonstrating how to convert various
; arithmetic expressions into assembly language.

.xlist
include         stdlib.a
includelib      stdlib.lib
.list


dseg            segment para public 'data'

; Arbitrary variables this program uses.

u               word    ?
v               word    ?
w               word    ?
x               word    ?
y               word    ?

dseg            ends



cseg            segment para public 'code'
assume  cs:cseg
ds:dseg


; GETI- Reads an integer variable from the user and returns its
;       its value in the AX register.

geti            textequ <call _geti>
_geti           proc
push    es
push    di

getsm
atoi
free

pop     di
pop     es
ret
_geti           endp


Main            proc
mov     ax
dseg
mov     ds
ax
mov     es
ax
meminit


print
byte    "Abitrary expression program"
cr
lf
byte    "---------------------------"
cr
lf
byte    lf
byte    "Enter a value for u: "
0

geti
mov     u
ax

print
byte    "Enter a value for v: "
0
geti
mov     v
ax

print
byte    "Enter a value for w: "
0
geti
mov     w
ax

print
byte    "Enter a non-zero value for x: "
0
geti
mov     x
ax

print
byte    "Enter a non-zero value for y: "
0
geti
mov     y
ax


; Okay
compute Z := (X+Y)*(U+V*W)/X and print the result.

print
byte    cr
lf
byte    "(X+Y) * (U+V*W)/X is "
0

mov     ax
v           ;Compute V*W
imul    w               ; and then add in
add     ax
u           ; U.
mov     bx
ax          ;Save in a temp location for now.

mov     ax
x           ;Compute X+Y
multiply this
add     ax
y           ; sum by the result above

imul    bx              ; and then divide the whole
idiv    x               ; thing by X.

puti
putcr

; Compute ((X-Y*U) + (U*V) - W)/(X*Y)

print
byte    "((X-Y*U) + (U*V) - W)/(X*Y) = "
0

mov     ax
y           ;Compute y*u first
imul    u
mov     dx
X           ;Now compute X-Y*U
sub     dx
ax
mov     cx
dx          ;Save in temp

mov     ax
u           ;Compute U*V
imul    V
add     cx
ax          ;Compute (X-Y*U) + (U*V)

sub     cx
w           ;Compute ((X-Y*U) + (U*V) - W)

mov     ax
x           ;Compute (X*Y)
imul    y

xchg    ax
cx
cwd                     ;Compute NUMERATOR/(X*Y)
idiv    cx

puti
putcr



Quit:           ExitPgm                 ;DOS macro to quit program.
Main            endp

cseg            ends

sseg            segment para stack 'stack'
stk             byte    1024 dup ("stack   ")
sseg            ends

zzzzzzseg       segment para public 'zzzzzz'
LastBytes       byte    16 dup (?)
zzzzzzseg       ends
end     Main

9.9.2 Boolean Operations Example

The following sample program (Pgm9_2.asm on the companion CD-ROM) demonstrates how to manipulate boolean values in assembly language. It also provides an example of Demorgan's Theorems in operation.

; Pgm9_2.ASM
;
; This program demonstrates DeMorgan's theorems and
; various other logical computations.


.xlist
include         stdlib.a
includelib      stdlib.lib
.list


dseg            segment para public 'data'


; Boolean input variables for the various functions
; we are going to test.

a               byte    0
b               byte    0


dseg            ends



cseg            segment para public 'code'
assume  cs:cseg
ds:dseg


; Get0or1-      Reads a "0" or "1" from the user and returns its
;               its value in the AX register.

get0or1         textequ <call _get0or1>
_get0or1        proc
push    es
push    di

getsm
atoi
free

pop     di
pop     es
ret
_get0or1        endp




Main            proc
mov     ax
dseg
mov     ds
ax
mov     es
ax
meminit


print
byte    "Demorgan's Theorems"
cr
lf
byte    "-------------------"
cr
lf
byte    lf
byte    "According to Demorgan's theorems
all results "
byte    "between the dashed lines"
cr
lf
byte    "should be equal."
cr
lf
byte    lf
byte    "Enter a value for a: "
0

get0or1
mov     a
al

print
byte    "Enter a value for b: "
0
get0or1
mov     b
al


print
byte    "---------------------------------"
cr
lf
byte    "Computing not (A and B): "
0

mov     ah
0
mov     al
a
and     al
b
xor     al
1           ;Logical NOT operation.

puti
putcr

print
byte    "Computing (not A) OR (not B): "
0
mov     al
a
xor     al
1
mov     bl
b
xor     bl
1
or      al
bl
puti

print
byte    cr
lf
byte    "---------------------------------"
cr
lf
byte    "Computing (not A) OR B: "
0
mov     al
a
xor     al
1
or      al
b
puti

print
byte    cr
lf
byte    "Computing not (A AND (not B)): "
0
mov     al
b
xor     al
1
and     al
a
xor     al
1
puti

print
byte    cr
lf
byte    "---------------------------------"
cr
lf
byte    "Computing (not A) OR B: "
0
mov     al
a
xor     al
1
or      al
b
puti

print
byte    cr
lf
byte    "Computing not (A AND (not B)): "
0
mov     al
b
xor     al
1
and     al
a
xor     al
1
puti

print
byte    cr
lf
byte    "---------------------------------"
cr
lf
byte    "Computing not (A OR B): "
0
mov     al
a
or      al
b
xor     al
1
puti

print
byte    cr
lf
byte    "Computing (not A) AND (not B): "
0
mov     al
a
xor     al
1
and     bl
b
xor     bl
1
and     al
bl
puti

print
byte    cr
lf
byte    "---------------------------------"
cr
lf
byte    0




Quit:           ExitPgm                 ;DOS macro to quit program.
Main            endp

cseg            ends

sseg            segment para stack 'stack'
stk             byte    1024 dup ("stack   ")
sseg            ends

zzzzzzseg       segment para public 'zzzzzz'
LastBytes       byte    16 dup (?)
zzzzzzseg       ends
end     Main

9.9.3 64-bit Integer I/O

This sample program (Pgm9_3.asm on the companion CD-ROM) shows how to read and write 64-bit integers. It provides the ATOU64 and PUTU64 routines that let you convert a string of digits to a 64-bit unsigned integer and output a 64-bit unsigned integer as a decimal string to the display.

; Pgm9_3.ASM
;
; This sample program provides two procedures that read and write
; 64-bit unsigned integer values on an 80386 or later processor.

.xlist
include         stdlib.a
includelib      stdlib.lib
.list

.386
option  segment:use16

dp              textequ <dword ptr>
byp             textequ <byte ptr>

dseg            segment para public 'data'

; Acc64 is a 64 bit value that the ATOU64 routine uses to input
; a 64-bit value.

Acc64           qword   0



; Quotient holds the result of dividing the current PUTU value by
; ten.

Quotient        qword   0


; NumOut holds the string of digits created by the PUTU64 routine.

NumOut          byte    32 dup (0)



; A sample test string for the ATOI64 routine:

LongNumber      byte    "123456789012345678"
0


dseg            ends

cseg            segment para public 'code'
assume  cs:cseg
ds:dseg


; ATOU64-       On entry
ES:DI point at a string containing a
;               sequence of digits.  This routine converts that
;               string to a 64-bit integer and returns that
;               unsigned integer value in EDX:EAX.
;
;               This routine uses the algorithm:
;
;               Acc := 0
;               while digits left
;
;                       Acc := (Acc * 10) + (Current Digit - '0')
;                       Move on to next digit
;
;               endwhile


ATOU64          proc    near
push    di              ;Save because we modify it.
mov     dp Acc64
0     ;Initialize our accumulator.
mov     dp Acc64+4
0

; While we've got some decimal digits
process the input string:

sub     eax
eax        ;Zero out eax's H.O. 3 bytes.
WhileDigits:    mov     al
es:[di]
xor     al
'0'         ;Translates '0'..'9' -> 0..9
cmp     al
10          ; and everything else is > 9.
ja      NotADigit

; Multiply Acc64 by ten.  Use shifts and adds to accomplish this:

shl     dp Acc64
1     ;Compute Acc64*2
rcl     dp Acc64+4
1

push    dp Acc64+4      ;Save Acc64*2
push    dp Acc64

shl     dp Acc64
1     ;Compute Acc64*4
rcl     dp Acc64+4
1
shl     dp Acc64
1     ;Compute Acc64*8
rcl     dp Acc64+4
1

pop     edx             ;Compute Acc64*10 as
add     dp Acc64
edx   ; Acc64*2 + Acc64*8
pop     edx
adc     dp Acc64+4
edx

; Add in the numeric equivalent of the current digit.
; Remember
the H.O. three words of eax contain zero.

add     dp Acc64
eax   ;Add in this digit

inc     di              ;Move on to next char.
jmp     WhileDigits     ;Repeat for all digits.

; Okay
return the 64-bit integer value in eax.

NotADigit:      mov     eax
dp Acc64
mov     edx
dp Acc64+4
pop     di
ret
ATOU64          endp






; PUTU64-       On entry
EDX:EAX contain a 64-bit unsigned value.
;               Output a string of decimal digits providing the
;               decimal representation of that value.
;
;               This code uses the following algorithm:
;
;                   di := 30;
;                   while edx:eax <> 0 do
;
;                       OutputNumber[di] := digit;
;                       edx:eax := edx:eax div 10
;                       di := di - 1;
;
;                   endwhile
;                   Output digits from OutNumber[di+1]
;                       through OutputNumber[30]

PUTU64          proc
push    es
push    eax
push    ecx
push    edx
push    di
pushf



mov     di
dseg        ;This is where the output
mov     es
di          ; string will go.
lea     di
NumOut+30   ;Store characters in string
std                     ; backwards.
mov     byp es:[di+1]
0 ;Output zero terminating byte.


; Save the value to print so we can divide it by ten using an
; extended precision division operation.

mov     dp Quotient
eax
mov     dp Quotient+4
edx


; Okay
begin converting the number into a string of digits.

mov     ecx
10                 ;Value to divide by.
DivideLoop:     mov     eax
dp Quotient+4      ;Do a 64-bit by
sub     edx
edx                ; 32-bit division
div     ecx                     ; (see the text
mov     dp Quotient+4
eax      ;  for details).

mov     eax
dp Quotient
div     ecx
mov     dp Quotient
eax

; At this time edx (dl
actually) contains the remainder of the
; above division by ten
so dl is in the range 0..9.  Convert
; this to an ASCII character and save it away.

mov     al
dl
or      al
'0'
stosb

; Now check to see if the result is zero.  When it is
we can
; quit.

mov     eax
dp Quotient
or      eax
dp Quotient+4
jnz     DivideLoop

OutputNumber:   inc     di
puts
popf
pop     di
pop     edx
pop     ecx
pop     eax
pop     es
ret
PUTU64          endp



; The main program provides a simple test of the two routines
; above.

Main            proc
mov     ax
dseg
mov     ds
ax
mov     es
ax
meminit

lesi    LongNumber
call    ATOU64
call    PutU64
printf
byte    cr
lf
byte    "%x %x %x %x"
cr
lf
0
dword   Acc64+6
Acc64+4
Acc64+2
Acc64


Quit:           ExitPgm                 ;DOS macro to quit program.
Main            endp

cseg            ends

sseg            segment para stack 'stack'
stk             byte    1024 dup ("stack   ")
sseg            ends

zzzzzzseg       segment para public 'zzzzzz'
LastBytes       byte    16 dup (?)
zzzzzzseg       ends
end     Main

9.9.4 Packing and Unpacking Date Data Types

This sample program demonstrates how to pack and unpack data using the Date data type introduced in Chapter One.

; Pgm9_4.ASM
;
;       This program demonstrates how to pack and unpack
;       data types.  It reads in a month
day
and year value.
;       It then packs these values into the format the textbook
;       presents in chapter two.  Finally
it unpacks this data
;       and calls the stdlib DTOA routine to print it as text.

.xlist
include         stdlib.a
includelib      stdlib.lib
.list


dseg            segment para public 'data'

Month           byte    ?       ;Holds month value (1-12)
Day             byte    ?       ;Holds day value (1-31)
Year            byte    ?       ;Holds year value (80-99)

Date            word    ?       ;Packed data goes in here.

dseg            ends



cseg            segment para public 'code'
assume  cs:cseg
ds:dseg


; GETI- Reads an integer variable from the user and returns its
;       its value in the AX register.

geti            textequ <call _geti>
_geti           proc
push    es
push    di

getsm
atoi
free

pop     di
pop     es
ret
_geti           endp


Main            proc
mov     ax
dseg
mov     ds
ax
mov     es
ax
meminit


print
byte    "Date Conversion Program"
cr
lf
byte    "-----------------------"
cr
lf
byte    lf
0


; Get the month value from the user.
; Do a simple check to make sure this value is in the range
; 1-12.  Make the user reenter the month if it is not.

GetMonth:       print
byte    "Enter the month (1-12): "
0

geti
mov     Month
al
cmp     ax
0
je      BadMonth
cmp     ax
12
jbe     GoodMonth
BadMonth:       print
byte    "Illegal month value
please re-enter"
cr
lf
0
jmp     GetMonth

GoodMonth:


; Okay
read the day from the user.  Again
do a simple
; check to see if the date is valid.  Note that this code
; only checks to see if the day value is in the range 1-31.
; It does not check those months that have 28
29
or 30
; day months.

GetDay:         print
byte    "Enter the day (1-31): "
0
geti
mov     Day
al
cmp     ax
0
je      BadDay
cmp     ax
31
jbe     GoodDay
BadDay:         print
byte    "Illegal day value
please re-enter"
cr
lf
0
jmp     GetDay

GoodDay:


; Okay
get the year from the user.
; This check is slightly more sophisticated.  If the user
; enters a year in the range 1980-1999
it will automatically
; convert it to 80-99.  All other dates outside the range
; 80-99 are illegal.

GetYear:        print
byte    "Enter the year (80-99): "
0
geti
cmp     ax
1980
jb      TestYear
cmp     ax
1999
ja      BadYear

sub     dx
dx          ;Zero extend year to 32 bits.
mov     bx
100
div     bx              ;Compute year mod 100.
mov     ax
dx
jmp     GoodYear

TestYear:       cmp     ax
80
jb      BadYear
cmp     ax
99
jbe     GoodYear

BadYear:        print
byte    "Illegal year value.  Please re-enter"
cr
lf
0
jmp     GetYear

GoodYear:       mov     Year
al


; Okay
take these input values and pack them into the following
; 16-bit format:
;
;      bit 15     8 7      0
;          |      | |      |
;          MMMMDDDD DYYYYYYY


mov     ah
0
mov     bh
ah
mov     al
Month       ;Put Month into bit positions
mov     cl
4           ; 12..15
ror     ax
cl

mov     bl
Day         ;Put Day into bit positions
mov     cl
7           ; 7..11.
shl     bx
cl

or      ax
bx          ;Create MMMMDDDD D0000000
or      al
Year        ;Create MMMMDDDD DYYYYYYY
mov     Date
ax        ;Save away packed date.

; Print out the packed date (in hex):

print
byte    "Packed date = "
0
putw
putcr

; Okay
the following code demonstrates how to unpack this date
; and put it in a form the standard library's LDTOAM routine can
; use.

mov     ax
Date        ;First
extract Month
mov     cl
4
shr     ah
cl
mov     dh
ah          ;LDTOAM needs month in DH.

mov     ax
Date        ;Next get the day.
shl     ax
1
and     ah
11111b
mov     dl
ah          ;Day needs to be in DL.

mov     cx
Date        ;Now process the year.
and     cx
7fh         ;Strip all but year bits.

print
byte    "Date: "
0
LDTOAM                  ;Convert to a string
puts
free
putcr




Quit:           ExitPgm                 ;DOS macro to quit program.
Main            endp

cseg            ends

sseg            segment para stack 'stack'
stk             byte    1024 dup ("stack   ")
sseg            ends

zzzzzzseg       segment para public 'zzzzzz'
LastBytes       byte    16 dup (?)
zzzzzzseg       ends
end     Main

Chapter Nine (Part 7)

Table of Content

Chapter Ten 

Chapter Nine: Arithmetic And Logical Operations (Part 8)
27 SEP 1996