Appendix A1. Loader Table Structure

KEY The loader tables are held in a file, T#LOAD, which is created at subsystem start up time. This file is initially 3 pages long but has the capability to expand up to a limit of 1Mb or the maximum file size permitted in the process, whichever is the smaller. After 32 bytes of header information, the remainder of the first page is used for references, the second for permanently loaded entries and the third for temporarily loaded entries. The start address and length of each table is held in a record array SSLOADTAB. There are certain common features in the way each table operates. The first 1008 bytes are used as 252 integers numbered from 0 to 251. These function as listheads from which chains of records are attached. Which particular listhead a given entry or reference is chained to is determined by hashing the entry or reference name. The hash constant for the hashing function is the prime number 251 so that the result of the hash function is an integer in the range 0 - 250. Listhead 251 is not used in the reference table but is used in the entries tables to hold a chain of filenames. This is described at greater length below. Within each table, including the listheads, all links and pointer fields are relative to the start of that table i.e. listhead 0 has relative address 0. Each table is therefore entirely self contained and is independent of the actual address of the start of that table. Thus a table can be moved about as a unit, the only updating required is to the SSLOADTAB record array. This design makes it easy to expand tables when circumstances demand. For example, suppose the permanent entries table, table 2, is full but we need to add more entries to it - a common requirement during a load. The procedure is as follows: CHANGEFILESIZE is called to extend T#LOAD by 1 Epage. Table 3, the table of temporary entries, is moved 1 Epage up the VM and SSLOADTAB(3)_START updated. The page vacated by table 3, which is at the end of table 2, is filled with a 'not used' pattern and assigned to table 2 by incrementing SSLOADTAB(2)_LEN and the load can continue. Each table can expand itself in a similar way as circumstances dictate until T#LOAD reaches the file size limit of the process or 1 Mb, whichever is the smaller. Although there are two tables of entries they should really be regarded as one since they have exactly the same table structure, the difference lying in how they are administered. We are therefore describing two types of table; reference and entry. An entry or reference within each table is uniquely identified by its name and its type, e.g. we could have a code entry called TRIAL and a data entry called TRIAL in the entry tables. These then are the common features of each type of table but to proceed farther it is now necessary to look at each type separately.

Reference Table

KEY Of the two types of table this is the more complex to operate. References can remain in the tables for long or short periods e.g. if five files are loaded one after the other, references generated by the first could be satisfied by entries in any or none of the following files. Thus while references arrive in the tables in solid chunks as a file is loaded, they are removed in dribs and drabs as further files are loaded or explicit searches are undertaken. Space is released at unpredictable times in unpredictable locations. One of the main problems in operating the reference table is therefore efficient space management. Equally several files can refer to the same item so that occurrences of the same reference can be be widely scattered during a load. It is not desirable to duplicate information unnecessarily. Before we can seriously consider the space management problem we must decide how to store the information that must be held for a reference. The solution adopted in the loader is that there are two kinds of records associated with a reference, a basic record of which there is one per unique entry (ref name+ref type) and an information record of which there is one per reference occurrence. The basic records are chained off the listheads while the information records for a reference are chained off its basic record.

Information Record Structure

An information record consists of 4 integer fields as follows: DR0 -> This integer is assigned in the following manner: 2**31 -> if set then the reference is a common reference 2**30 -> if set then the reference is a dynamic reference 2**29 -> if set then the reference is an unsatisfied reference 2**28 - 2**24 -> load level at which the reference was generated 2**23 - 2**0 -> If this is a data reference then the length of the data item else 0 DR1 -> The address of the reference in the gla ADYNR/OFFSET -> If the reference is a dynamic or unresolved code or data reference then the address of the escape table else if the reference is a single word reference then the value of the offset to the address of the entry point else 0. LINK -> Offset of the next information record in the chain relative to the start of the table or -1 if this is the last record in the chain.

Basic Record Structure

KEY This consists of a name field double word aligned and 4 integers. NAME -> name of the reference double word aligned TYPE -> This integer is assigned as follows: 2**31 -> If set then at least one of the information records attached to this basic record is a common reference. 2**30 -> if set then at least one of the information records attached to this basic record is a dynamic reference. 2**29 -> If set then at least one of the information records attached to this basic record is unsatisfied. 2**28 -> If set then ALL the unsatisfied references on the information record chain have been made unresolved; i.e. a complete search for an entry to satisfy this NAME has failed but LOADPARM(LET) was set. Note that this bit and the 2**29 bit cannot both be set. 2**27 - 2**8 -> free 2**7 - 2**0 -> Reference type, 1=data, 2=code, 3=single word i.e code!data FIRST -> Offset relative to the start of the reference table of the first information record associated with this basic record. LAST -> Offset relative to the start of the reference table of the last information record associated with this basic record. LINK -> Offset of the next basic record chained to this listhead or -1 if this is the last record in the chain. The design of the basic record is geared to finding out what we need to know about the information records during loading without the overhead of chaining through them. Thus during a cascade load we can find the next reference that we should search for by inspecting the basic records for ones which have bit 2**29 set in the TYPE field. Similarly when adding a new information record we wish to go directly to the last in the chain rather than follow the chain to the end. More subtly if a reference is currently unresolved then we don't want to add a new unsatisfied reference to the table as this would trigger off a complete search which was bound to fail anyway; we want to make it unresolved right away.

Table Space Management

KEY When we want to add a new basic or information record to the reference table the space management problem boils down to finding a hole big enough to take the record. The minimum size record that will be searched for is 16 bytes for information records whereas basic records will be in the range 24 to 48 bytes long depending on the size of NAME. For each basic record there could be many information records. The strategy adopted is as follows. The most probable task is to find a 16 byte hole so we start by searching for one of these from the beginning of the table. If we don't find one then we must extend the reference table. If we do then if we are actually looking for a 16 byte hole then we have succeeded but to avoid searching the table from the beginning next time round we store the offset of this hole +16 in a global integer array element, NEXTAD(1). We can start searching from there next time. If however we were looking for a bigger hole we store the offset of the start of the hole in NEXTAD(1) as a future starting point and carry on looking from there for the larger hole. If we can't find one then we must extend the table otherwise we have succeeded. This strategy minimises the searching necessary to find holes of the correct size. When we satisfy a reference then we are releasing space. If the offset of the start of the space released is < NEXTAD(1) then NEXTAD(1) is reset to this value.

Entry tables

Entries are better behaved than references in that they arrive and depart at predictable times. All the entry points from an object file are added to the entry table when that file is loaded and they are all removed when the file is unloaded. Further, more than one entry with a given name and type cannot be in the entry table at the same time. In fact entries are so well behaved that we can regard the entry table as being like a stack. This has enormous advantages when we come to unload. Files loaded in a specific order will be unloaded in reverse order. The one exception to this is that at any time we can ask for a file to be 'permanently' loaded, either explicitly as in PRELOAD or implicitly as in a request to load a piece of standard system software. To keep the advantages of pseudo stack operation we use two tables, one for permanently loaded entries and one for temporarily loaded entries. As implied, we do not have the space management problems with entry tables that we had with references and all we need to remember is the current 'top of the stack' for permanent and temporary entries. These are stored in the global integer array elements NEXTAD(2) and NEXTAD(3) respectively in the main body of the loader. When an object file is loaded we add an entry record for each entry point. An entry record consists of a word aligned name field followed by four integers as follows: NAME -> Name of the entry point single word aligned TYPE -> This integer is assigned as follows: 2**31 - If set then NAME is a main entry point 2**30 - If set then this is a pseudo data entry created by DATASPACE 2**29 - If set then NAME is an alias of some other entry point which is already loaded i.e. created by ALIASENTRY 2**28 - 2**8 Free 2**7 - 2**0 The entry type; 1=data, 2=code (4=macro - not fully implemented as yet) DR0 -> If NAME is a code entry then an appropriate 2900 descriptor else if NAME is a data entry then the length of the data area in BYTES. DR1 -> Address of the entry point LINK -> Offset of the next entry record chained to this listhead or -1 if the record is the chain terminator. However, as well as the entry points from an object file it is also desirable to store other relevant information at the same time. For instance, it may be necessary to create a T#CODE file or a T#GLA file or both. These must be destroyed when the original object file is unloaded. Equally it turns out to be extremely advantageous in diagnosing problems with mismatching data reference/data entry lengths to store the range of gla (and initialised stack, if any) regardless of whether a T#GLA file has been created or not. To achieve these aims, then, after adding the entry points, we add a series of filename records - one for the object file itself and one for each of the other useful pieces of information that we need. In the worst case, therefore, there could be 4 filename records associated with an object file; the object filename, a record describing the gla used by this object file (which would have a T#GLA filename in its NAME field if one had been created or +GL if it hadn't), a record describing its range of initialised stack (which would have +IS in its NAME field) and a T#CODE record. It is not sensible to hash these filename records, instead they are all chained off the extra listhead, listhead 251. This is convenient for providing information to the user and vital in finding out quickly which files to unload at unload time. The information we want to store in a filename record depends on the type of file. The loader recognises four different types - object files, T#CODE files, T#GLA files and character or store mapped files used by DATASPACE. Filename records are the same structure as other entry records; a word aligned name string followed by four integers. We deal with each type in turn.

Object filename records

KEY NAME -> Full EMAS 2900 filename TYPE -> 0 MAINEP -> If the object file has one or more main entry points then the address of the first main entry point encountered else 0 DUM1 -> 0 (not used) LINK -> Offset of the next filename record or -1 if the last in the chain.

T#CODE filename records

KEY NAME -> EMAS 2900 filename i.e. no username TYPE -> 0 DUM2 -> 0 (not used) DUM3 -> 0 (not used) LINK -> Offset of next filename record or -1 if the last in the chain

T#GLA/gla/initialised stack filename records

KEY NAME -> EMAS 2900 filename i.e. no username/+GL/+IS TYPE -> 0 GLAFROM -> Address of first byte of gla/gla/initialised stack in T#GLA/T#UGLA or T#BGLA/T#USTK GLATO -> Address of last byte of gla/gla/initialised stack in T#GLA/T#UGLA or T#BGLA/T#USTK LINK -> Offset of next filename record or -1 if the last in the chain.

DATASPACE filename records

KEY NAME -> Full EMAS 2900 filename TYPE -> X'40000000' USECOUNT -> Number of separate DATASPACE entry points defined in NAME ACCESSMODE -> Connect mode of NAME, i.e. 1=R, 3=W, 11=WS LINK -> Offset of next filename record or -1 if the last in the chain.