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