<< Home | About Forth | About TurboForth | Download | Language Reference | Resources | Tutorials | YouTube >>
TurboForth has a rich suite of file I/O words allowing it to work with floppy drives, hard drives, SD card readers, serial ports and parallel ports. The same facilities that are found in TI-BASIC and Extended Basic are made available in TurboForth, though, as one would expect, the way in which it is implemented is different. Nevertheless, the File I/O handling suite of words in TurboForth is quite easy to understand and work with. Working with files in Forth on the TI-99/4A is very specific to the TI-99/4A, due to the DSR (Device Service Routine) architecture of the machine, and the unique, feature-rich file-system that is in use on the 4A. The file system is device independant. Reading/writing etc. to, say, a floppy disk is no different to a hard disk, or a RAM disk, if the author of the DSR has done his job properly!
Wether reading or writing to files in TurboForth, the procedure is more or less the same:
Each of the above points above will be described in the following sections. Example code will also be given.
The very first step when using files in TurboForth is to define a file buffer. This is used throughout the life of the file. It's managed internally by TurboForth, and you don't need to know anything about the internals of the buffer to use files. Just treat them as a black-box.
File buffers are declared with the word FBUF:. Note that file buffers are named; you should give them a meaningful name. Once you have done so, you will use this name to reference the file. Here's an example:
FBUF: text-file
Simple enough. We've defined a file buffer called text-file. Whoop-de-doo.
Note: file buffers should be defined outside of colon definitions (because they reserve dictionary space).
So, we've defined a buffer but we can't do anything with it yet. We need to set a file name, tell it the file type, etc. We'll build that up in the next few sections.
File characteristics are defined using the word FILE. File expects a string as input, and that string will completely describe all the file characteristics to get us to a point where we can actually open and work with the file. We'll start with the file name, and introduce each attribute that is required in the string as we go.
S" DSK1.TEXTFILE DV80SI"
The above code sets up a string which defines the filename, and the file characteristics, or attributes (as I tend to call them). As can be seen, we're going to point at a file on disk drive 1 (DSK1) called TEXTFILE. But what do those attributes mean? Read on.
The TI file system supports the notion of display type files, and internal type files. I researched the differences, and, as far as I can tell, there's no difference, other than a single bit in the file entry record in the disk directory. I think the intention is that is the file is going to contain printable/displayable data, then you'd declare it as a Display type file when creating it, whereas if it's a binary type file (perhaps character set data, for example) then you'd declare it as an Internal type file. It's duly implemented in TurboForth. I tend to always use Display type files. The D in the string above declares that the file we are going to be working with is a Display type file. For Internal type files we'd use I.
Another characteristic of the file system is the record length of individual records. They can be variable length, or fixed length. Variable length records include a record length byte with each record, whereas fixed length records don't have a length byte (their length is fixed, afterall). V means variable length, and F means fixed length records. As can be seen in the example, we're declaring a Variable record length.
The file system also requires the length of each record in the file. For Variable type records, this represents the maximum length of a record; for Fixed type records, it is the record length.
When writing records to a variable length file, if the amount of data to be written exceeds the record size, the data will be written as two records. For example: if the file type is Display Variable with 80 byte record lengths, and you write a string of 100 characters, you'll create two records: One with a length of 80 characters, and one with a length of 20 characters. When reading this data back, you'll get two seperate records: One of 80 characters, and one of 20 characters.
As you might expect, writing records to a fixed-length record type is slightly different. If you write a string of 10 characters to a DF80 (display, fixed, 80 characters max. record length) then you'll actually create a record on the disk of 80 characters. If write a string of 100 characters to a DF80 file, you'll create two 80 character records in the file - they're fixed length records afterall!
In the example above, we're declaring a record length of 80 characters.
Files can be opened for Input only (I), Output only (O), Update (U) or Append (A) modes.
The file is input (read) only.
The file is output (write) only. If the file already exists, it will be overwritten with a new (empty) file.
The file is output (write) only. If the file already exists, and it is opened for Append Mode, any writes will be appended to the end of the file existing file.
The file can be both read from and written to.
Variable length type files can only be read from or written to sequentially. When writing, the first record to be written will be first record in the file. When reading, the records will be read back in the same order they were written.
Fixed length type records can be accessed in any order, both reading and writing. For example, a file could populated with records backwards (starting with the last record) if desired, and read back in any desired order.
The following sections give some actual examples of working with files in TurboForth.
FBUF: my-file \ first declare a file buffer and give it a name.
Type of file | Example code |
Display Variable 80, Sequential Input | S" DSK1.SOME-FILE DV80SI" my-file #OPEN |
Display Variable 80, Sequential Output | S" DSK1.SOME-FILE DV80SO" my-file #OPEN |
Display Fixed 80, Sequential Input | S" DSK1.SOME-FILE DF80SI" my-file #OPEN |
Display Fixed 80, Sequential Output | S" DSK1.SOME-FILE DF80SO" my-file #OPEN |
Display Fixed 128, Sequential Input | S" DSK1.SOME-FILE DF128SI" my-file #OPEN |
Display Fixed 128, Seqential Output | S" DSK1.SOME-FILE DF128SO" my-file #OPEN |
Display Fixed 128, Relative Input | S" DSK1.SOME-FILE DF128RI" my-file #OPEN |
Display Fixed 128, Relative Output | S" DSK1.SOME-FILE DF128RO" my-file #OPEN |
Display Fixed, 128, Relative Append | S" DSK1.SOME-FILE DF128RA" my-file #OPEN |
#OPEN pushes a flag to the stack. If the value returned by #OPEN is 0, then the file was opened successfully, if any other value, the file was not opened.
This allows ABORT" to be used immediately after #OPEN, as ABORT" requires a flag on the stack:
FBUF: my-file \ declare a file buffer
S" DSK1.SOME-FILE DV80SO" my-file FILE \ set up the file characteristics
my-file #OPEN ABORT" Could not open the file." \ try and open the file
In the example code above, if #OPEN fails, it will be caught by the word ABORT".
See section 13.14, File Attributes for a list of the attributes used to describe a file.
#PUT writes a string (a single record) to a file. As can be seen from the stack signature above, it requires the following stack inputs on the stack before being called:
#PUT has a single stack output - flag. If 0 #PUT successfully wrote to the file. If non-zero, an error occurred. This can be caught with ABORT" in the same way as with #OPEN.
FBUF: my-file
: write-hello ( -- )
S" DSK1.TESTFILE DV80SO" my-file FILE
my-file #OPEN ABORT" Could not open file"
S" Hello, World!" my-file #PUT ABORT" Could not write to file"
my-file #CLOSE
cr ." Successfully wrote to DSK1.TESTFILE"
;
#GET reads a record in from the file and writes it to memory. #GET has the following stack inputs:
#GET has a single stack output: flag. If zero, the record was read successfully. If non-zero, an error occurred. This can be caught with ABORT" in the same way as with #OPEN.
The length of the record that was actually read from the file is stored in the buffer with the record itself at address addr+0. This is a single byte value. The word COUNT may be used ( addr -- addr+1 len ) to convert from the address of the buffer to the address of the string in the buffer and fetch its length to the stack.
CREATE record-buf 82 chars allot
FBUF: my-file
: read-file ( -- )
S" DSK1.TESTFILE DV80SI" my-file FILE
my-file #OPEN ABORT" Could not open file"
record-buf my-file #GET ABORT" Could not read from the file"
my-file #CLOSE
cr ." Successfully read from DSK1.TESTFILE"
cr ." The record read was: "
record-buf COUNT TYPE CR
;
When reading a file, it is necessary to be able to detect the end of the file. #EOF? performs that function.
#EOF? only has a single stack input: The file-id. If the file is currently positioned at the end of the file then #EOF? will push TRUE to the stack, otherwise it pushes FALSE.
First, write some records:
create record-buf 80 chars ALLOT
FBUF: my-file
: writeOK? ( -- flag )
abort" Could not write to file." ;
: write-records ( -- )
S" DSK1.TESTFILE DV80SO" my-file FILE
my-file #OPEN ABORT" Could not open file"
S" Hello, World!" my-file #PUT writeOK?
S" This is a test!" my-file #PUT writeOK?
S" Testing testing 1 2 3!" my-file #PUT writeOK?
my-file #CLOSE
cr ." Successfully wrote to DSK1.TESTFILE"
;
write-records
Now, some code to read those records back, checking for end-of-file as it does so:
: read-records ( -- )
S" DSK1.TESTFILE DV80SI" my-file FILE
my-file #OPEN ABORT" Could not open file"
BEGIN my-file #EOF? NOT WHILE
record-buf my-file #GET ABORT" Could not read from the file"
cr record-buf count type
REPEAT
my-file #CLOSE
cr ." Successfully read from DSK1.TESTFILE"
;
read-records
Files are closed with #CLOSE. #CLOSE takes a single stack input - the file-id. It has no outputs. See the examples above for usage information.
The TI Disk Controller card, and some other storage systems (MYARC RAM Disk Controllers, MYARC Hard Disk Controllers etc.) support additional file access modes that permit much more powerful methods of file access.
When a file is opened in Append Mode, the file is write-only. However, when data is written to the file, the data is added to the end of the existing data in the file. If a non-existent file is opened in Append Mode (at least, on the TI Disk Controller, other controllers may have different behaviour) then the file is created (as an empty file, obviously) and the data written to the file as normal. Both Variable and Fixed record lengths are permitted with Append Mode.
EXAMPLE: TO DO
Update mode is an exteremly powerful file mode. Update mode permits random access to any record in the file, in either read or write mode. Only fixed length record types are permitted in this mode. It is used in conjunction with #REC (see 13.12.3, below) which sets the record number to access.
EXAMPLE: TO DO
Relative files are simply fixed record-length files, where any record can be accessed in any order, by setting the record to access by means of #REC. If the file is opened in the Update Mode (see 13.12.2, above) then the file may be read from, or written to, in any order. Very powerful.
EXAMPLE: TO DO
To do
To do
To do
To do
To do
The file attributes are summarised below for easy reference:
|
*Note that Internal type files require L - this is because I is used to specify INPUT mode.
Also, see the language refererence for a description of all the file related words.
<< Home | About Forth | About TurboForth | Download | Language Reference | Resources | Tutorials | YouTube >>