All files which are to be loaded by EXOS should follow the format
described here. It is designed so that the operator of a program such as
BASIC can simply give a command such as LOAD
without knowing
what he is going to load. It could be a BASIC internal format program, or
it could be a new device driver in relocatable format, to name but two.
A file consists of a series of one or more modules. Each module starts with a 16 byte module header which defines what type of data is to follow in the rest of the module. A file can contain several modules so that, for example a BASIC program can be loaded at the same time as a new device driver which the program uses, simply by having them as two modules in a single file.
The header starts with a null byte (zero) to indicate that it is an module header, rather than for example an ASCII text file. Any files which do not start with a null byte will be referred to as ASCII files although they may be any other sort of data.
Following the null is a type byte, which specifies what type of data the rest of the module contains. The next 13 bytes are different for each type and contain various other parameters such as size and entry point addresses. The very last byte of the header is a version number and should always be zero for current versions.
The defined types of module are:
0 - $$ASCII ASCII file 1 - Not used 2 - $$REL User relocatable module 3 - $$XBAS Multiple BASIC program 4 - $$BAS single BASIC program 5 - $$APP New applications program 6 - $$XABS Absolute system extension 7 - $$XREL Relocatable system extension 8 - $$EDIT Editor document file 9 - $$LISP Lisp memory image file 10 - $$EOF End of file module 11 .. 31 - Reserved for future use by IS/Enterprise
Type zero is recognised as an ASCII file to reduce the possibility of an ASCII file being mistaken for an module header. This will be explained in section 10.2.
When a module has been loaded another module may follow, so the system will attempt to load another header. It is therefore necessary to end each file with a module header with the end of file type (type 10) to indicate that there is no more to load.
Header types 3, 4, 8 and 9 are specific to particular languages or devices and are described in the documentation for those programs (ISBASIC, ISLISP and the EXOS editor). They will not be mentioned further here.
Of the remaining types, numbers 5, 6 and 7 are handled entirely by the EXOS kernel. Type 2 is handled by the EXOS kernel but under the control of the user. All of these types will be described in the following sections.
When the user wants to load a file, he should ensure that the channel to
load from is open and then make a load
module EXOS call (code 29). This will read one byte from the
channel and immediately return a .ASCII
error, with the
character code in register B, if the byte is nonzero.
If the first byte is zero then another byte (the type byte) is read. If
this is zero then it is an ASCII file so a .ASCII
error is
returned, with the type byte (zero) in register B. This
ensures that if an ASCII file starts with a series of nulls then it will
be recognised as an ASCII file and only the first null byte will be lost.
If the type byte is nonzero then it is saved and another 14 bytes
read in to complete the module header. If it is an end of file header
(type 10) then a .NOMOD
error will be returned. This should
be trapped by the user program since it is not really an error, it is the
normal terminating condition.
If the module is a type wich is handled internally by EXOS (type 5, 6 or 7) then the rest of the module will be loaded in and initialised (details are given in the following sections). If it is not a type handled by EXOS then the module header will be passed around any system extensions to give them a chance to load it if they recognise the type. If the module is loaded in either of these ways then a zero status code will be returned to the user.
Assuming that the module was not loaded by EXOS or by a system extension
then a .ITYPE
error will be returned to the user, and the
module header copied into a buffer passed by the user. The user can then
look at the type byte and load the rest of the module if he recognises
it.
When a module has been loaded, by the user, by EXOS, or by a system
extension, another load module call
should be made to load in the next module of the file. This will continue
until a .NOMOD
error is received from EXOS, which is the
normal termination, or a fatal error occurs, either from the loading
channel or an invalid module, which will result in an error response.
EXOS supports the loading of relocatable modules using a simple bit stream relocatable format. There are two types of relocatable modules, user relocatable modules and relocatable system extensions. These module types and how they are loaded are described in sections 10.4 and 10.5. This section just describes the relocatable bit stream format itself.
The data of a relocatable module is a bit stream in the sense that individual data fields are a variable number of bits and are not aligned on byte boundaries. The bytes of the data are interpreted most significant bit first, so the first bit of the bit stream is bit 7 of the first byte.
A complete relocatable module consists of a sequence of items which are defined by sequences of bits in the bit stream. The following diagram shows the decoding of the bit stream into the various items. The items themselves are explained afterwards.
0 -> 8-bits load absolute byte 1 00 -> 16-bits load relocatable word . 01 0 0 -> 2-bits set run time page . .. . 1 -> restore run time page . .. 1 -> 16-bits set new location counter . 10 -> end of module . 11 -> illegal - for future expansion
When the relocatable loader is called it is passed a starting address wich can be in any Z80 page. It loads the data into whatever segment was in that page, and must not cross a segment boundary. It keeps a location counter which is the current address it is storing bytes at and is also used for relocatable words. This location counter is initially set to the start address passed to the loader.
If a set new location counter item is found then the following 16 bits form an offset which is added to the current location counter. Adding this offset must not move the location counter into a new page.
It is often useful to have sections of code loaded into a segment which will be accessed in different Z80 pages, since the segment can be paged into different pages. This is particularly true when creating user device drivers which may be loaded into page0, but when executed will run in page3. It is to provide this facility that the set run time page and restore run time page items are provided.
When a set run time page item is found, the following two bits define a new page. The top two bits of the location counter will be set to this new page setting. This will not affect where bytes are actually loaded since the page is irrelevant as they are always loaded into a single segment. However it will affect the values produced for relocatable words wich are loaded. This means that code can be loaded in one page to run in another.
The restore run time page item will set the page of the location counter back to what it was when the loader was called, regardless of any new pages which have been set since then.
When a load absolute byte item is found, the following 8 bits are stored at the current location counter address and the location counter incremented by one. When a load relocatable word item is found, the following 16 bits are read and the current location counter added on to them. The resulting word is stored low byte first at the location counter address and the location counter is incremented by two.
When an end of module item is found it will terminate the relocatable loader. Any remaining bits in the last byte will be padded out with zeros and the following byte will be the start of the next module header.
User relocatable modules are loaded into user RAM and are regarded as being part of the current applications program once loaded. It is the responsibilty of the user to organise allocation of RAM for them to be loaded into. They are useful for providing user device drivers, indeed the interlace video driver which is provided with the Enterpise Computer is loaded as a user relocatable module.
The module header for a user relocatable module is:
0 - zero 1 - module type (2) 2 ... 3 - Size of code once loaded 4 ... 5 - Initialisation offset (0FFFFh if none) 6 .. 15 - zero
When an EXOS load module function call
finds a header of this type, it will not recognise it but will just
return a .ITYPE
error to the user. The user looks at the
type and sees that it is a user relocatable module. The size field in the
header defines the complete size of the module once it is loaded. The
user must find an area of RAM of this size, in one segment which he can
allocate permanently, and pass this address to a
load relocatable module EXOS call
(code 30), along with the channel number.
EXOS will load the module into the RAM and then return to user with a
zero status code if there was no error. If the initialisation offset is
not 0FFFFh
then the user should call this address (the
offset is from the initial loading address). This routine will do any
initialisation of the module which is required. For example in the case
of the interlace video driver, the initialisation will link it into EXOS
as a user device.
Relocatable and absolute system extension are loaded automatically by EXOS when the appropriate module header is found. They are loaded into segments which EXOS marks as allocated to devices and will therefore never be freed. Once loaded they function exactly like ROM based system extensions, with a single entry point which is passed action codes. Operation of the extensions once loaded was described in a previous chapter, this section just covers the actual loading and header format.
EXOS maintains a list of segments allocated in this way. They can be used for loading relocatable and absolute system extensions, and also for allocating RAM to ROM extensions at cold start time. Absolute system extensions always go at the bottom of a segment and so there can only be one per segment. Relocatable extensions and RAM areas for ROM extensions are allocated from the top of a segment downwards and there can be as many of these in a segment as will fit.
The module header format for the two types is the same except for the type byte:
0 - zero 1 - module type (6 for absolute, 7 for relocatable) 2 .. 3 - Size of code once loaded (<16K) 4 .. 15 - zero
EXOS will first allocate enough RAM to load the extension into, which
may require allocation of a new segment or may be able to make use of a
space in an earlier segment. The data will then be loaded into the
segment. In the case of an absolute extension the data will be loaded
with the first byte going at address 0C00Ah
, which will be
the entry point of the extension. For relocatable extensions the code
will be loaded anywhere in the segment (addressed in Z80page 3) and
the entry point will be the very first byte loaded.
If an error occurs in loading then the extension will be lost and the RAM for it will be deallocated which may involve freeing a segment if it was a newly allocated one. If no error occurs then the new extension will be linked on to the start of the list of system extensions and then initialised, as described in the chapter on system extensions. Control will then return to the user in the usual way.
The new applications program module type is loaded automatically by EXOS when the header is found. It can be used to load programs of up to 47.75K. The program it loads will automatically be started up as the new applications program, losing the previous one. It is intended for loading programs such as machine code games from cassette although it will have other uses.
The module header format is:
0 - zero 1 - module type (5) 2 ... 3 - Size of program in bytes (low byte first) 4 .. 15 - zero
EXOS will look at the size of the program and work out if enough user
RAM can be allocated to load it into, allowing for a shared segment but
without closing any channels. If there is not enough then a
.NORAM
error is returned, otherwise EXOS will commit itself
to loading the file.
Having reached this stage it will allocate the necessary user RAM segments for the program and from this point on it cannot return to the current applications program since it will have corrupted the RAM it was using. If an error occurs from here on then it will display an error message on the default channel and then scan all extensions with the cold start action code. This is the only time that extensions can receive a cold start action code other than at a genuine cold start (see section 9.2).
Once the required segments have been allocated the new program will be
read in from the channel and stored as absolute bytes starting at address
0100h
. When the whole program has been loaded, EXOS will
simulate a warm reset to the start of the program at 0100h
.
This warm reset will be done with the reset flags set to 20h
(see section 11.2) which will completely reset
the I/ O system, without disturbing user RAM. The new applications
program will have to go through the normal startup procedure (described
in section 9.3), except that it needn't do
another EXOS reset call.
Since user segments may have had to be allocated to load the program in, the program may be occupying a shared segement, If this is the case then the user boundary will have been set to just above the end of the program to allow as much RAM as possible for opening channels etc.