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