9. The SMAL Loader and Object Sublanguages

Part of the SMAL Manual
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

  1. The Loader Sublanguage
  2. A SMAL32 Loader
  3. The Object Sublanguage

SMAL32 load files contain text in the loader sublanguage. The loader sublanguage is a subset of the SMAL32 object sublanguage, which is, in turn, a subset of the entire language. These sublanguages are very small subsets of the entire SMAL32 language; they only contain the bare minimum of information needed to satisfy their purpose.

9.1. The Loader Sublanguage

A SMAL32 load file consists of a fixed header followed by a sequence of load directives:

<load file> ::= R=.<end of line>
              { <load directive><end of line> }
                <end of file>

No blanks or blank lines are used within a load file unless they are specifically required by some particular load directive.

There are 6 basic load directives which control the setting of the loader location counter, the loading of words, and the setting of the starting address.

<load directive> ::= .=<load value>
                   | W<load value>
                   | T<load value>
                   | H<load value>
                   | B<load value>
                   | S<load value>

The value on each load directive may be either an absolute (hexadecimal) integer, or a relocatable value, specified by the use of the symbol "R".

<load value> ::= <hexadecimal number>
               | <hexadecimal number>+R
               | <space>R
<hexadecimal number> ::= #<extended number>

Every load file always ends with the location counter set just beyond the highest relocatable location used by that load file.

9.2. A SMAL32 Loader

The following Pascal function will read and load one SMAL load file from the file "f" and place it in the global array called "memory", relocated relative to "loc", the relocation base. The function returns the address of the first location beyond the last loaded relocatable location; in addition, if the object file contains starting address specifications set using S directives, the values of these will be set in the global variable "start". Generally, there should be only one S directive in any program!

function load (f:textfile; loc:integer): integer;
var temp: integer;
    ch: char;
    base: integer { the relocation base };

    procedure value;
    begin
        read(f,ch);
        if ch = '#' then begin
            temp := 0;
            repeat
                read(f,ch);
                if ch in ['0'..'9'] then begin
{precision}         temp := 16*temp + (ord(ch) - ord('0'));
                end else if ch in ['A'..'F'] then begin
{precision}         temp := 16*temp + (ord(ch) - ord('A')) + 10;
                end;
            until (ch = '+') or eoln(f);
            {assert ch in ['+','A'..'F','0'..'9']}
            if ch = '+' then begin
                read(f,ch) {assert ch='R'};
                temp := temp + base;
            end;
        end else begin
            {assert ch=' '}
            read(f,ch) {assert ch='R'};
            temp := base;
        end;
        {assert eoln(f)}
    end {value};

begin
    reset(f);
    while not(eof(f)) do begin
        read(f,ch);
        { assert f in [".","B","H","T","W","S","R"] }
        case ch of
            ".": begin
                     read(f,ch) {assert ch='='};
                     value;
                     loc := temp;
                 end;
            "B": begin
                     value;
                     memory[loc] := byte0(temp);
                     loc := loc + 1;
                 end;
            "H": begin
                     value;
{byte order}         memory[loc] := byte0(temp);
{byte order}         memory[loc+1] := byte1(temp);
                     loc := loc + 2;
                 end;
            "T": begin
                     value;
{byte order}         memory[loc] := byte0(temp);
{byte order}         memory[loc+1] := byte1(temp);
{byte order}         memory[loc+2] := byte2(temp);
                     loc := loc + 3;
                 end;
            "W": begin
                     value;
{byte order}         memory[loc] := byte0(temp);
{byte order}         memory[loc+1] := byte1(temp);
{byte order}         memory[loc+2] := byte2(temp);
{byte order}         memory[loc+3] := byte3(temp);
                     loc := loc + 4;
                 end;
            "S": begin
                     { start should not previously have been set }
                     value;
                     start := temp;
                 end;
            "R": begin
                     read(f,ch) {assert ch='='};
                     read(f,ch) {assert ch='.'};
                     base := loc;
                 end
        end {case};
        {assert eoln(f)}
        readln(f);
    end {while};
    load := loc;
end {load};

The above code is not resiliant in the sense that it makes no effort to detect errors or make use of the redundancy inherent in the object language; if such resiliancy is desired, comments included in it give assertions about conditions which can be tested.

This code assumes that the arithmetic operations available to it support unsigned 32 bit integer values. If it is to be used on a machine supporting an insufficient range of integers, the the arithmetic operators on the lines marked with a "precision" comment must be modified. If the code is rewritten in machine language, the multiply operators should be replaced with shift operators.

This code uses 4 functions, "byte0" through "byte3", that return successive bytes of the value to be loaded. Of these, "byte0" always returns the least significant byte, and "byte3" always returns the most significant byte. The code given here is for a machine that stores the least significant byte first. For machines that store the most significant byte first, the order of the calls to "byte0" through "byte3" must be changed on the lines with the comment "byte order".

9.3. The Object Sublanguage

A SMAL32 object file consists of a fixed header identical to that used on load files, followed by a sequence of object directives, followed by an optional sequence of internal definitions:

<object file> ::= R=.<end of line>
                { <object directive><end of line> }
                { <internal definition><end of line> }
                  <end of file>

As in load files, spaces and blank lines are not included in object files unless they are explicitly needed. In the case of object files, however, this rule need not be strictly followed; spaces are allowed wherever the assembler will accept them.

There is a form of object directive which corresponds to each of the loader directives, but the forms of the allowed values are more complex, and a new compound directive is used to manage common declarations:

<object directive> ::= .=<object value>
                     | W<object value>
                     | T<object value>
                     | H<object value>
                     | B<object value>
                     | S<object value>
                     | <common definition>

The value specified on each basic object directive may be either an absolute integer or a relocatable value and a specification of the relocation base:

<object value> ::= <hexadecimal number>
                 | <hexadecimal number>+<relocation base>
                 | <space><relocation base>
<relocation base> ::= R | R<identifier>

Note that "R" is the default relocation base, and that any other form of relocation base is a reference to an external symbol defined in the internal definition section of some other object file.

Common declarations in a SMAL32 source program result in the following special form of object directive:

<common definition> ::= IF\\DEF(S<identifier>)<end of line>
                         S<identifier>=#<extended number><end of line>
                        ENDIF<end of line>
                        IF\\DEF(R<identifier>)<end of line>
                         R<identifier>=C<end of line>
                         C=C+S<identifier><end of line>
                         CT=.<end of line>
                         .=C<end of line>
                         .=CT<end of line>
                        ENDIF

Each common directive in a SMAL32 source program will result in a sequence of object code patterned on the above, where all identifiers are replaced by the common name, and the extended number is replaced by the common size in hexadecimal. The common size passed through by the assembler will be rounded up to an integer number of words to help assure word alignment of commons.

If the source program contains any internal symbol definitions, these will be gathered together at the end of the object file and encoded in the following form:

<internal definition> ::= R<identifier>=<object value>

If the relocatable part of the object program does not consist of an integer number of words, the assembly origin will also be set in order to round the size up to the nearest word boundary. This assures that all relocatable object files will be word aligned, assuming that the linker aligns the first in the sequence of object files.