IMP Compiler for M68000: Version 3

The Compiler translates programs written in the high-level programming language IMP to M68000 native code. Compilation is a single pass translation from source to executable code.

Reference descriptions of the IMP language are contained in The IMP80 Language (html format) (or pdf format) (John Murison, Edinburgh Regional Computing Centre) and The IMP-77 Language (1977 edition, html format) (alternative 1986 edition in pdf format) (Peter S. Robertson, Edinburgh University Computer Science Department and Lattice Logic Ltd). The dialect of IMP accepted by the Compiler is closely similar to IMP-77, and this document provides only an outline summary of features which are fully covered by the IMP-77 Report.

A dagger in the margin is used to flag features which may not be present in IMP-77, or may be present in different form.

In addition to some general-purpose language extensions, a number of extra features have been included in the present implementation to extend the capability of the language for system programming applications. By their nature these extensions should be avoided in software which is intended to be portable.


Hamish Dewar
CLAN Systems
February 1986


Source program

Source programs are ordinary free format text files. There is no explicit restriction on line length.

Program order

The normal ordering in any block should be declarations, then event trap if any, then instructions. It is acceptable for static declarations, such as procedures and constant or own variable declarations, to be interspersed among instructions, but the Compiler queries any dynamic variable declarations which appear after instructions, and hard-faults any which appear in a loop or after a sequence change.

External specifications (defining imports) must appear at the outermost textual level, that is, before the first begin or in the main program block. An external spec may be satisfied by a procedure occurring later in the same module, that is, it may turn out not to be an import after all. This, in a rather unsatisfactory way, allows the same definitions to be used by importing and exporting modules.

External definitions (defining exports) must appear in the source file before the main block (if any).

End of file

A main program file, containing a main block opened with %begin, may finish with an %end statement matching the main block %begin followed by %end %of %file. Alternatively it may finish with %end %of %program.

A program file containing just external procedures but no main block, or an include file, is terminated by end of file.

End of file may be either the physical end of the file or the statement %end %of %file.

Main program files may also contain external procedures.

If either of the statements %end %of %program or %end %of %file is used to terminate a file, the Compiler does not process any subsequent text which there may be in the file.

Line continuation

As well as the old convention for continuing a statement onto the following line (%c), it is permitted to continue a line by finishing it with a hyphen (minus-sign). The hyphen must be the very last character on the line. In addition, a line break is not treated as significant following a comma or following an equals-sign introducing an array initialisation sequence.

File inclusion

Other files may be included in the source by means of the directive:

%include "........"

for example %include "GRAPHICS" The quoted string specifies the name of the file to be included. Line numbering in included files operates independently from the main file, and any Options (see later section) which are specified within the included file, are localised to this file. Included files may be nested to a maximum of three.

Conditional compilation

† The Compiler implements a conditional compilation mechanism, which allows parts of the program source to be selected for compilation, and other parts ignored, depending on the truth or falsity of controlling conditions. This simplifies the maintenance of multiple versions of a program.

A compile-time condition is introduced by the directive $IF (dollar-sign followed by IF). It may only appear at a point in the program text at which a source statement may begin. The directive is followed by any valid IMP condition involving only literal expressions. The effect is that during compilation the condition is evaluated: if it is true, the following source lines are compiled; if it is false, they are ignored. The scope of the condition extends to a matching $ELSE or $FINISH directive. In the $ELSE case, the following source lines are compiled if the condition was false, and ignored if it was true.

Compile-time conditions may be nested within one another. However, it should be noted that excessively elaborate or lengthy conditional sections can make the flow of a program for one particular case difficult to follow. It may be preferable to place long sections of code which are peculiar to one version in a separate file, and conditionally include this file.

All conditional-compilation directives must appear at the start of a source line, apart from any leading spaces. Source lines which are not selected for compilation are not processed in any way. They do not require to be valid IMP. Consequently quotation and line continuation conventions are not applied to material which is being skipped.

In Compiler listings, lines which have been skipped are listed with a minus following the line number as a flag symbol.

Selected material must constitute a number of complete source statements, but there are no structural restrictions. In particular, the normal block and statement structure of the language does not require to be properly nested with respect to the conditional compilation structure.

Simple example:

%constant integer resident=1, loaded=2   {variant values}
%constant integer variant=resident       {that or LOADED}
     .................
     .................
$IF variant = loaded {is main program}
%begin
%constant %integer  max=20000
$ELSE {is external}
%external %routine EDIT(%string(255) cliparam)
%constant %integer max=5000
$FINISH

Declarations and data types

Declaration modes

Data declarations starting with any of the under-noted mode specifications introduce static variables. Data declarations without a mode specification introduce dynamic variables.

Initialisation values for static variables must be literals. A static name variable may be initialised to NIL by the form '==NIL', or to point to an absolute location by the use of an appropriate store mapping function with literal argument, for example '%integer %name MODE==INTEGER(126)'.

† Own variables for which no initial value is specified are not initialised to zero. In general, for a normally loaded program, this means that they are unassigned, although in special cases they may inherit values from the program environment.

external constant or const identifiers of scalar numeric types, but not strings or structures, may be used in literal expressions.

In a constant declaration declaring multiple identifiers of ordinal type, it is permitted to omit the assignment part (equals-sign and expression) for any identifier apart from the first. The effect is that the identifier takes the next value in sequence.

low-level mode allows variables to be declared at (@) explicitly specified addresses. The normal alignment requirements which are enforced on automatically allocated variables are suspended for the first (or sole) variable declared with this form of mode specification.

It takes either of two forms:

@ address-expression

address-expression is either a literal expression of type %integer or a register-displacement form of assembly-language effective address. For example:

@126 %byte MODE

@-32(A5) %integer ELAPSED

@16_90001 %record (duartf) DUART

@# simple-variable

The effect of this form is to declare the identifiers in the declaration list starting at the same address as that of the designated variable. Successive allocation is at increasing addresses, even if the designated variable is on stack.

Declaration attributes

Attribute keywords appear before the data-type specification in a declaration. Multiple attribute keywords may appear in any order. For example:
%record %format PRINTERF(-
                %byte MODE,
                %writeonly %byte DATA,
                %volatile %half TIMER,
                %volatile %readonly %byte STATUS)
A %readonly attribute indicates that a variable may only be read, not written, by the program. It is useful mainly for purposes of documentation, particularly in application to fields of a record characterising a memory-mapped device.

A %writeonly attribute indicates that a variable may only be written to, not read, by the program. As well as serving documentation purposes, it indicates that the variable should not be updated using instructions which imply a read as well as a write (eg CLR on the M68000).

For a variable, the %volatile attribute indicates that the designated location is subject to change independently of the execution of the program (for example, a timer or device status register). For a %function, %map or %predicate, the attribute indicates that the result is not a pure function of its arguments. One of the implications is that the Compiler should not optimise references to such variables and procedures.

The %register attribute is valid only for a simple dynamic variable. It requests that the variable should be allocated to one of the machine registers available for this purpose, rather than a storage location, for the sake of efficiency. At run-time, the content of the selected register is saved at the point of allocation and restored on block exit.

The %register attribute is ignored if monitoring diagnostics are in force. Certain restrictions apply to variables allocated to registers: in particular they may not be used in a context where the address of the variable is required, and their values should be regarded as undefined after event trapping if they have been re-declared in any of the procedures from which exit has been forced.

e.g.: %register (a2) %mite %name pos is a low-level variant of the preceding attribute in which the register to be used is specified explicitly. No saving/restoring of the register content is effected.

Subsequent to this declaration the register is no longer treated as eligible for use either as a temporary or user allocatable register.

This form is valid for parameters to procedures where it indicates that the parameter is to be passed, as well as being accessed, in the designated register.

This is a very low-level feature which should be employed only where the implications for Compiler register usage have been fully taken into account.

Ordinal data types

%integer
integer in range -2147483648 (-231) to 2147483647 (231-1); 32 bits

%long %integer
accepted as equivalent to integer

%short
integer in range -32768 to 32767; 16 bits

%half
integer in range 0 to 65535; 16 bits

%mite
integer in range -128 to 127; 8 bits

%byte
integer in range 0 to 255; 8 bits

%integer (lo:hi)
integer in range lo to hi; 8,16,32 bits

char
character; 8 bits

IMP does not distinguish characters from integers. The use of the %char type permits more appropriate diagnostics, automatic type transfer, and better exploitation of over-loading. It also provides compatibility with Pascal. A value of type %char may be used wherever a value of type %string is required, by virtue of automatic type conversion from character to string of length 1. A single character between string quotes is treated as of type %char, rather than %string, but like any other character, is subject to automatic type conversion.

%boolean
boolean (%false,%true); 8 bits

procedure class
%predicate is treated as equivalent to %boolean %function. An expression of type %boolean is a valid form of condition (but conditions are not boolean expressions). Again this type provides compatibility with Pascal.

Reals

%real
single-precision floating-point;32 bits
Implementation is by software on M68000

%long %real
treated as %real, apart from type-checking

Strings

%string (maxsize)
character string; maxsize+1 bytes

In a name or map, maxsize may be given as '*' to indicate compatibility with any string variable, irrespective of its declared maximum size. This is also permitted in the case of %string (*) %array %name parameters to procedures.

For a constant string, but not a constant string array, maxsize (and enclosing parentheses) may be omitted, for example:

%constant %string message="TOO MANY NAMES".

Records

%record %format record-type-ident (.....)

Format declaration introducing a record type

%record (record-type-ident) record-ident, record-ident, ....

Record declaration

Record construction

† A record format identifier may be used as a constructor for the record type defined by that format, from component values specified in parentheses following the format identifier. At present only literal component values are permitted. This form is particularly useful for specifying initial values for records, but is not restricted to that context.

The component values may be presented either positionally or using the record component names, preceded by the sub character (_), as keywords.

Example - Format declaration:

%record %format f1( %integer lo,hi, %string (7) code,
                    %byte %name where, %short weight)
Example - Record declarations with initialisation:
%own %record (f1) r1=0
meaning: numeric values = 0, strings = "", names == NIL
%own %record (f1) r2=f1(-100,100,"FACTOR",nil,1)
%own %record (f1) r3=f1(0,,"DUMMY")
by enumeration, any omitted values unassigned
%own %record (f1) r4=f1(_code="UNDEF",_weight=99)
%own %record (f1) r5=f1(h_i=99999,_code="MAX")
by selection using field names, any omitted values unassigned
field names may appear in any order

Arrays

Static arrays must have literal bounds. Dynamic arrays may have literal or non-literal bounds. Array names may have literal, literal unbounded, implicit or conformant bounds.

† The array type information defining the bounds may be placed after the keyword array or after the list of identifiers. For example:
%integer %array (1:5,1:4) table or %integer %array table (1:5,1:4)
%integer %array (0:*) %name a or %integer %array %name a (0:*)
%short %array (1:%byte hi) %name c or %short %array %name c (1:%byte hi)

The former position is mandatory for the implicit case.

† As in Pascal, a multi-dimensional array is regarded as an array of arrays, and inner slices of it may be used as arrays of lower dimensionality. For example, if TABLE is an array with bounds (0:3,1:40), then TABLE(0) is an array with bounds (1:40).

Example array declarations:

Pointer variables

Pointer variables are declared by including the keyword %name after the data-type. For example:

%byte %name pos
%own %string (*) %name details
%record (cellinfo) %name head,next,last

The combinations %name %array, %array %name and %name %array %name are all permissible, but not any other combinations.

Untyped pointer variables

† Untyped pointer variables are declared by including just the keyword %name without a data-type. For example:

%routine dump store(%name from, %integer bytes)

Pointers of arbitrary type may be name-assigned to such variables.

Untyped pointer variables are characterised simply by an address; they have no type or size information associated with them. The pre-defined function SIZEOF is not applicable to such variables, nor to variables of type %string (*) %name or %record (*) %name.

Labels

The label declaration is optional except for labels which are forward-referenced in machine-code instructions or ADDR() expressions.

Dummy identifiers

† In an identifier list within a declaration of scalars (not arrays), it is permitted to use an asterisk ('*') in place of an identifier.

Storage is allocated in the usual way for the anonymous variable. The main use of this facility is in record formats describing memory-mapped devices with non-contiguous fields.

Procedures

Procedures as Parameters

Procedures (routines, functions, and maps) may themselves be passed as parameters to other procedures. However, it is not permitted by this technique to circumvent the usual scope rules of block structure. That is, a procedure A may not be passed as argument to a procedure B unless A could be called by B directly, with the same effect. To police this rule, the Compiler requires procedures passed as parameters to be in scope to the procedures to which they are passed, not just to be in scope to the point of call. A procedure spec may be used to achieve this in cases where it would be inconvenient to place the body earlier.

The application of the rule to external procedures implies that a procedure passed as a parameter to an external procedure should itself be external. In this way, the parameter satisfies the requirement that it could be called directly (through the external linkage mechanism).

Over-loading

† To cover some of the situations in which it is necessary to have more than one procedure performing essentially the same operation but on arguments of different types, it is permitted to declare more than one procedure with the same name. This may be done provided that each such procedure can be distinguished from the others on the basis of the type of its leading (or sole) parameter. In each case the procedure type must be the same and either the leading parameters must all be 'value' or they must all be 'name'. Each procedure apart from the first must include the keyword alias after the procedure type. For example:

%routine put(%string(255) s)
    : : : : : : : :
%end

%routine %alias put(%real x)
    : : : : : : : :
%end

%routine %alias put(%integer i)
    : : : : : : : :
%end

%routine %alias put(%char i)
    : : : : : : : :
%end

In this case each call on PUT is treated as a call on the appropriate individual procedure determined by the type of the argument provided in the call. The order of matching is on the basis: most recently declared first. Hence in the examples above, it is relevant that the integer case occurs after the real case, since an integer is a valid argument for a real value parameter, under automatic type conversion. Similarly, it is relevant that the char case follows the string case, since a character is a valid string, under automatic type conversion.

The procedures do not all require to be in the same block, and some or all of them may be external. In the case of external procedures, each case will require to have a unique name for loading (through external name aliasing).

Instructions

Assignment

† Entire array assignment is permitted between two arrays with identical literal bounds. Array assignment to 0 is permitted for an array of literal bounds.

Routine call

† Repeated calls of the same routine may be elided to a single call, with the successive parameter lists separated by semi-colons. For example:
    get(i;j;k)
    put(n;" cases processed in ";t/1000;" seconds";snl)
This applies only to routines with parameters, not to other kinds of procedure.

Procedure return

† The form %return expression may be used as an alternative to the result , or true / false , instruction for specifying the result of a function, map or predicate.

Jump

† The form %goto is permitted as an alternative to '->'.

Loop control

† The instructions %exit and %continue may be followed by a simple decimal constant to identify an outer loop, for example %exit 2 or %continue 3. The default is 1 - the immediately containing loop.

† The effect of %continue is to pass control to the head of the containing loop, where any %while or %for control clause is tested; any %until clause attached to the %repeat at the end of the loop is ignored.

Monitor

The instruction %monitor calls the diagnostics package to monitor the current state of the program. Program execution is resumed after the call.

Expressions

Operators

Literal constants

and radix-based constants are supported for both integer and real. Character and string constants are ASCII. EBCDIC constants are not supported.

Cast

† A value of one scalar type may be cast as a value of another scalar type by using the type identifier of the latter as a cast constructor. In the case of two ordinal types, the effect is well-defined provided that the value being cast is within the ordinal range of the type to which it is being converted. In other cases, the effect is system dependent. The form is type-identifier [expression], for example, %integer ["a"] or %char [nl] or %real [16_C8400000].

String indexing

† Indexing may be applied to a string in the same way as for an array. The effect of a such a reference, say S(n), is to access the nth character of the string. The type of the indexed element is char.

Name-relative addressing

The name-relative addressing facility provides a means of accessing a sequence of items of the same type in contexts where array addressing may not be appropriate. It takes the form of a name variable followed by an index value enclosed in square brackets. Such an expression denotes a reference to the variable located that number of elements away from the element identified by the current assignment to the name variable.

Caution: this is a Low-level facility. There is no check on the validity of the references. Use of the facility is, however, preferable to the use of store-mapping functions in a similar role, because it preserves the type identity of the objects denoted. (The facility is referred to as 'Address modification' in the system dependent section of the IMP-77 Report).

For example, with the following definitions and assignment:

%record %format PAIR(real x,y)

%record(pair)%array A(1:100)

%record(pair)%name  P

P == A(7)
the following would hold: P[2] would denote A(9)
P[-6] would denote A(1)
P == P++1 would advance P to denote A(8)

Compilation options

Compiler options are generally established at the outset in the command used to invoke the Compiler. However, most of them may also be switched on and off within the program text by means of the %option directive.

Command parameters

There is only one obligatory parameter in the command used to call the Compiler - the first input parameter - and that is the name of the source program to be compiled. There is no default file-name extension for the source file-name. As a matter of personal preference, programmers may or may not choose to adopt the extension .IMP for IMP programs.

The other, optional, positional parameters are:

second input: pre-definition file to be included before main file
first output: object code file
second output: listing file

Option directive

The Option directive provides the means of specifying options within the source program. It takes the form of the keyword %option followed by a quoted string. For example:

%option "-NOWARN-NOLIST"
The quoted string consists of a sequence of Option keywords each preceded by a dash (hyphen).

The effect of most options is localised to the current procedure or block. That is, at the end of the block, the Options current at the start of the block are re-instated. In addition, the use of the option directive without a quoted string causes the state to revert to what it was immediately before the last option directive. These points do not apply to the global Options -EDIT, -LOG, -RUN and -FORCE.

Caution: indiscriminate variation of checking and diagnostic options within a program can create confusion for debugging.

Listing control

By default the Compiler does not produce a listing. If a listing is required, it is requested either by specifying a file-name as the second output parameter or by including one of the listing option keywords. Listing to a file, as opposed to listing to the terminal, may be selected only at the outset, in the command parameters.

The listing file consists of the text of the source file with added line numbers, and any fault or warning messages produced during compilation. Line numbering runs from 1 at the start of the file and includes blank lines and comment lines. Lines in included files are numbered independently, with an ampersand as an indicator.

Selecting any of the listing options also causes the Compiler to output UNUSED and UNDERUSED warnings for identifiers which are declared but not utilised. These warnings are not produced for certain classes of identifier, such as constant scalars and external specifications, which are often included as a complete package. A variable is regarded as underused if (a) being readable it is never read, or (b) being writeable it is never written to.

-LIST produce source file listing
listing name is derived from the source file-name (without the extension .IMP if present). The extension .LIS is applied.

-TTList send listing to terminal

-MAP program map
information at end of each procedure, indicating size of code, etc.

-LOG print log
statistics at end of compilation indicating number of statements, atoms per statement, identifiers per statement, time taken, etc.

The remaining listing options may not be available in all releases:

-CODE code listing
hybrid assembly language interpretation of code generated for each statement. This is before address fix-up and branch shortening, and so does not fully reflect the final code.

-DICT print dictionary entries for identifiers

-LOOP Undocumented

-EXP Undocumented

Object file control

By default, the Compiler generates an object file provided that no errors are detected during compilation.

The compiled code is native Motorola 68000 machine-code, ready for direct execution. The code file starts with a header which identifies the import and export lists, the code section with main and reset entry-points, and the diagnostic sections.

If no name is provided explicitly, as the first output parameter, the name is derived from the source file-name (without the extension .IMP if present). The extension .MOB (for Motorola object) is applied to the object file-name.

The generation of an object file may be suppressed by specifying the null file-name as the first output parameter.

-FORCE produce object file even if program faulty

-RUN run program after compilation (if supported)

Run-time checks

Any individual check may be disabled by citing the indicated keyword preceded by NO, for example -NOSTRASS or -NOCAP.

-ASS include unassigned check on full integers
-STRASS include unassigned check on strings
-SASS include unassigned check on 16-bit values
-BASS include unassigned check on 8-bit values
The unassigned check is implemented by standardising all newly declared dynamic variables to a fixed pattern which is an improbable integer value. The check is defaulted off for variables occupying less than 32 bits because there is a greater possibility of the special pattern occurring as a genuine value. Even so, the probability remains quite low, so that most programs can benefit for the additional checking implied by specifying -SASS and -BASS.

Defaults for unassigned checks: -ASS -STRASS -NOSASS -NOBASS

For the following three cases, violations which involve only literal operands are detected and rejected at compile-time rather than run-time.

-ARR include array bound checking
-OVER include overflow check
-CAP include capacity checking on assignment

-STACK include stack over-run check

Defaults: -ARR -NOOVER -CAP -STACK

-SYS suppress all checks apart from stack over-run

-NOCHECK suppress all checks

Run-time diagnostic control

-DIAG generate procedure identification information
-DIAG is the basic level of diagnostics, and is implied by selection of any of the other diagnostic options. So -LINE implies -DIAG, as does -MON, and hence -NODIAG implies both -NOLINE and -NOMON. In order to disable diagnostics for an individual procedure, -NODIAG should be asserted at the very start of the procedure. However, if a run-time error does occur in such a procedure when diagnostics have been selected for other parts of the program, the effect is unpredictable.

-LINE generate line number identification information

-MON generate variable monitoring information
This option requests that tables of identifier information should be included in the object file to allow the values of variables to be monitored. It is effective at the point where a variable is declared and may be freely switched on and off to select some variables and exclude others.

-TRACE enable tracing

-STEP causes code to be generated which allows the program to be executed one line at a time under the control of the Software Front Panel

Default: -DIAG -LINE -MON -NOTRACE

Compiler limits

-IDents n allow for n user idents (default 1200) the amount of space allowed for storing the text of identifiers is a function of the number allowed, so that this is also increased by increasing IDents. The upper limit is about 2500 to 4000 depending on diagnostic options.

-KBytes allow for codesize nK (default 64k, max 128k)

Compile-time control

-STRICT exclude use of non-standard features
-NONStandard enable warning-free use of non-standard features
the first example of the use of any individual non-standard feature is reported if this option is not enabled at the point where the feature is first used. This can be used to provide a degree of selectivity by switching on and off again.

-LOW enable use of low-level features

-VOLatile tells the Compiler to assume that functions and predicates are volatile by default

restricted multiply/divide

-HALF
-SHORT
causes the Compiler to assume that the arguments for multiply operations and the results of divide operations are limited to half or short values respectively. -WARN enable soft warnings
-EDIT enable editing
if selected, the detection of a hard error during compilation automatically causes control to be transferred to the editor for possible correction of the error. Compilation resumes when the edit is closed.

Default: -NONONS -NOSTRICT -NOLOW -VOL -NOHALF -NOSHORT -WARN -EDIT

Compiler Error Reports

Error reports are kept short to economise on screen space. For reports which relate to a particular component of a statement, the culprit is identified by a marker at the start of that component. An asterisk indicates a hard error, a query a soft warning.

Complexity errors

The 'out of reach' error reports relate to cases in which an attempt is made to generate a machine instruction with a displacement which is outside the range permitted for the instruction addressing mode. This may occur in the case of assembly language instructions, or accesses to variables declared with explicit address modes. It can also occur for automatically generated references.

For example, it can occur as a result of a static (%own) data storage requirement in a single module which exceeds 32 kilobytes. It is sometimes possible to overcome this limitation for a single large array by declaring it as the final %own, but note that external specs also require static storage, and the space for such a reference is allocated when and if used, not at the point of declaration.

For references to the code area, including stored constants, and references to dynamic variables, the Compiler uses various techniques to circumvent the +/- 32 kilobyte relative addressing limit of the M68000. There is a possibility in exceptional cases that these do not succeed, leading to an 'out-of-reach' report. If a name is given in the report, it is that of a procedure which is too distant from one of its calls. If no name is given, the problem is access to a constant or variable.

Disastrous errors

These reports relate to internal Compiler storage limits being exceeded. They all cause compilation to be abandoned.

Run-time errors and diagnostics

Errors detected at run-time are reported through the system's exception signalling mechanism (also used for IMP 'events'). If an exception is not trapped, the occurrence of the exception is reported in the form of an error code and text message, and diagnostics are produced.

For most errors, detection at run-time depends on whether particular checking options were selected at compile-time. The default compile-time options include most checks, but not unassigned checks on variables occupying less than 32 bits nor overflow checks.

For the fullest level of checking, the options -SASS -BASS -OVER should be enabled, over and above the default checks.

If relevant checks are not enabled, an error may manifest itself in the form of a hardware detected error, such as Bus Error or Illegal Instruction.

Exception conditions

The detailed distinctions of the sub-classification should not be taken as definitive. Some cases may be treated as identical by hardware or software detection mechanisms.
  1. PROGRAM TERMINATION
    1. normal termination (%stop)
    2. operator termination
    3. n hardware error (eg address error) + culprit
  2. OVERFLOW
    1. integer overflow
    2. real overflow
    3. string overflow (more than 255 characters)
    4. division by zero
  3. RESOURCE EXCEEDED
    1. store space exhausted
    2. output limit exceeded
    3. time limit exceeded
  4. INPUT/OUTPUT ERROR
    1. data transmission error
    2. timeout
    3. failure to open file
    4. failure on other file system operation
  5. INPUT DATA TYPE ERROR
    1. non-numeric char for numeric input + culprit
  6. INVALID ARGUMENTS
    1. invalid values for %for loop
    2. upper bound less than lower in array declaration
    3. illegal exponent for exponentiation + culprit
  7. RANGE ERROR
    1. value outside range
    2. array index out of bounds + culprit
    3. switch index out of bounds + culprit
  8. STRING RESOLUTION FAILURE
  9. UNDEFINED VALUE
    1. unassigned variable
    2. illegal address
  10. END OF INPUT
  11. LIBRARY PROCEDURE ERROR
  12. - 15. GENERAL PURPOSE

Diagnostics

Diagnostics are produced following the suspension of a program as a result of: The last case results from the use of the system attention interrupt (eg ^G or ^T) followed by '.' (dot) for diagnostics with termination or '?' (query) for diagnostics with continuation

The level, and amount, of diagnostic information produced depends on the diagnostic Options applied to the program at compilation (and to any modules to which it is linked).

Procedure trace-back

The basic level of diagnostic information is the procedure trace-back. This identifies the procedure in which the suspension occurred, then the procedure from which that was called, and so on up to the level of program invocation. Each procedure is identified by its name, and the name of the containing program file.

Line numbers

If line number diagnostics have been enabled, the line number of the suspension and each of the calls in the trace-back is also provided. An ampersand following the line number indicates that the line belongs to an included file.

Variable display

If variable monitoring diagnostics have been enabled, the values of the variables declared at each level of the trace-back are also displayed. In the case of a name variable, the address of the variable is displayed as well as the contents. However, %record %name variables are monitored to one level only. In the case of an array, the value of the first element only is displayed.

In all cases, no value is displayed in the case that the variable appears to be unassigned, just the name of the variable.

Note:

There is always the possibility, when a program goes wrong, that it will corrupt the diagnostic information or the program structure on which diagnosis depends. Similarly, when diagnostics are entered by operator intervention, it is possible for the program to be suspended in a state which prevents valid diagnostics from being produced. The diagnostic interpretation routines attempt to detect abnormalities of this kind and report on any corruption. Fortunately, in practice, these are rare occurrences, but the possibility should be borne in mind.

Another potential source of erroneous diagnostics is when programs and linked modules have been compiled with diagnostic options switched off in some parts and on in others. In general, diagnostics cannot be guaranteed to succeed if suspension occurs in a section compiled with diagnostic options disabled.

Pre-declared procedures

%integer %map  INTEGER(%integer a)
%real %map     REAL(%integer a)
%string(*)%map STRING(%integer a)
%record(*)%map RECORD(%integer a)
%byte %map     BYTE or BYTEINTEGER(%integer a)
%short %map    SHORT or SHORTINTEGER(%integer a)
%mite %map     MITE or MITEINTEGER(%integer a)
%half %map     HALF or HALFINTEGER(%integer a)
%byte %map     LENGTH(%string(*)%name s)
%byte %map     CHARNO(%string(*)%name s, %integer n)
%integer %function    ADDR(%name n)
%string(1)%function   TOSTRING(%integer k)
%string(255)%function SUBSTRING(%string(255) s, %integer from,to)
%integer %function REM(%integer a,b)
%integer %function MULDIV(%integer a,b,c)   {A*B//C without overflow}
%integer %function INTPT(%real x)
%integer %function INT(%real x)
%real %function FRACPT(%real x)
%real %function SQRT(%real x)
%integer %function CPUTIME                 {in milliseconds}
%name NIL
%record (*) %map NEW(%record (*) %name n)
%routine DISPOSE(%record (*) %name n)
%record %format EVENTFM(
  %byte event, sub,
  %short line,
  %integer extra,
  %string(255) message)
%record(eventfm) EVENT
%constant %integer NL
%constant %char SNL
%integer %function NEXTSYMBOL
%routine READSYMBOL(%name n)
%routine PRINTSYMBOL(%integer k)
%routine SKIPSYMBOL
%routine PRINTSTRING(%string (255) s)
%routine READ(%name n)
%routine WRITE(%integer m, n)
%routine PRINT(%real x, %integer n,m)
%routine PRINTFL(%real x, %integer n)
%routine NEWLINE
%routine NEWLINES(%integer i)
%routine SPACE
%routine SPACES(%integer i)
%routine SELECT INPUT(%integer n)
%routine SELECT OUTPUT(%integer n)
%routine CLOSE INPUT
%routine CLOSE OUTPUT
%routine SET INPUT(%integer  pos)
%routine SET OUTPUT(%integer pos)
%routine RESET INPUT         {equivalent to SET INPUT(0)}
%routine RESET OUTPUT        {equivalent to SET OUTPUT(0)}
%integer %function INSTREAM
%integer %function OUTSTREAM
%routine OPEN INPUT(%integer n, %string(255) S)
%routine OPEN OUTPUT(%integer n, %string(255) S)
%string(255)%function CLIPARAM
%routine PROMPT(%string(255) S)

Assembler in IMP

Sequences of assembly language instructions may be incorporated in an IMP program at any point. The Option -LOW should be asserted if this facility is used. Apart from the usual hazards of machine level programming, there is the danger of corrupting the environment pre-supposed by the high-level facilities. For this reason, it is sensible to keep the use of these instructions to a minimum and to take particular care in programming these sections.

Syntax

Assembly language instructions are distinguished from IMP statements by being prefaced by an asterisk (eg *MOVE D0,D1). They appear within IMP blocks and procedures and are executed according to the normal flow of control, subject to any control transfers invoked by the instructions themselves. IMP conventions for statement termination, labelling and commenting apply, but otherwise the form of instruction follows closely that defined under the heading of Assembler Syntax for each individual instruction in the Motorola M68000 manual.

Machine-level operands are specified using the mnemonics D0-D7 and A0-A7 (or SP), and the standard syntax for effective addressing (<ea>) modes. Note, however, that any declaration of IMP identifiers which coincide with the register mnemonics takes precedence. Immediate, Quick, and Address register variants of op-codes are selected automatically. In comparison with full Assembler there are the following omissions:

The error report 'Faulty operand' indicates use of an invalid operand for the context, but the Compiler does not fully check the validity of <ea> modes for the particular instruction.

Default size-code

The default 'size' applied is Long (32-bits) rather than Word (16-bits), which is the manufacturer's default. The choice is more appropriate for a high-level language in which the default integer size is 32 bits. Programmers who regularly use true Assembler may choose to make a practice of including the size suffix explicitly for all relevant instructions, to avoid confusion.

Defining temporary registers

The pseudo-assembler directive *TEMP may be used to define the temporary registers available to the Compiler. It is followed by a register-list in standard assembler form (eg D0-D2/A0-A1), or 0 to indicate none, or nothing to indicate the default temporary register set.

IMP identifiers in Assembler

IMP identifiers may also be used as operands for assembly language instructions, provided that they are directly addressable (see below). Record sub-fields and literally selected elements of arrays may also be accessed provided that the containing structure is directly addressable.

Labels and procedure names may be referenced in the Branch group of instructions, including BSR. The only alternative form of operand for these instructions is immediate (eg *BLT #-4), the value specified being the machine-level displacement. Short and long branches are handled automatically (though the Compiler's CODE listing does not show this). To access a forward label in an assembly language instruction, it is neccesary for the label to have been declared by means of the IMP declaration %label label-ident.

Execution environment

The preceding sections have described the forms of assembly language statements which are syntactically acceptable to the Compiler. This section provides some information about the environment of program execution which is presumed and maintained by the code generated by the Compiler. This has a bearing on the legitimacy and effects of assembly language statments (and IMP statements involving low-level features). It should be noted that this is an area which is subject to change between different versions and different operating system environments.

The registers defined as available for use as temporaries by the Compiler (by default D0-D4 and A0-A3), with the exception of D4, may be freely used within assembly sections, but no assumptions can be made about their contents after execution of most IMP statements. Other registers may be used only on the basis that their values are restored before reverting to IMP. In particular this applies to the stack pointer SP. In addition, the accessing of local variables may depend on SP; if SP is changed other than by Compiler-generated code, the addressing of these variables will be rendered erroneous. The addressing of global and own variables depends on A4, so that any modification of this register rules out access to non-local variables.

Directly addressable variables

The detailed definition of which variables can be directly addressed (and with what effect) over the whole range of data types is fairly complex. The following cases are reasonably straightforward and stable. Identifiers are directly addressable if they are:
           declared as own
       or  declared at the outermost level
       or  declared at the current level
       or  declared by means of an explicit address (@) declaration
       or  declared as registers
Array identifiers are directly addressable only if they meet the above requirements and their bounds are literal and their size is 'moderate'.

Variables declared at intermediate levels, and external objects, should not be used since an indirection may be involved in accessing them. In the case of a name variable the effective operand is the pointer value (32-bit address) rather than the referenced object.