18.6 File I/O

Just filling memory with constant values isn't very interesting (or useful). It's far more useful to be able to load in data from an external file: graphics being the most obvious example. However, data such as maps, precalculated function tables, and even executable code can be loaded from disk. The library itself loads executable code from disk for the graphics driver.

The library has a set of general file handling functions that make opening, closing, reading, and writing files much easier. The OpenFile() function takes a pointer to (the address of) the filename to open, and returns an integer handle, which identifies the file for all of the other file functions. It is therefore possible to have multiple files open at the same time, but be aware that there is a limit on the maximum number of open files, so it's smart to have as few open at the same time as possible: when loading multiple files, open, read, and close one before loading the next.

As the library has a specialized set of functions for loading graphics files, it's wise to use those instead of the generic file functions for loading graphics files. We'll use those when we cover high-resolution graphics using PModeLib.

Example 18-4. File I/O

%include "lib291.inc"

GLOBAL _main

mapsize equ 512*512     ; 512x512 map

SECTION .bss    ; Uninitialized data

mapoff  resd 1  ; Offset of the map data

SECTION .data   ; Initialized data

mapfn   db "mymap.dat",0        ; file to load data from (notice 0-terminated)

SECTION .text

_main
        push    esi              ; Save registers

        call    _LibInit         ; You could use invoke here, too
        test    eax, eax         ; Check for error (nonzero return value)
        jnz     near .initerror

        ; Allocate memory for map
        invoke  _AllocMem, dword mapsize
        cmp     eax, -1          ; Check for error (-1 return value)
        je      near .error
        mov     [mapoff], eax    ; Save offset

        ; Open file for reading
        invoke  _OpenFile, dword mapfn, word 0
        cmp     eax, -1          ; Check for error (-1 return value)
        je      near .error
        mov     esi, eax         ; EAX will get overwritten by ReadFile so save

        ; Read mapsize bytes from the file.
        ; Note the indirection for the address of the buffer.
        invoke  _ReadFile, esi, dword [mapoff], dword mapsize
        cmp     eax, mapsize     ; Check to see if we actually read that much
        jne     .error

        ; Close the file
        invoke  _CloseFile, esi

.error:
        call    _LibExit
.initerror:
        pop     esi              ; Restore registers
        ret                      ; Return to DOS