The Art of
ASSEMBLY LANGUAGE PROGRAMMING

Chapter Thirteen (Part 5)

Table of Content

Chapter Thirteen (Part 7) 

CHAPTER THIRTEEN:
MS-DOS, PC-BIOS AND FILE I/O (Part 6)
13.3.8 - MS-DOS "New" Filing Calls
13.3.8.1 - Open File
13.3.8.2 - Create File
13.3.8.3 - Close File
13.3.8.4 - Read From a File
13.3.8.5 - Write to a File
13.3.8.6 - Seek (Move File Pointer)
13.3.8.7 - Set Disk Transfer Address (DTA)
13.3.8.8 - Find First File
13.3.8.9 - Find Next File
13.3.8.10 - Delete File
13.3.8.11 - Rename File
13.3.8.12 - Change/Get File Attributes
13.3.8.13 - Get/Set File Date and Time
13.3.8.14 - Other DOS Calls

13.3.8 MS-DOS "New" Filing Calls

Starting with DOS v2.0, Microsoft introduced a set of file handling procedures which (finally) made disk file access bearable under MS-DOS. Not only bearable, but actually easy to use! The following sections describe the use of these commands to access files on a disk drive.

File commands which deal with filenames (Create, Open, Delete, Rename, and others) are passed the address of a zero-terminated pathname. Those that actually open a file (Create and Open) return a file handle as the result (assuming, of course, that there wasn't an error). This file handle is used with other calls (read, write, seek, close, etc.) to gain access to the file you've opened. In this respect, a file handle is not unlike a file variable in Pascal. Consider the following Microsoft/Turbo Pascal code:

        program DemoFiles; var  F:TEXT;
        begin
                assign(f,'FileName.TXT');
                rewrite(f);
                writeln(f,'Hello there');
                close(f);
        end. 

The file variable "f" is used in this Pascal example in much the same way that a file handle is used in an assembly language program - to gain access to the file that was created in the program.

All the following DOS filing commands return an error status in the carry flag. If the carry flag is clear when DOS returns to your program, then the operation was completed successfully. If the carry flag is set upon return, then some sort of error has occurred and the AX register contains the error number. The actual error return values will be discussed along with each function in the following sections.

13.3.8.1 Open File

Function (ah):  3Dh
Entry parameters:
                al- file access value
                        0- File opened for reading 
                        1- File opened for writing
                        2- File opened for reading and writing
                ds:dx- Point at a zero terminated string containing the filename.
Exit parameters:        
                If the carry is set, ax contains one of the following error codes:
                        2- File not found 
                        4- Too many open files
                        5- Access denied 
                        12- Invalid access
        
                If the carry is clear, 
                        ax contains the file handle value assigned by DOS.

A file must be opened before you can access it. The open command opens a file that already exists. This makes it quite similar to Pascal's Reset procedure. Attempting to open a file that doesn't exist produces an error. Example:

                lea     dx, Filename    ;Assume DS points at segment
                mov     ah, 3dh         ; of filename
                mov     al, 0           ;Open for reading.
                int     21h
                jc      OpenError
                mov     FileHandle, ax

If an error occurs while opening a file, the file will not be opened. You should always check for an error after executing a DOS open command, since continuing to operate on the file which hasn't been properly opened will produce disastrous consequences. Exactly how you handle an open error is up to you, but at the very least you should print an error message and give the user the opportunity to specify a different filename.

If the open command completes without generating an error, DOS returns a file handle for that file in the ax register. Typically, you should save this value away somewhere so you can use it when accessing the file later on.

13.3.8.2 Create File

Function (ah):  3Ch
Entry parameters:
                ds:dx- Address of zero terminated pathname
                cx- File attribute
Exit parameters:
                If the carry is set, ax contains one of the following error codes:
                 3- Path not found      
                 4- Too many open files
                 5- Access denied
                If the carry is clear, 
                 ax is returned containing the file handle

Create opens a new file for output. As with the OPEN command, ds:dx points at a zero terminated string containing the filename. Since this call creates a new file, DOS assumes that you're opening the file for writing only. Another parameter, passed in cx, is the initial file attribute settings. The L.O. six bits of cx contain the following values:

Bit     Meaning if equal to one 
0       File is a Read-Only file
1       File is a hidden file 
2       File is a system file
3       File is a volume label name
4       File is a subdirectory
5       File has been archived

In general, you shouldn't set any of these bits. Most normal files should be created with a file attribute of zero. Therefore, the cx register should be loaded with zero before calling the create function.

Upon exit, the carry flag is set if an error occurs. The "Path not found" error requires some additional explanation. This error is generated, not if the file isn't found (which would be most of the time since this command is typically used to create a new file), but if a subdirectory in the pathname cannot be found.

If the carry flag is clear when DOS returns to your program, then the file has been properly opened for output and the ax register contains the file handle for this file.

13.3.8.3 Close File

Function (ah):  3Eh
Entry parameters:       
                bx- File Handle
Exit parameters:        
                If the carry flag is set, 
                 ax contains 6, the only possible error, 
                 which is an invalid handle error.

This call is used to close a file opened with the Open or Create commands above. It is passed the file handle in the bx register and, assuming the file handle is valid, closes the specified file.

You should close all files your program uses as soon as you're through with them to avoid disk file corruption in the event the user powers the system down or resets the machine while your files are left open.

Note that quitting to DOS (or aborting to DOS by pressing control-C or control-break) automatically closes all open files. However, you should never rely on this feature since doing so is an extremely poor programming practice.

13.3.8.4 Read From a File

Function (ah):    3Fh
Entry parameters:
                  bx- File handle 
                  cx- Number of bytes to read
                  ds:dx- Array large enough to hold bytes read
Exit parameters:        
                  If the carry flag is set, 
                  ax contains one of the following error codes
                  5- Access denied
                  6- Invalid handle
                  If the carry flag is clear, 
                  ax contains the number of bytes actually read from the file.

The read function is used to read some number of bytes from a file. The actual number of bytes is specified by the cx register upon entry into DOS. The file handle, which specifies the file from which the bytes are to be read, is passed in the bx register. The ds:dx register contains the address of a buffer into which the bytes read from the file are to be stored.

On return, if there wasn't an error, the ax register contains the number of bytes actually read. Unless the end of file (EOF) was reached, this number will match the value passed to DOS in the cx register. If the end of file has been reached, the value return in ax will be somewhere between zero and the value passed to DOS in the cx register. This is the only test for the EOF condition.

Example: This example opens a file and reads it to the EOF

                mov     ah, 3dh         ;Open the file
                mov     al, 0           ;Open for reading
                lea     dx, Filename    ;Presume DS points at filename
                int     21h             ; segment.
                jc      BadOpen
                mov     FHndl, ax       ;Save file handle

LP:             mov     ah,3fh          ;Read data from the file
                lea     dx, Buffer      ;Address of data buffer
                mov     cx, 1           ;Read one byte
                mov     bx, FHndl       ;Get file handle value
                int     21h
                jc      ReadError
                cmp     ax, cx          ;EOF reached?
                jne     EOF
                mov     al, Buffer      ;Get character read
                putc                    ;Print it
                jmp     LP              ;Read next byte

EOF:            mov     bx, FHndl
                mov     ah, 3eh         ;Close file
                int     21h
                jc      CloseError

This code segment will read the entire file whose (zero-terminated) filename is found at address "Filename" in the current data segment and write each character in the file to the standard output device using the UCR StdLib putc routine. Be forewarned that one-character-at-a-time I/O such as this is extremely slow. We'll discuss better ways to quickly read a file a little later in this chapter.

13.3.8.5 Write to a File

Function (ah):    40h
Entry parameters:       
                  bx- File handle
                  cx- Number of bytes to write
                  ds:dx- Address of buffer containing data to write
Exit parameters:        
                  If the carry is set, 
                  ax contains one of the following error codes
                  5- Accessed denied 
                  6- Invalid handle
                  If the carry is clear on return, 
                  ax contains the number of bytes actually written to the file. 

This call is almost the converse of the read command presented earlier. It writes the specified number of bytes at ds:dx to the file rather than reading them. On return, if the number of bytes written to the file is not equal to the number originally specified in the cx register, the disk is full and this should be treated as an error.

If cx contains zero when this function is called, DOS will truncate the file to the current file position (i.e., all data following the current position in the file will be deleted).

3.3.8.6 Seek (Move File Pointer)

Function (ah):  42h Entry parameters:
                al- Method of moving
                 0- Offset specified is from the beginning of the file. 
                 1- Offset specified is distance from the current file pointer.
                 2- The pointer is moved to the end of the file minus the specified offset.
                bx- File handle. 
                cx:dx- Distance to move, in bytes.
Exit parameters:        
                If the carry is set, 
                 ax contains one of the following error codes
                  1- Invalid function 
                  6- Invalid handle
                If the carry is clear, 
                 dx:ax contains the new file position

This command is used to move the file pointer around in a random access file. There are three methods of moving the file pointer, an absolute distance within the file (if al=0), some positive distance from the current file position (if al=1), or some distance from the end of the file (if al=2). If AL doesn't contain 0, 1, or 2, DOS will return an invalid function error. If this call is successfully completed, the next byte read or written will occur at the specified location.

Note that DOS treats cx:dx as an unsigned integer. Therefore, a single seek command cannot be used to move backwards in the file. Instead, method #0 must be used to position the file pointer at some absolute position in the file. If you don't know where you currently are and you want to move back 256 bytes, you can use the following code:

                mov     ah, 42h         ;Seek command
                mov     al, 1           ;Move from current location
                xor     cx, cx          ;Zero out CX and DX so we
                xor     dx, dx          ; stay right here
                mov     bx, FileHandle
                int     21h
                jc      SeekError
                sub     ax, 256         ;DX:AX now contains the
                sbb     dx, 0           ; current file position, so
                mov     cx, dx          ; compute a location 256 
                mov     dx, ax          ; bytes back.
                mov     ah, 42h
                mov     al, 0           ;Absolute file position
                int     21h             ;BX still contains handle.
13.3.8.7 Set Disk Transfer Address (DTA)
Function (ah):  1Ah Entry parameters:
                ds:dx- Pointer to DTA
Exit parameters:        
                None

This command is called "Set Disk Transfer Address" because it was (is) used with the original DOS v1.0 file functions. We wouldn't normally consider this function except for the fact that it is also used by functions 4Eh and 4Fh (described next) to set up a pointer to a 43-byte buffer area. If this function isn't executed before executing functions 4Eh or 4Fh, DOS will use the default buffer space at PSP:80h.

13.3.8.8 Find First File

Function (ah):    4Eh
Entry parameters:       
                  cx- Attributes 
                  ds:dx- Pointer to filename
Exit parameters:        
                  If carry is set, 
                  ax contains one of the following error codes
                   2- File not found 
                  18- No more files 

The Find First File and Find Next File (described next) functions are used to search for files specified using ambiguous file references. An ambiguous file reference is any filename containing the "*" and "?" wildcard characters. The Find First File function is used to locate the first such filename within a specified directory, the Find Next File function is used to find successive entries in the directory.

Generally, when an ambiguous file reference is provided, the Find First File command is issued to locate the first occurrence of the file, and then a loop is used, calling Find Next File, to locate all other occurrences of the file within that loop until there are no more files (error #18). Whenever Find First File is called, it sets up the following information at the DTA:

Offset  Description 
0       Reserved for use by Find Next File 
21      Attribute of file found
22      Time stamp of file
24      Date stamp of file
26      File size in bytes
30      Filename and extension (zero terminated)

(The offsets are decimal)

Assuming Find First File doesn't return some sort of error, the name of the first file matching the ambiguous file description will appear at offset 30 in the DTA.

Note: if the specified pathname doesn't contain any wildcard characters, then Find First File will return the exact filename specified, if it exists. Any subsequent call to Find Next File will return an error.

The cx register contains the search attributes for the file. Normally, cx should contain zero. If non-zero, Find First File (and Find Next File) will include file names which have the specified attributes as well as all normal file names.

13.3.8.9 Find Next File

Function (ah):  4Fh
Entry parameters:       
                none
Exit parameters:        
                If the carry is set, 
                then there aren't any more files and 
                ax will be returned containing 18.

The Find Next File function is used to search for additional file names matching an ambiguous file reference after a call to Find First File. The DTA must point at a data record set up by the Find First File function.

Example: The following code lists the names of all the files in the current directory that end with ".EXE". Presumably, the variable "DTA" is in the current data segment:

                mov     ah, 1Ah         ;Set DTA
                lea     dx, DTA
                int     21h
                xor     cx, cx          ;No attributes.
                lea     dx, FileName
                mov     ah, 4Eh         ;Find First File
                int     21h
                jc      NoMoreFiles     ;If error, we're done
DirLoop:        lea     si, DTA+30      ;Address of filename
                cld
PrtName:        lodsb
                test    al, al          ;Zero byte?
                jz      NextEntry
                putc                    ;Print this character
                jmp     PrtName

NextEntry:      mov     ah, 4Fh         ;Find Next File
                int     21h
                jnc     DirLoop         ;Print this name
13.3.8.10 Delete File
Function (ah):  41h
Entry parameters:       
                ds:dx- Address of pathname to delete
Exit parameters:        
                If carry set, 
                 ax contains one of the following error codes
                  2- File not found
                  5- Access denied

This function will delete the specified file from the directory. The filename must be an unambiguous filename (i.e., it cannot contain any wildcard characters).

13.3.8.11 Rename File

Function (ah):  56h Entry parameters:
                ds:dx- Pointer to pathname of existing file 
                es:di- Pointer to new pathname
Exit parameters:        
                If carry set, 
                 ax contains one of the following error codes
                   2- File not found 
                   5- Access denied
                  17- Not the same device

This command serves two purposes: it allows you to rename one file to another and it allows you to move a file from one directory to another (as long as the two subdirectories are on the same disk).

Example: Rename "MYPGM.EXE" to "YOURPGM.EXE"

; Assume ES and DS both point at the current data segment
; containing the filenames.

                lea     dx, OldName 
                lea     di, NewName
                mov     ah, 56h
                int     21h
                jc      BadRename 
                         . 
                         . 
                         .
OldName         byte    "MYPGM.EXE",0
NewName         byte    "YOURPGM.EXE",0

Example #2: Move a filename from one directory to another:

; Assume ES and DS both point at the current data segment
; containing the filenames.

                lea     dx, OldName
                lea     di, NewName
                mov     ah, 56h
                int     21h
                jc      BadRename
                         .
                         .
                         .
OldName         byte    "\DIR1\MYPGM.EXE",0
NewName         byte    "\DIR2\MYPGM.EXE",0
13.3.8.12 Change/Get File Attributes
Function (ah):  43h
Entry parameters:       
                al- Subfunction code
                 0- Return file attributes in cx
                 1- Set file attributes to those in cx
                cx- Attribute to be set if AL=01
                 ds:dx- address of pathname
Exit parameters:        
                If carry set, 
                 ax contains one of the following error codes:
                  1- Invalid function
                  3- Pathname not found
                  5- Access denied
                If the carry is clear and the subfunction was zero,
                 cx will contain the file's attributes.

This call is useful for setting/resetting and reading a file's attribute bits. It can be used to set a file to read-only, set/clear the archive bit, or otherwise mess around with the file attributes.

13.3.8.13 Get/Set File Date and Time

Function (ah):  57h
Entry parameters:       
                al- Subfunction code
                 0- Get date and time
                 1- Set date and time
                bx- File handle
                cx- Time to be set (if AL=01)
                dx- Date to be set (if AL=01)
Exit parameters:        
                If carry set, ax contains one of the following error codes
                 1- Invalid subfunction
                 6- Invalid handle
                If the carry is clear, 
                 cx/dx is set to the time/date if al=00

This call sets the "last-write" date/time for the specified file. The file must be open (using open or create) before using this function. The date will not be recorded until the file is closed.

13.3.8.14 Other DOS Calls

The following tables briefly list many of the other DOS calls. For more information on the use of these DOS functions consult the Microsoft MS-DOS Programmer's Reference or the MS-DOS Technical Reference.

Miscellaneous DOS File Functions
Function #(AH) Input
Parameters
Output
Parameters
Description
39h ds:dx- pointer to zero terminated pathname. - Create Directory: Creates a new directory with the specified name.
3Ah ds:dx- pointer to zero terminated pathname. - Remove Directory: Deletes the directory with the specified pathname. Error if directory is not empty or the specified directory is the current directory.
3Bh ds:dx- pointer to zero terminated pathname.

 
- Change Directory: Changes the default directory to the specified pathname.
45h bx- file handle ax- new handle Duplicate File Handle: creates a copy of a file handle so a program can access a file using two separate file variables. This allows the program to close the file with one handle and continue accessing it with the other.
46h bx- file handle

cx- duplicate handle

-

Force Duplicate File Handle: Like function 45h above, except you specify which handle (in cx) you want to refer to the existing file (specified by bx).
47h ds:si- pointer to buffer

dl- drive
- Get Current Directory: Stores a string containing the current pathname (terminated with a zero) starting at location ds:si. These registers must point at a buffer containing at least 64 bytes. The dl register specifies the drive number (0=default, 1=A, 2=B, 3=C, etc.).
5Ah cx- attributes

ds:dx- pointer to temporary path.
ax- handle Create Temporary File: Creates a file with a unique name in the directory specified by the zero terminated string at which ds:dx points. There must be at least 13 zero bytes beyond the end of the pathname because this function will store the generated filename at the end of the pathname. The attributes are the same as for the Create call.
5Bh cx- attributes

ds:dx- pointer to zero terminated pathname.
ax- handle Create New File: Like the create call, but this call insists that the file not exist. It returns an error if the file exists (rather than deleting the old file).
67h bx- handles - Set Maximum Handle Count: This function sets the maximum number of handles a program can use at any one given time.
68h bx- handle - Commit File: Flushes all data to a file without closing it, ensuring that the file's data is current and consistent.

 

Miscellaneous DOS Functions
Function
#(AH)
Input Parameters Output Parameters Description
25h al- interrupt # ds:dx- pointer to interrupt service routine.

-

Set Interrupt Vector: Stores the specified address in ds:dx into the interrupt vector table at the entry specified by the al register.
30h

-

al- major version ah- minor version bh- Version flag bl:cx- 24 bit serial number Get Version Number: Returns the current version number of DOS (or value set by SETVER).
33h al- 0 dl- break flag (0=off, 1=on) Get Break Flag: Returns the status of the DOS break flag. If on, MS-DOS checks for ctrl-C when processing any DOS command; if off, MS-DOS only checks on functions 1-0Ch.
33h al- 1 dl- break flag.

-

Set Break Flag: Sets the MS-DOS break flag according to the value in dl (see function above for details).
33h al- 6 bl- major version bh- minor version dl- revision dh- version flags Get MS-DOS Version: Returns the "real" version number, not the one set by the SETVER command. Bits three and four of the version flags are one if DOS is in ROM or DOS is in high memory, respectively.
34h

-

es:bx- pointer to InDOS flag. Get InDOS Flag Address: Returns the address of the InDOS flag. This flag helps prevent reentrancy in TSR applications
35h al- interrupt # es:bx- pointer to interrupt service routine. Get Interrupt Vector: Returns a pointer to the interrupt service routine for the specified interrupt number. See function 25h above for more details.
44h al- subcode Other parameters!

-

Device Control: This is a whole family of additional DOS commands to control various devices. See the DOS programmer's reference manual for more details.
4Dh

-

al- return value ah- termination method Get Child Program Return Value: Returns the last result code from a child program in al. The ah register contains the termination method, which is one of the following values: 0-normal, 1-ctrl-C, 2-critical device error, 3-terminate and stay resident.
50h bx- PSP address

-

Set PSP Address: Set DOS' current PSP address to the value specified in the bx register.
51h - bx- PSP address Get PSP Address: Returns a pointer to the current PSP in the bx register.
59h - ax- extended error bh- error class bl- error action ch- error location Get Extended Error: Returns additional information when an error occurs on a DOS call. See the DOS programmer's guide for more details on these errors and how to handle them.
5Dh al- 0Ah ds:si- pointer to extended error structure.

-

Set Extended Error: copies the data from the extended error structure to DOS' internal record.

In addition to the above commands, there are several additional DOS calls that deal with networks and international character sets. See the MS-DOS reference for more details.

Chapter Thirteen (Part 5)

Table of Content

Chapter Thirteen (Part 7) 

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