|Z88 Developers' Notes|
8. File Input / Output
The Z88 supports a fairly uniform device-independent I/O system, so although calls do exist wich will explicitly send data to, say, the screen or the serial port, for all operations other than reading from standard input and writing to standard output, it is usual to regard such devices as files and use a standard file I/O interface.
This section will cover file access for applications, while noting that the approach remains the same if device names are substituted for the filename, thus opening a stream to the device. Major pseudo-file are:
:SCR.0 The screen. Write only. :PRT.0 The printer. Write only. :COM.0 The serial interface :NUL.0 Null device. Output discarded, always end of file for input. :INP.0 Standard input. Read only. Normally the keyboard, but may be redirected by the CLI. :OUT.0 Standard output. Write only. Normally the screen, but again, may be redirected.Note that the terminating numbers are optional, ie. ':COM' is equivalent to ':COM.0'. As an example of how devices are accessed using file I/O look at this BBC BASIC program:
10 LF$ = CHR$10The program sends 'Hello' to the serial port. The extra line feed character is necessary as both <CR> and <LF> are necessary for a true newline when writing to an actual display device and BBC BASIC's PRINT# command only sends a <CR>.
20 X% = OPENOUT(":COM.0")
This can be broken into three basic stages:
The open file call must be provided with a filename and an access mode. The access mode is open for input, output or update, although other options are available for working with directories and DOR's. The call returns a handle which is unique to the particular access of the file, but note that more than one handle may be associated with any particular file. A file may be open for input from several source, but may only be opened for update or output by one source. An attempt to open a file for output or updating, which is already open for output or updating will result in an "In use" error, RC_USE. Other reasons for not being able to open files are poor filenames, read or write protection of a device, non-existence (in the case of opening for input or update), a lack of memory or lack of handles. There are only a finite supply of handles - about 150. The call used for opening files and is GN_Opf.2. Access
All file access is referenced by the file handle. Access to RAM files is random with a sequential pointer into the file which can be both read and written. Both byte by byte transfer, and transfer of whole blocks is possible. Calls for file access are OS_Pb (put byte), OS_Gb (get byte) and OS_Mv (move bytes between a file and memory). Timed versions of the byte transfer calls exists (OS_Pbt and OS_Gbt) for use with devices like the printer and serial port. Three file attributes can be read, and two written. PTR (pointer) is the file pointer and gives the location wthin the file. EXT (extent) gives the size of the file and finally EOF (end of file) is the read only attribute which is true when the file pointer is at the end of the file ie. when PTR = EXT. These attributes may be read using OS_Frm (file read miscellaneous) and written with OS_Fwm (file write miscellaneous).3. Closing
The close call needs only be supplied with the file handle. When closed, a file is free for updates from other sources, its handle is released, any memory used for buffers is freed, etc. The call used to close files and streams is GN_Cl.An open file may not be deleted and an attempt to do this will result in an "In use" (RC_USE) error. Note that because opening a file for output will attempt delete the file if it already exists, the "In use" error also occurs in this context.
Filenames come in two forms, only one of which is actually given a name. Ordinary, as it were, filenames can have as much or as little information as is required to uniquely identify a file. For example "fred.txt" is an ordinary filename, or even "fred.*", which will probably be sufficient to find the same file. An explicit filename, on the other hand, is one which includes all the information associated with the file, including it's full name, any directory within which it resides, and any in which that may reside, and finally the device where the file is stored. So we might have ":RAM.1/maindir/fred.txt" as an explicit filename.
Each part of the explicit filename divided by slashes (or backslashes)
is called a segment, so the above name consists of three segments which
are a device, a directory and a filename respectively. Segments will always
come in this order, but there may be several directories in the explicit
name. Each segment consists of a name which is up to twelve characters
long and an optional extension, separated from the name by a full stop,
which may be up to three characters long. All device names are preceeded
by a colon. The system provides various calls for breaking down and assembling
filenames in terms of segments, such as GN_Esa (read and write specific
segments), GN_Fex (produce explicit filename) and GN_Fcm (compress an explicit
filename). The open file call, GN_Opf, in the course of its operation produces
from the filename supplied an explicit filename which can be returned in
full, or in a cut down form, to the user. Other useful filename processing
calls are GN_Pfs (parse filename segment) and GN_Prs (parse filename),
which not only check for correct syntax, but also return information as
to what has been specified in the filename, eg. whether wildcards have
been used, or a specific device mentioned. The wildcard facilities are
covered later in "Wildcards".
The file transfer calls may be suspended in circumstances. If the stream
being used is associated either the serial port or printer then RC_SUSP
will be returned if the machine is switched off and on again, or if a battery
low interrupt occurs. If escape detection is enabled then <ESC> will
cause file transfer to abort and RC_ESC to be returned. A more serious
case is where the stream is associated with the keyboard. If this is the
situation, pre-emption can occur when reading from the stream and the possible
return codes will be RC_SUSP, RC_DRAW and RC_QUIT.
If a lot of data is to be transferred between memory and a stream then
doing the work a byte at a time can be quite a slow process. Each time
a byte is transferred with these calls, the operating system must be paged
in and various checks and action carried out, all of which generates a
considerable overhead. The OS_Mv call reduces this overhead by transferring
a large number of bytes between memory and a stream all in one go, and
so can operate much more quickly. It can be useful for operations like
loading and saving files.
GN_Opf Open a file, returns a file handle used for subsequent access GN_Cl Close a file or stream OS_Gb Get byte from file or stream OS_Gbt As OS_Gb but with timeout OS_Pb Put byte to file or stream OS_Pbt As OS_Pb but with timeout OS_Mv Move bytes from stream to memory, or from memory to stream OS_Frm Read file or stream parameters PTR, EXT and EOF OS_Fwm Write file parameters PTR and EXT GN_Esa Read and write filename segments GN_Fex Produce explicit filename GN_Fcm Compress explicit filename GN_Pfs Parse filename segment GN_Prs Parse filenameExample
; very simple example of opening and closing a file
; open a file and display full filename assumes that on entry
; HL points to filename and that DE points to a 40 byte buffer
; entry point is main
include "#fileio.def" ; file I/O definition calls, parameters include "#stdio.def" ; standard I/O definition calls, parameters include "#errors.def" ; error code definitions .main push de ; save DE for future reference ld b, 0 ; indicate HL a local address ld c, 40 ; size of expanded name buffer ld a, OP_IN ; open file for input call_oz(GN_Opf) ; open... jr nc, no_error call_oz(GN_Err) ; display error in error box pop de ; balance stack ret .no_error xor a ld (de),a ; terminate explicit filename ld hl, file_mess call_oz(GN_Sop) ; output constant string pop hl ; get start address of expanded name call_oz(GN_Sop) ; output expanded name call_oz(GN_Nln) ; newline call_oz(GN_Cl) ; now close the file .file_mess defm "Using: " & 0
|Output and the screen driver||File Input / Output||Input and the keyboard decoder|