Your style of writing assembly language programs is almost as important as your accuracy. Good habits in layout, selection of symbolic names, and appropriate and illuminating comments help you to program correctly and easily. Good programming habits also make your programs much easier for your TA to read and grade. Here are two examples of the same NASM program to illustrate good and poor programming styles:
; MP99
; Sharon Sharp
; 02-23-2001
;
; This program copies the word contents of list1 to list2
; Enter with:
; SI = offset of List1
; DI = offset of List2
;
; Assumes:
; Both lists are in DS
; List end marker = 0
;
SEGMENT code
GLOBAL LstCpy
LstCpy
push ax ; AX used for temp
.lp:
mov ax, [si] ; fetch word and test for zero
test ax, ax
jz .exit
mov [di], ax
add si, 2 ; advance and recycle
add di, 2
jmp .lp
.exit:
pop ax
ret
Here the symbols help to define the program structure and the variables. The comments are inserted only where relevant and give functional rather than trivial instruction explanations. The open-spacing and orderly structure of the program also suggests the various parts. In addition, the listing to be handed in has a useful title.
The following program is a different kettle of fish:
segment code
cc
PUSH ax
global cc
A: mov ax,[si]
CMP AX,0
je b ;
mov [di],ax; move ax to list
add si,2; incr si
ADD di,2
jmp a
b: pop ax
ret
This is a fairly extreme example of muddling up a program. The variables are hard to identify; the comments are thoroughly mixed with the instructions and only explain instructions not their function. Amazingly enough, NASM will still assemble this example to a working program, but it is almost incomprehensible to humans, including your teaching assistants.
Here is a complete program written to illustrate good programming style:
; Example Program
; M. C. Loui
; 29 June 1986
;
; Translated to NASM
; Peter L. B. Johnson
; 2001
;
; This boring program illustrates good programming style. The user
; types a list of names separated by carriage returns; if the user
; presses ESC before the return, then that name is canceled. The
; program uses the extremely inefficient bubble sort algorithm to
; sort the names into lexicographic order and prints them out.
CR equ 0Dh
LF equ 0Ah
BEL equ 07h
ESCKEY equ 1Bh
MAXNUM equ 20 ; Maximum number of names
MAXLEN equ 80 ; Maximum name length
EXTERN kbdine, dspout, dspmsg, dosxit
SEGMENT stkseg STACK CLASS=STACK ; **** STACK SEGMENT ****
resb 64*8
stacktop:
SEGMENT code ; **** CODE SEGMENT ****
Array times MAXNUM*MAXLEN db '$' ; Array of names
NumNam dw 0 ; Number of names input
HdMsg db 'Type up to 20 names separated by carriage returns','$'
..start:
mov ax, cs
mov ds, ax ; Initialize DS and ES registers
mov es, ax
mov ax, stkseg ; Initialize Stack Segment
mov ss, ax
mov sp, stacktop ; Initialize Stack Pointer
mov dx, HdMsg
call dspmsg
call InNames ; Input the names
call Sort ; Sort them
call OutNames ; Output the names
call dosxit
;
; Subroutine InNames
; Reads in list of up to 20 names from the user and stores them in Array.
; Output: NumNam - Number of names typed in
; Calls: GetOne
;
InNames
push bx
mov word [NumNam], 0 ; Initialize NumNam
mov bx, Array ; BX points to next name
.lp:
call GetOne ; Input one name
cmp byte [GetStat], 0 ; Done if user typed only
je .done ; a carriage return
add bx, MAXLEN ; Point to next name
inc word [NumNam]
cmp word [NumNam], MAXNUM ; Continue only if
jl .lp ; NumNam < MAXNUM
.done:
pop bx
ret
;
; Subroutine GetOne
; Reads in one name of up to MAXLEN characters into Array.
; Input: BX - Offset of name in Array
; Output: GETSTAT - Status of call
; = 1 if normal return
; = 0 if user typed only carriage return
; Calls: dspmsg, kbdine, dspout
;
GetStat resb 1
Prompt db CR,LF,':$'
GetOne
push si
push dx
push ax
.lp1:
xor si, si
mov dx, Prompt ; Prompt user for name
call dspmsg
.lp2:
call kbdine ; Get next character into AL
cmp al, CR
je .cr
cmp al, ESCKEY
je .esc
mov byte [bx+si], al ; Store character
inc si
jmp short .lp2
.esc:
mov dl, BEL ; If user typed ESC
call dspout ; then ring bell
jmp short .lp1 ; and restart this name
.cr:
mov byte [GetStat], 1 ; If user typed only CR
test si, si
jnz .exit
mov byte [GetStat], 0 ; then set GetStat to 0
.exit:
pop ax
pop dx
pop si
ret
;
; Subroutine Sort
; Sorts Array using the bubble sort algorithm.
; WARNING: This algorithm is extremely inefficient.
; Input: NumNam - Number of names in Array
; Calls: CmpNam, XChgNam
;
I resw 1 ; Index into Array
J resw 1 ; Index into Array
Len resw 1 ; Length of each name
Sort
push ax
push dx
push si
push di
mov word [Len], MAXLEN
mov ax, [NumNam] ; Initialize I
dec ax ; to NumNam - 1
mov [I], ax ; for I=N-1 down to 1 do
.loopi:
cmp word [I], 1
jl .exit
mov word [J], 1 ; for J=1 to I do
.loopj:
mov ax, [J]
cmp ax, [I] ; (cannot cmp [J],[I])
jg .deci
dec ax ; compute offset of
mul word [Len] ; Jth name
add ax, Array
mov si, ax ; SI = offset of Jth name
mov di, ax
add di, [Len] ; DI = offset of J+1st name
call CmpNam
cmp byte [Result], 0 ; if Jth name > J+1st name
jle .incj
call XChgNam ; then exchange them
.incj:
inc word [J]
jmp short .loopj ; end for
.deci:
dec word [I]
jmp short .loopi ; end for
.exit:
pop di
pop si
pop dx
pop ax
ret
;
; Subroutine CmpNam
; Compares two names and determines which is lexicographically larger.
; Inputs: SI, DI - pointers to two names
; Outputs: Result = -1 if name at SI is before name at DI
; = 0 if name at SI equals name at DI
; = 1 if name at SI is after name at DI
;
Result resb 1
CmpNam
push ax
push cx
push si
push di
mov cx, MAXLEN ; Length of names
.lp:
mov al, [si] ; Compare next bytes
cmp al, [di]
ja .a
jb .b
inc si
inc di
loop .lp
mov byte [Result], 0 ; If all bytes equal, then names
jmp short .exit ; equal.
.a:
mov byte [Result], 1 ; Here if name at SI before name at
jmp short .exit ; DI.
.b:
mov byte [Result], -1 ; Here if name at SI after name at
.exit: ; DI.
pop di
pop si
pop cx
pop ax
ret
;
; Subroutine XChgNam
; Exchanges the names pointed to by SI and DI.
; Inputs: SI, DI - pointers to two names
;
XChgNam
push ax
push cx
push si
push di
mov cx, MAXLEN
.lp:
mov al, [si] ; Exchange bytes pointed to
xchg al, [di] ; by SI and DI
xchg al, [si]
inc si
inc di
loop .lp
pop di
pop si
pop cx
pop ax
ret
;
; Subroutine OutNames
; Prints out names from Array
; Inputs: NumNam - Number of names in Array
; Calls: dspmsg
;
CRLF db CR, LF, '$'
OutNames
push bx
push cx
push dx
mov cx, [NumNam] ; Number of names to output
mov bx, Array ; BX holds offset of next name
.lp:
mov dx, bx
call dspmsg
mov dx, CRLF ; CR and LF to separate
call dspmsg ; adjacant names
add bx, MAXLEN
loop .lp
pop dx
pop cx
pop bx
ret