The code generated by the IMP/Pascal compilers is pure and position-independent. A module is an independent compilation unit, corresponding to a main program or a file of external procedures and/or external data. A main program module may also contain external procedures/data; the only distinction is that it has a non-null main block. If a module has any static data (including linkage for external references), the module contains a code sequence to initialise the allocated static data area. This allows a module to be reset without re-loading or re-allocating the static data area.
The system provides for automatic linking of modules at load time, or dynamically, at the time of first call.
[SP] stack pointer
[A6] link register for level 1 procedures
[A5] base for process-global data
[A4] base for static data (+) - all modules (which have static data)
top-level data (-) - main program module (level 0)
[A3-A0] temporaries and parameter passing
[D7] unassigned pattern
[D6] stack limit + 256
[D5] line number (if maintained dynamically)
[D4] byte accumulator (high order 24 bits kept clear)
[D3-D0] temporaries and parameter passing
By default, parameters and results are passed in registers, up to the number assigned for this purpose (4 data registers and 4 address registers). Values are passed in data registers and addresses are passed in address registers. Registers in the two groups are assigned starting from D0 and A0, respectively. A value result for a function is passed in D0 (D0 and D1 if two-valued) and an address result in A0. For these purposes a value is any parameter passed by value which is a simple scalar or an aligned structure occupying no more than 32 bits; an address is any parameter passed by name or any other structure passed by value. The initial code in a procedure has the task of assigning the parameters passed into it in registers to local storage as appropriate. [Under optimisation, this may not be necessary in the case of simple procedures.] In the case of structure values passed as addresses, it is the structure content which is stored, not the address, so that the properties of pass-by-value are preserved. When the result of a structure function is returned as an address in A0, it may point to the now released stack frame and the receiving code requires to move the structure elsewhere (eg by pushing it properly onto the stack).
Parameters beyond those which can be accommodated in registers of the appropriate group are pushed to the stack in reverse order of occurrence. In this case, the actual value is pushed for all value parameters (including structures), the 32-bit address for %name parameters.
There are special unpublicised conventions in the IMP compiler which allow explicit control to be exercised over how many registers are used for parameter passing and hence when the escape to the stack is invoked.
Internal procedures (ie those local to the module making the call) are called using BSR (possibly reach-extended by one or more BRAs as stepping-stones):
evaluate parameters; BSR dddd; remove any stacked parameters
Extracode procedures (regarded as hardware extensions) are called by the sequence:
evaluate parameters; JSR aaaa; remove any stacked parameters
where aaaa is the absolute address of the relevant extracode vector slot.
System procedures are called by the sequence:
evaluate parameters; JSR xxxx(A4); remove any stacked parameters
with the content of the section of the static data area starting at xxxx initialised at load/link time to:
JMP eeeeeeee
External procedures are called by the sequence:
push A4; eval param; JSR xxxx(A4); remove param; pop A4
with the content of the section of the static data area starting at xxxx initialised at load/link time to:
MOVE.L #newa4,A4; JMP eeeeeeee
For dynamic external procedures, the call is the same but the 12-byte sequence is initially set to invoke the loader.
Note that it is the responsibility of the calling sequence to adjust the Stack Pointer to remove any stacked parameters following the call. [Motorola thought of ReTurn and De-allocate stack too late for the original 68000].
There is no general assumption about the preservation of the values of the temporary registers across procedure calls. The standard entry sequence does not therefore save these registers. In the case of most extracodes and certain system procedures, however, there are stipulated registers involved in each case and other registers are preserved.
The process-global data area maintains the process environment, in particular the input/output context and the exception trap linkage. This is common to all the modules making up a complete program. It is addressed by the Global Base register (A5).
Static (own) data, in either main program modules or external modules, is accessed at non-negative displacements from the Static Base register (A4). [Comment: the first declared own enjoys an efficiency bonus]. Every module which has any static data (required also for linkage for external references) has its own static data area which is private to (a specific incarnation of) the module. [The code of a module is sharable; the data area, in general, not]. The calling sequence for an external procedure (or one that might be, eg a procedure parameter) includes code to save and restore the current Static Base and the transfer sequence sets it for the module containing the procedure. This is done without affecting the way in which parameters are handled.
In a main program module, the static data area is allocated from the initial stack allocation for the program and addressed in the usual way positively from the Static Base.
' Dynamic' data in the main program (top-level) is then allocated on the stack and is addressable negatively from Static Base.
Local variables in the current stack frame are accessed SP-relative (positive or zero displacements). This strategy is used since many procedures have variables which are accessed only locally, so that no other form of access requires to be provided.
Variables at intermediate levels, neither local nor top-level, are accessed using a version of the 'display' technique for multi-level addressing. A single address register (A6) is reserved for level 1 procedures (main program being taken as level 0) and the display for lower levels is held in store (at the start of the process-global area, maximum level 7). The store-held part of the display is slaved into temporary address registers as needed.
In most cases the stack extension for a block or procedure is of fixed size. The extension is made incrementally, corresponding to the declaration of individual dynamic variables, but with accumulation of multiple declarations. This strategy allows efficient assignment of initial values and enables the use of SP-relative addressing on a one-pass basis.
Where arrays of variable size are involved, the total stack extension is of unknown size. In such cases, the dope information and one or two pointer slots for each array are allocated as part of the fixed stack extension, and the arrays are subsequently allocated at the end of the fixed part. The presence of this section of unknown length prevents the use of SP-relative addressing (except for later declared compiler temporaries), so that local addressing in such cases follows the pattern for intermediate level addressing. Fixed size arrays which are big enough to threaten addressability of the fixed section (max 32k bytes) are treated as if they were of variable size.
At procedure entry, a LINK operation (on A6 or stored display slot) is performed if at least one of the following conditions holds:
there are non-local references to variables at this level;
there are dynamic (or large) objects declared at this level;
there is an event trap statement in the procedure;
stack diagnostics are enabled [pending].
[Although the compilers are one-pass, the entry sequence is generated at the end of a procedure, rather than the beginning.]
Object module files consist of a number of sections. These are, in order of occurrence:
[HEADER] information about complete module and section lengths
[EXPORTS] list of identifiers defined in this module for external use
[IMPORTS] list of external identifiers referenced in this module
[CODE] binary position-independent code of module
[DIAG] diagnostic tables if present
All section sizes are even numbers of bytes. The total length of the object module file is the sum of the sizes of the individual sections. Numeric information is in standard M68000 binary twos-complement representation (MS bytes first).
The header section has a fixed size of 32 bytes (for module format V02). Its layout is:
__________________
0000 / | F E 0 2 | object module code + version
0002 / | 0 0 0 0 | spare
0004 / | export size | size of export section (bytes)
0006 / | import size | size of import section (bytes)
0008 / | code | size of
000A / | size | code section (bytes)
000C / | reset entry | reset entry point (word) [1]
000E / | main entry | main entry point (word) [1]
0010 / | static data | required size for
0012 / | size | static data area (bytes)
0014 / | stack | required size for
0016 / | size | stack (bytes) [2]
0018 / | diag section | size of
001A / | size | diagnostic section
001C / | 0 0 0 0 | spare
001E / | 0 0 0 0 | spare
__________________
[1] word (16-bits) displacement from start of code section
[2] stack size > 0 is actual requirement when known
stack size <= 0 is negated minimum requirement
Both the import and export sections consist of a variable number of consecutive records, one for each identifier. The last record is followed by at least one zero word as an end marker. (This is for processing convenience; the overall section size is as given in the header). The records are of variable size, depending on the length of the identifier. The size of the record is 13 plus the length of the identifier, evenned up. The layout of each record is:
__________________
0000 / | x x x x | flag word [1]
0002 / | type | type [2]
0004 / | " | information
0006 / | " | words
0008 / | address | byte address of
000A / | " | entity or reference [3]
000C+/ | identifier | text of identifier
/ : " : (length-prefixed string)
/ : " :
/ | " |
__________________
Note [1]
relevant bits ( MS bit = 15):
15 = 0: last (terminator word)
= 1: not-last
14 = 0: internal (to be ignored)
= 1: external
13,12 = 00: data object
= 01: system procedure
= 10: external procedure
= 11: dynamic external procedure
Note [2]
to be specified: not used at present
Note [3]
for a data entry in the export list, the address is the byte displacement of the object relative to the start of the static data area
for a procedure entry in the export list, the address is the byte displacement of the entry-point relative to the start of the code area
for a data entry in the import list, the address is the byte displacement in the static data area of a 4-byte slot to be filled in with the absolute address of the object
for a procedure entry in the import list, the address is the byte displacement in the static data area of a 6-byte (system) or 12-byte (external) call sequence to be set up to call the procedure
PROGRAM simple;
PROCEDURE process(a:Integer); EXTERN;
VAR i:Integer;
BEGIN
read(i);
process(i);
END.
HEADER
0000 FE02 file-type code (FE), version (02)
0002 0000 [unused]
0004 0000 size of export section (0)
0006 0028 size of import section (40 bytes)
0008 0000 code section size
000A 0044 (68 bytes)
000C 000D reset entry (word 13 = byte 26)
000E 0001 main entry (word 1 = byte 2)
0010 0000 static data requirement
0012 0018 (24 bytes)
0014 FFFF stack requirement
0016 FFF0 (16+ bytes)
0018 0000 diag section size
001A 0000 (0)
001C 0000 [unused]
001E 0000 [unused]
IMPORTS
0020 D000 system procedure "RINT"
0022 0000
0024 0000
0026 0000
0028 0000 relative address in static
002A 0000 data area of transfer sequence (0)
002C 0452 "R
002E 494E IN
0030 5400 T" + pad
0032 E000 external procedure "process"
0034 0000
0036 0000
0038 0000
003A 0000 relative address in static data
003C 000C area of transfer sequence (=12)
003E 0770 "p
0040 726F ro
0042 6365 ce
0044 7373 ss"
0046 0000 terminator
CODE
0048 4E75
004A 206D
.... ....
008A 0000
Creating object modules in assembly language programmingAn include file is available for use when preparing assembly language programs which are to be compatible with modules written in IMP or Pascal. By including the statement INCL INC:MODULE.ASM in the assembly language source file, the following macros are made available to allow the assembled output to be in FE02 format suitable for "linking" with modules written in IMP or PASCAL:
[MODULE text-string] The file should contain exactly one call of this.
TEXT-STRING is the module-name, which is ignored
except that it appears as the title on every page
of the listing.
[EXPORT symbol,mode] For each procedure or data object defined in
this module for use from other modules, a call on EXPORT should be made.
SYMBOL is the name given to the external object, and
is also the label which must appear in the code or
data section. MODE is one of DATA, EXTERNAL, or SYSTEM.
[IMPORT symbol,mode] For each procedure or data object referenced
but not defined in this program, a call on IMPORT should be
made. SYMBOL is the name of the external object, and
the label which must appear in the data section.
MODE is one of DATA, EXTERNAL, SYSTEM, or DYNAMIC.
[CODE] This macro should be called if the module contains
any code.
[STACK SET value] This
defines the value plugged into the module header which informs
the loader of the estimated maximum stack requirement of the procedures
in this module. (>0: known maximum, <0: -guestimate, =0: unknown)
[CALL symbol] This is used to call an external or dynamic procedure.
[SCALL symbol] This is used to call a system (data-less external)
procedure.
[ADDRESS symbol,ptr] This is used to address an external data
object.
[COPYDATA] This may be used in the reset procedure to initialise the
data area.
[DATA] This macro should be called if the module contains any
static data or any references to external objects.
[symbol VECTOR mode] This macro should be called, in the data
section, for every imported object. SYMBOL and MODE are the same
as in the IMPORT statement. This generates either a
single longword to receive the address of the external
object (for DATA references) or the appropriate entry
sequence for calling external procedures (6 bytes for
SYSTEM procedures, 12 for EXTERNAL and DYNAMIC ones).
[ENDMODULE] This should be called last thing, immediately before
the END statement. Its effect is to delimit the end
of the last section, and to insert dummy export, import,
and code sections if they were absent.
The calls on the macros listed above must be made in the following order, corresponding to the order of sections in the object module:
(optional, once) STACK SET value
(obligatory, once) MODULE name
This generates the header.
(optional, repeated) EXPORT name,mode
On the first call, this identifies the start of the export section.
It generates a loader record for the symbol.
(optional, repeated) IMPORT name,mode
On the first call, this identifies the end of the export section and
the start of the import section. It generates a loader record similar
to that for the EXPORT directive.
(optional, once) CODE
This identifies the end of the import section, and the start of the
code section. If the code section is present, it must contain the
labels RESET and BEGIN.
The RESET procedure will be called by the loader whenever the module
is loaded, this procedure should be used to initialise the data
section, but note that external objects may not yet be accessed.
The BEGIN procedure is called by the loader (after the RESET one) if
the module is being invoked as a main program.
(optional, once) DATA
This identifies the end of the code section proper
and the beginning of the data initialisation section.
It must be present if the module uses any static data
or references any external objects.
(optional, repeated) name VECTOR mode
This reserves space in the data section for addresses of external data
objects or call sequences for external procedures. These may be placed
only in the data section, but can appear anywhere within it, i.e. need
not appear all together at the front, say.
(obligatory, once) ENDMODULE
This identifies the end of the data section and deals with any sections
which may have been absent.
It is expected that the RESET procedure will copy the contents of the (read-only) data initialisation area within the code section. (accessed by PC-relative addressing) into the writeable static data area (accessed by A4). The data initialisation area starts at label DATABEG (defined by the DATA macro) and is of size DATASIZE bytes (defined by the ENDMODULE macro).
It is expected that code referring to anything in the data area should do so via A4. A reference to the label X, say, should read X-DATABEG(A4).
In the code section, the following macros may be used in order to access external objects more conveniently:
[ADDRESS symbol,x] This puts the address of external data object SYMBOL
into X, which will usually be an address register.
[CALL symbol] This calls an external (or dynamic) procedure.
[SCALL symbol] This calls a "system" (data-less external) procedure.
[COPYDATA] This may be called by the reset routine, it copies
DATASIZE bytes from (DATABEG) to (A4).
The distinction between EXTERNAL and SYSTEM procedures is that the latter are called by shorter and faster entry sequences. SYSTEM procedures are expected not to have any static data of their own. The standard call sequence for EXTERNAL procedures is:
MOVE.L A4,-(SP)
JSR label-DATABEG(A4) --+
+--> MOVE.L #<filled in by loader>,A4
JMP.L <filled in by loader> --+
+--> RTS --+
MOVE.L (SP)+,A4 <--------------------------------------------+
The call sequence for SYSTEM procedures misses out the code affecting A4,
on the understanding that the called module will neither affect nor use A4:
JSR label-DATABEG(A4) --+
+--> JMP.L <filled in by loader> --+
+--> RTS --+
(straight back) <--------------------------------------------------+
Although SYSTEM procedures may not use any module-own data, they can
access the process-global data area addressed via A5
which is common to all modules, but local
to the process in which they are executing.