EXOS 2.1 Kernel Specification

10. Enterprise File Format & EXOS Loading Functions

10.1 Enterpise File Format

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 Enterprise 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.

10.1.1 Module Header Types

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 Enterprise 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 (IS­BASIC, IS­LISP 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.

10.2 Loading Enterprise Format Files

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 non­zero.

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 non­zero 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.

10.3 Relocatable Data Format

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     

10.3.1 Location Counter and Run Time Page

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 page­0, but when executed will run in page­3. 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.

10.3.2 Relocatable Words and Absolute Bytes

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.

10.3.3 End of Module Item

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.

10.4 User Relocatable Modules

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.

10.5 Relocatable and Absolute System Extensions

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 Z80­page 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 de­allocated 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.

10.4 New Applications Programs

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.

Next Chapter: EXOS Function calls in detail.

David Bouman. (dsbouma@cs.vu.nl)