Technical Reference Manual
This section describes the format of file-structured datapacks, then the system services available for file and record management. System services which access datapacks directly are discussed in chapter low level pack access.
In the following sections "datapack" means (E)EPROMs, ROMs on external devices, external RAM packs, and internal RAM (the device A:). The operating system handles the different device types in the same way apart from delete operations. All device types use the same record structure. Device dependencies will be pointed out as necessary.
Datapacks contain two types of records, preceded by either a byte or word length. This is to al low long records while minimising the overhead for text files which typically contain short records. The first record is always at byte $0A in the datapack. The record structure is terminated by a byte $FF.
Short records are of the format:
"HELLO" saved using SAVE. Record type is $90.
Note that the length byte does not include itself or the record type. The record length can not be zero. The maximum length is $FE, because a first byte $FF is used to terminate the record structure. The record type is in the range $81 to $FE inclusive. When writing to more than one file, the record type is used to identify which file each record belongs to, since the records may be written in any order.
Long records are used primarily for saving blocks of data which will not be regularly altered, which are intended to be loaded and saved in one piece. Long records are also used to contain information which is to be hidden from the file system.
Long records have the following format:
long record "HELLO"
null long record
Long records do not have a length byte as such, the $02 is purely for error handling (see below). The $80 is a special record type which identifies a long record.
On EPROM datapacks, short records are marked as deleted by clearing the top bit of the record type. On EPROMs long records are never deleted - this will be discussed later. On a RAM device, the space occupied by a record is recovered when the record is deleted, so there will not be any long records with a record type < $80 on either RAM or EPROM devices.
short record "AAA" of record type $91 on an EPROM
when deleted becomes:
There are two kinds of files; data files accessible to OPL (of which the file "MAIN" is an example), and block files such as OPL procedures. In future we will refer to the first kind simply as 'files'.
Each file has a name, which is a short record of type $81, and a number of data records which are short records all of the same record type in the range $90 to $FE. The name and data records of a file can be interspersed by any number of other records and files. Also the name need not come before the first record of a file.
Each file has data records of a unique record type, so there can be up to 111 files on a device. An attempt to create more files will produce the error "DIRECTORY FULL". A file may contain up to $FFFE data records, given a sufficiently large pack.
Filenames have the following format:
Example: the file MAIN (all records of type $90 will be in MAIN)
Filenames are always padded with spaces to make them eight bytes long. Byte 10 in the above diagram is the record type which will be used for all data records in the file. It can take values in the range $90 to $FE inclusive.
When a file is created, the operating system first searches through all the filenames on the datapack to find the lowest free record type and this is then allocated to the new file. The new record type is then stored in the new file name record. Since several files can be open at once, the records of a file can be mixed with any other type of records. No facilities are provided to allow reordering of records in a file, or to allow the insertion of records in the middle of a file.
When a file is deleted, all records of the appropriate type are deleted one by one, then the filename is deleted. On EPROMs the filename is deleted by overwriting the record byte (i.e. $81 in the above example) in the filename with $01.
The total overhead for each file is 11 bytes for the filename plus two bytes per record.
The top level commands FIND and SAVE work with short records of type $90 directly, not by opening the file MAIN. This means that if MAIN is deleted, problems can occur. SAVE will still save records of type $90, but these records can not be re-read by OPL. The record type $90 is 'reserved' for MAIN, so that no other file can have this record type. When blank packs are sized, the filename MAIN is created.
Note: User programs should never delete MAIN, since this will cause problems with certain OS versions. To create a new MAIN, delete all the records in a loop.
Block files are a special class of files with a name immediately followed by a single long data record. No records are allowed between the block filename and the long record. They are intended for storing data which will be re-saved as a whole each time the data is changed. Long records may be used alone without a preceding block filename - for example to enclose a machine code program on a datapack. Long records with no preceding block filename are ignored by the file system.
Format of a block file:
Example: block file ABCD of block type $83, containing four bytes of data.
The record type, byte 1 in the above diagram, is in the range $82-$8F inclusive. This byte is used to distinguish different classes of block files containing different kinds of information, and will be referred to as the block file type. Because block files have their data immediately after the filename they do not need a data record type, so byte 10, which was used as the data record type in ordinary filenames is not used in block files. This byte is reserved by Psion. Note also that the number of block files on a device is limited only by the space available, and there can be up to fourteen different kinds of block files with types $82 to $8F.
On EPROMS, a block file is deleted by clearing the top bit in the record type byte of the name (byte 1, the $83 in the above example). The total overhead for each block file is 15 bytes.
OPL procedures are saved as block files of type $83. The data block has the following format:
The length of the Q-code may be zero, if the procedure was saved with the SAVE option in the TRAN SAVE QUIT sub-menu or was received from PC. The length of the source may be zero if the procedure has been copied object-only. Each line of the source is terminated by a zero.
The DIR option in the PROG sub-menu works by finding all type $83 records.
These are block files of type $82. Its data block consists of a list of entries which is terminated by a zero byte. Each entry has the form:
The alarm flag is 0 for no alarm, otherwise one more than the number of minutes early the alarm has to go off.
On LZ machines the diary is no longer saved as a block file of type $82, but instead as an ordinary data file. Each entry is saved as a separate record.
The first field specifies the date of the entry (1989, 05, 08, for 8th May 1989), the time (17 15, for 5:15pm), the duration (02) and the alarm byte (16, for 15 minutes before - again, 00 would mean "no alarm"). The second field is the text of the entry. The order of things in the first field, and the use of zeroes where necessary to keep things the same length (e.g. "02" for a duration of 2) make the file easy to sort in OPL, or otherwise.
Note, though, that the "Restore" option in the diary will restore entries in any order from a file. The "Xrestore" option can be used to load a CM/XP diary from a pack, in which case each entry is given a duration of 2 (=30 minutes).
These are block files of type $84. Its data block consists of 27 bytes exactly, containing the parameters:
All string have a maximum length of 2 and a leading length byte.
These are block files of type $87. Its data block consists of two parts, the header, and the data, both preceded by a length word.
If a password was used:
Other block file types:
PSION states that all other types are reserved. These might be used by PSION or third-party software.
Block file types are important in keeping different types of data distinct: disaster would result if OPL procedures could be loaded into the diary, for example. Applications tend to assume that block files of their own type are in the correct format, and usually perform no further checking.
This is the algorithm for scanning through the record structure of a datapack:
SET_PACK_ADDRESS ($0A) do LENGTH_BYTE = NEXT_BYTE RECORD_TYPE = NEXT_BYTE if LENGTH_BYTE = 0 ERROR (246) -- "no pack" stop endif if RECORD_TYPE = $80 -- if long record BLOCK_LENGTH = NEXT_WORD SKIP_BYTES (BLOCK_LENGTH) else if RECORD_TYPE <> $FF -- if valid short record SKIP_BYTES (LENGTH_BYTE) endif endif until LENGTH_BYTE = $FF -- found end of pack
If a length byte of zero is seen the pack is assumed to have been pulled out, and a NO PACK error is reported. Errors can occur in various places when writing to EPROM devices, which can disrupt the record structure. The file system error handling attempts to mark the data as deleted, or creates special 'invalid' records so that the remainder of the datapack can still be used. In certain exceptional cases this mechanism can fail, and a "READ PACK" error will result when invalid record structure is detected. This error is reported whenever the last record extends beyond the end of the pack. An "END OF FILE" error will also be reported when accessing a block file if the block file name is not followed immediately by a long record.
When writing a block file, any error will cause the operating system to first delete the long record, then the block file name. This is the reason for the $02 before the $80 in a long record - if writing the length word fails, the $80 is re-written as a zero, which forms a deleted short record enclosing the bad length word. When a block file is opened, if a valid long record beginning with $02 $80 is not found, the error "END OF FILE" (238) is reported.
When writing a short record the following errors may occur:
Some of the errors such as "PACK NOT BLANK", and "WRITE PACK ERROR" may only occur when sizing a blank EPROM device. Apart from the obvious, "END OF FILE" can mean: an illegal block file (as discussed above), or more generally "not found".
Example of record structure:
09 81 4D 41 49 4E 20 20 20 20 90 filename "MAIN"
04 90 41 41 41 41 record "AAAA" in MAIN
09 81 41 42 43 20 20 20 20 20 91 filename "ABC", data records are type $91
03 91 42 42 42 record "BBB" in ABC
01 10 41 deleted record "A" in MAIN
09 85 42 4C 4F 43 4B 20 20 20 00 block file "BLOCK", type $85 02 80 00 05 01 02 03 04 05 contains 5 bytes of data
09 02 4F 4C 44 20 20 20 20 20 00 deleted block file "OLD", 02 80 00 01 FF of type $82 (diary), contained 1 byte of data
F7 FF invalid short record
09 03 42 41 44 20 20 20 20 20 00 invalid block file "BAD" 02 00 00 FF of type $83 (OPL proc), deleted when length word failed FF end of datapack
Some of the file system's variables can be usefully read by the user, but these variables should be treated as read only. Writing to these variables may produce unpredictable results, and will cause incompatibility with future OS versions.
Organiser I datapacks are structured differently to Organiser II datapacks - the details will not be discussed here. The main differences are:
The Organiser I datapacks themselves are all compatible with the Organiser II, and can be used on the Organiser II after UV-erasure. Also there is no reason why a special user program could not read an Organiser I datapack, by accessing the datapack directly. If some special data other than SAVEd records is to be transferred, the easiest method is to use two RS232 leads.
This section briefly describes the system services used by OPL to handle files, records and block files. The names of these services are prefixed by FL$. A detailed explanation is given on the system services page.
It is more efficient to use record types directly to access several files at once, rather than opening one file, then the other. This is the equivalent of the OPL command USE. The services FL$OPEN, and FL$CRET return the record type in use by a file. FL$FREC will provide details of a record including its address in the pack for any user wishing to perform direct pack accessing, however such programs may not be compatible with future OS versions.
Programmers should note that calls to the FL$ services may be interspersed with PK$ calls - which may change currently selected device or change the current pack address - provided that PK$SETP is called to re-select the correct device for the file system before calling any further FL$ services. As always, any programs directly accessing the datapack hardware should notify the OS by calling PK$SETP.
Sets the current file position back to the previous record.
When called repeatedly, returns each filename of a given record type on a device.
Delete a named block file. On RAM devices the block file name and the following long record is deleted and the space is freed. On EPROMs the long record is not affected.
Finds a named block file of the given block file type. It returns the length of the data, and the pack address is set to the start of the data, ready for PK$READ to load the data into RAM.
Called in preparation for saving a block file, FL$BSAV saves a block filename followed by the first four bytes of a long record: $0280 and the length word. Then a call to PK$SAVE must be made to save the data. FL$BSAV checks that there is sufficient room on the pack for both the filename and the long record before writing to the datapack.
When called repeatedly, returns each the name of each file on a device.
Calling FL$CATL is equivalent to calling FL$BCAT for file type $81.
Copies files or block files from one device to another, as in the top level COPY or PROG COPY menu options. The copy-from device must not be the same as the copy-to device. A file may be copied to a different name on the target device. If a device only is specified in the copy-to string, the file is copied with the same name.
If the file already exists on the TO device then the records will be appended to the file otherwise a new file of the appropriate name will be created.
Creates a file. If the file already exists then FL$CRET returns error "FILE EXISTS", otherwise the file is created and the record type to be used by the data records in the file is returned
Delete a named file.
Erase the current record in the current file. On EPROMs this is done by clearing the top bit of the record type. On RAM devices the space occupied by the record is recovered.
Searches through all the records of a particular type on current datapack for a record beginning with a given search string. Also used to find file names.
Finds the next record of the current record type on the current datapack which contains the given search string. If a match occurs, FL$FIND leaves the current file position on the found record, if there is no match, reports error "END OF FILE" and leaves the current position at the end of file.
Returns information about the n'th record of the current record type on the current datapack. If the record exists, the three byte pack address of the start of the record is returned.
Adds one to the current record number. The next file operation, such as FL$READ will now read the next record of the current type.
Opens a previously existing file.
Checks that a file name is legal, and converts it to a standard form (D:Name where D is the device 'A'..'D', and the filename is between one and eight characters long.
Read the record from the current position of the current record type into not memory.
Sets the 'current record type' to the given value.
Changes the name of a file. Files can only be renamed onto the same device. The old name is deleted and then the new name is saved. On an EPROM device, this means that only eleven more bytes used by the new filename are saved.
Sets the 'current record number' to a given value
Selects a datapack. This datapack will then be used by other file system services.
Returns statistics about the current datapack, and current file or current record type. Counts the number of records of the current type, finds the end of the current datapack and returns the amount of free space.
Appends a new record of the current record type to the records on the current device. Records of length 255 are truncated to 254 characters. The current record number is set to the number of records of the current type.
FL$WRIT checks that there is sufficient space free first.
Performs the top level COPY, complete with "FROM" and "TO" prompts.
FL$WPAR (LZ only)
Validates wild card filenames. Returns length of body of filename and the file type for the extension.
FL$WCAT (LZ only)
Provides a wild card catalogue of the file names on a device. Works in the same way as FL$BCAT and FL$CATL except that it returns only those files which are consistent with the match string.
FL$NCAT (LZ only)
Same as FL$WCAT but gets the n'th match.
FL$WCPY (LZ only)
Wild file copy, works in a similar way to FL$COPY.
FL$WDEL (LZ only)
Wild file delete, works in a similar way to FL$DELN and FL$BDEL except that wild card characters and file extensions are allowed.
FL$WFND (LZ only)
Wild FIND, works exactly like FL$FIND but the wild card characters *, +, can be used.
FL$FDEL (LZ only)
Fast delete of a given number of records starting from a given record. If the number is -1 this deletes to end of file.
FL$GETX (LZ only)
Get the file extension for a given numeric file type ($81 to $8F)
FL$VALX (LZ only)
Returns the numeric file type for an extension.
See also chapter Packs for PK$READ