The Art of
ASSEMBLY LANGUAGE PROGRAMMING

Chapter Thirteen (Part 10)

Table of Content

Chapter Fourteen  

CHAPTER THIRTEEN:
MS-DOS PC-BIOS AND FILE I/O (Part 11)
13.4.10 - A File I/O Example
13.5 - Sample Program

13.4.10 A File I/O Example

The following piece of code puts everything together from the last several sections. This is a short program which adds line numbers to a text file. This program expects two command line parameters: an input file and an output file. It copies the input file to the output file while appending line numbers to the beginning of each line in the output file. This code demonstrates the use of argc argv the Standard Library file I/O routines and I/O redirection.

; FILEIO
;
; This program copies the input file to the output file and adds line
; numbers while it is copying the file.

include         stdlib.a
includelib      stdlib.lib


dseg            segment para public 'data'

ArgCnt          word    0
LineNumber      word    0
DOSErrorCode    word    0
InFile          dword   ?               ;Ptr to Input file name.
OutFile         dword   ?               ;Ptr to Output file name.
InputLine       byte    1024 dup (0)    ;Input/Output data buffer.
OutputFile      FileVar {}
InputFile       FileVar {}

dseg            ends


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

; ReadLn- Reads a line of text from the input file and stores the
;         data into the InputLine buffer:

ReadLn          proc
push    ds
push    es
push    di
push    si
push    ax

mov     si
dseg
mov     ds
si
mov     si
offset InputLine
lesi    InputFile

GetLnLp:
fgetc
jc      RdLnDone                ;If some bizzarre error.
cmp     ah
0                   ;Check for EOF.
je      RdLnDone                ;Note:carry is set.
mov     ds:[si]
al
inc     si
cmp     al
lf                  ;At EOLN?
jne     GetLnLp
dec     si                      ;Back up before LF.
cmp     byte ptr ds:[si-1]
cr  ;CR before LF?
jne     RdLnDone
dec     si                      ;If so
skip it too.

RdLnDone:       mov     byte ptr ds:[si]
0     ;Zero terminate.
pop     ax
pop     si
pop     di
pop     es
pop     ds
ret
ReadLn          endp

; MyOutput- Writes the single character in AL to the output file.

MyOutput        proc    far
push    es
push    di
lesi    OutputFile
fputc
pop     di
pop     es
ret
MyOutput        endp



; The main program which does all the work:

Main            proc
mov     ax
dseg
mov     ds
ax
mov     es
ax

; Must call the memory manager initialization routine if you use
; any routine which calls malloc!  ARGV is a good example of a
; routine calls malloc.

meminit

; We expect this program to be called as follows:
;               fileio file1
file2
; anything else is an error.

argc
cmp     cx
2           ;Must have two parameters.
je      Got2Parms
BadParms:       print
byte    "Usage: FILEIO infile
outfile"
cr
lf
0
jmp     Quit

; Okay
we've got two parameters
hopefully they're valid filenames.
; Get copies of the filenames and store away the pointers to them.

Got2Parms:      mov     ax
1           ;Get the input filename
argv
mov     word ptr InFile
di
mov     word ptr InFile+2
es

mov     ax
2           ;Get the output filename
argv
mov     word ptr OutFile
di
mov     word ptr OutFile+2
es

; Output the filenames to the standard output device

printf
byte    "Input file: %^s\n"
byte    "Output file: %^s\n"
0
dword   InFile
OutFile

; Open the input file:

lesi    InputFile
mov     dx
word ptr InFile+2
mov     si
word ptr InFile
mov     ax
0
fopen
jnc     GoodOpen
mov     DOSErrorCode
ax
printf
byte    "Could not open input file
DOS: %d\n"
0
dword   DOSErrorCode
jmp     Quit

; Create a new file for output:

GoodOpen:       lesi    OutputFile
mov     dx
word ptr OutFile+2
mov     si
word ptr OutFile
fcreate
jnc     GoodCreate
mov     DOSErrorCode
AX
printf
byte    "Could not open output file
DOS: %d\n"
0
dword   DOSErrorCode
jmp     Quit

; Okay
save the output hook and redirect the output.

GoodCreate:     PushOutAdrs
lesi    MyOutput
SetOutAdrs


WhlNotEOF:      inc     LineNumber

; Okay
read the input line from the user:

call    ReadLn
jc      BadInput

; Okay
redirect the output to our output file and write the last line
; read prefixed with a line number:

printf
byte    "%4d:   %s\n"
0
dword   LineNumber
InputLine
jmp     WhlNotEOF


BadInput:       push    ax              ;Save error code.
PopOutAdrs              ;Restore output hook.
pop     ax              ;Retrieve error code.
test    ax
ax          ;EOF error? (AX = 0)
jz      CloseFiles
mov     DOSErrorCode
ax
printf
byte    "Input error
DOS: %d\n"
0
dword   LineNumber

; Okay
close the files and quit:

CloseFiles:     lesi    OutputFile
fclose
lesi    InputFile
fclose

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

cseg            ends



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


;zzzzzzseg is required by the standard library routines.

zzzzzzseg       segment para public 'zzzzzz'
LastBytes       db      16 dup (?)
zzzzzzseg       ends
end     Main
13.5 Sample Program

If you want to use the Standard Library's output routines (putc print printf etc.) to output data to a file you can do so by manually redirecting the output before and after each call to these routines. Unfortunately this can be a lot of work if you mix interactive I/O with file I/O. The following program presents several macros that simplify this task for you.

; FileMacs.asm
;
; This program presents a set of macros that make file I/O with the
; Standard Library even easier to do.
;
; The main program writes a multiplication table to the file "MyFile.txt".

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


dseg            segment para public 'data'

CurOutput       dword   ?

Filename        byte    "MyFile.txt"
0

i               word    ?
j               word    ?

TheFile         filevar {}

dseg            ends


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


; For-Next macros from Chapter Eight.
; See Chapter Eight for details on how this works.

ForLp           macro   LCV
Start
Stop
local   ForLoop

ifndef  $$For&LCV&
$$For&LCV&      =       0
else
$$For&LCV&      =       $$For&LCV& + 1
endif

mov     ax
Start
mov     LCV
ax

ForLoop         textequ @catstr($$For&LCV&
%$$For&LCV&)
&ForLoop&:
mov     ax
LCV
cmp     ax
Stop
jg      @catstr($$Next&LCV&
%$$For&LCV&)
endm



Next            macro   LCV
local   NextLbl
inc     LCV
jmp     @catstr($$For&LCV&
%$$For&LCV&)
NextLbl         textequ @catstr($$Next&LCV&
%$$For&LCV&)
&NextLbl&:
endm


; File I/O macros:
;
;
; SetPtr sets up the CurOutput pointer variable.  This macro is called
; by the other macros
it's not something you would normally call directly.
; Its whole purpose in life is to shorten the other macros and save a little
; typing.

SetPtr          macro   fvar
push    es
push    di

mov     di
offset fvar
mov     word ptr CurOutput
di
mov     di
seg fvar
mov     word ptr CurOutput+2
di

PushOutAdrs
lesi    FileOutput
SetOutAdrs
pop     di
pop     es
endm
;
;
;
; fprint-       Prints a string to the display.
;
; Usage:
;               fprint  filevar
"String or bytes to print"
;
; Note: you can supply optional byte or string data after the string above by
;       enclosing the data in angle brackets
e.g.

;
;               fprint  filevar
<"string to print"
cr
lf>
;
; Do *NOT* put a zero terminating byte at the end of the string
the fprint macro
; will do that for you automatically.

fprint          macro   fvar:req
string:req
SetPtr  fvar

print
byte    string
byte    0

PopOutAdrs
endm

; fprintf-      Prints a formatted string to the display.
; fprintff-     Like fprintf
but handles floats as well as other items.
;
; Usage:
;               fprintf  filevar
"format string"
optional data values
;               fprintff filevar
"format string"
optional data values
; Examples:
;
;       fprintf  FileVariable
"i=%d
j=%d\n"
i
j
;       fprintff FileVariable
"f=%8.2f
i=%d\n"
f
i
;
; Note: if you want to specify a list of strings and bytes for the format string

;       just surround the items with an angle bracket
e.g.

;
;       fprintf FileVariable
<"i=%d
j=%d"
cr
lf>
i
j
;
;

fprintf         macro   fvar:req
FmtStr:req
Operands:vararg
setptr  fvar

printf
byte    FmtStr
byte    0

for     ThisVal
<Operands>
dword   ThisVal
endm

PopOutAdrs
endm

fprintff        macro   fvar:req
FmtStr:req
Operands:vararg
setptr  fvar

printff
byte    FmtStr
byte    0

for     ThisVal
<Operands>
dword   ThisVal
endm

PopOutAdrs
endm


; F-    This is a generic macro that converts stand-alone (no code stream parameters)
;       stdlib functions into file output routines.  Use it with putc
puts
puti

;       putu
putl
putisize
putusize
putlsize
putcr
etc.
;
; Usage:
;
;       F       StdLibFunction
FileVariable
;
; Examples:
;
;       mov     al
'A'
;       F       putc
TheFile
;       mov     ax
I
;       mov     cx
4
;       F       putisize
TheFile


F               macro   func:req
fvar:req
setptr  fvar
func
PopOutAdrs
endm

; WriteLn- Quick macro to handle the putcr operation (since this code calls putcr
; so often).

WriteLn         macro   fvar:req
F       putcr
fvar
endm


; FileOutput- Writes the single character in AL to an output file.
; The macros above redirect the standard output to this routine
; to print data to a file.

FileOutput      proc    far
push    es
push    di
push    ds
mov     di
dseg
mov     ds
di

les     di
CurOutput
fputc

pop     ds
pop     di
pop     es
ret
FileOutput      endp


; A simple main program that tests the code above.
; This program writes a multiplication table to the file "MyFile.txt"

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

; Rewrite(TheFile
FileName);

ldxi    FileName
lesi    TheFile
fcreate

; writeln(TheFile);
; writeln(TheFile
'    ');
; for i := 0 to 5 do write(TheFile
'|'
i:4
' ');
; writeln(TheFile);

WriteLn TheFile
fprint  TheFile
"    "

forlp   i
0
5
fprintf TheFile
"|%4d "
i
next    i
WriteLn TheFile

; for j := -5 to 5 do begin
;
;       write(TheFile
'----');
;       for i := 0 to 5 do write(TheFile
'+-----');
;       writeln(TheFile);
;
;       write(j:3
' |');
;       for i := 0 to 5 do write(i*j:4
' |);
;       writeln(TheFile);
;
; end;

forlp   j
-5
5

fprint  TheFile
"----"
forlp   i
0
5
fprintf TheFile
"+-----"
next    i
fprint  TheFile
<"+"
cr
lf>

fprintf TheFile
"%3d |"
j

forlp   i
0
5

mov     ax
i
imul    j
mov     cx
4
F       putisize
TheFile
fprint  TheFile
" |"

next    i
Writeln TheFile

next    j
WriteLn TheFile

; Close(TheFile);

lesi    TheFile
fclose


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

cseg            ends

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

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

Chapter Thirteen (Part 10)

Table of Content

Chapter Fourteen  

Chapter Thirteen: MS-DOS PC-BIOS and File I/O (Part 11)
28 SEP 1996