Super Prev Next

Low-level programming


Super Prev Next

Data representation

The internal representation of values of Modula-2 and Oberon-2 basic types is described in the tables 13. Representation of Modula-2 basic types and 14. Representation of Oberon-2 basic types. In the table 15. Representation of SYSTEM types the representation of system types is described.

Table 13. Representation of Modula-2 basic types

Modula-2 type Bits Representation
SHORTINT 8 signed
INTEGER 16/32 signed (See Modula-2 INTEGER and CARDINAL types)
LONGINT 32 signed
SHORTCARD 8 unsigned
CARDINAL 16/32 unsigned (See Modula-2 INTEGER and CARDINAL types)
LONGCARD 32 unsigned
CHAR 8 unsigned
BOOLEAN 8/32 unsigned (See Modula-2 BOOLEAN type)
    0 for FALSE, 1 for TRUE
subranges   according to the base type
REAL 32 80x87 single-precision data format
LONGREAL 64 80x87 double-precision data format
LONGLONGREAL 80 80x87 extended-precision data format

Table 14. Representation of Oberon-2 basic types

Oberon-2 type Bits Representation
SHORTINT 8 signed
INTEGER 16 signed
LONGINT 32 signed
CHAR 8 unsigned
BOOLEAN 8 unsigned byte
    0 for FALSE, 1 for TRUE
REAL 32 80x87 single-precision data format
LONGREAL 64 80x87 double-precision data format
LONGLONGREAL 80 80x87 extended-precision data format
SET 32 packed set

Table 15. Representation of SYSTEM types

System type Bits Representation
ADDRESS 32 unsigned
BOOL8 8 unsigned
BOOL16 16 unsigned
BOOL32 32 unsigned
BYTE 8 unsigned
CARD8 8 unsigned
CARD16 16 unsigned
CARD32 32 unsigned
INT8 8 signed
INT16 16 signed
INT32 32 signed
LOC 8 unsigned
WORD 32 ARRAY [0..3] OF LOC


Super Prev Next

Modula-2 INTEGER and CARDINAL types

If the option M2BASE16 is OFF, objects of types INTEGER and CARDINAL are 4 bytes (32 bits) long, otherwise they are 2 bytes (16 bits) long.


Super Prev Next

Modula-2 BOOLEAN type

A value of the type BOOLEAN occupies 1 byte of memory.


Super Prev Next

Modula-2 enumeration types

Representation of enumeration type values depends on the current ENUMSIZE equation setting. Values of an enumeration type which fits the specified size (1, 2, or 4 bytes) occupy exactly that number of bytes; otherwise the smallest suitable size from that list is taken.


Super Prev Next

Modula-2 set types

Sete are represented as bit arrays. The SETSIZE equation specifies the default size for small sets (1, 2, or 4 bytes).

If the option M2BASE16 is OFF, the type BITSET is represented by 32 bits, otherwise by 16 bits.


Super Prev Next

Pointer, address, and opaque types

The XDS compiler allocates 4 bytes of storage for a value of a pointer, address, or opaque type. Address arithmetic is implemented as 32-bit unsigned arithmetic without overflow checks.


Super Prev Next

Procedure types

Procedure types are represented by 4 bytes which hold an address of a procedure entry point in the task code segment.


Super Prev Next

Record types

Records are represented by a continuous memory segment containing all record components (fields) in a representation corresponding to their types. The compiler aligns each field according to its size and the current alignment (1,2,4, or 8), which may be set with the ALIGNMENT equation. Fields, which sizes, being rounded to the nearest power of 2, are less or equal to the current alignment, are placed at offsets which are multiple of their (rounded) sizes. Offsets of all other fields are multiples of the current alignment. Variant parts are aligned at the largest alignment of variant fields. Size of a record is rounded so that size of an array of such records is a multiple of the record size and the number of elements in the array, and each record in the array is correctly aligned.

    TYPE
      R1 = RECORD                (* ALIGNMENT   1   2   4  *)
             f1: CHAR;           (* f1 offset   0   0   0  *)
             f2: SYSTEM.CARD16;  (* f2 offset   1   2   2  *)
             f3: SYSTEM.CARD16;  (* f3 offset   3   4   4  *)
             f4: CARDINAL;       (* f4 offset   5   6   8  *)
             f5: CHAR;           (* f1 offset   9   10  12 *)
           END;                  (* SIZE(R1)    10  12  16 *)


Super Prev Next

Array types

An array is represented by a continuous memory segment containing all array elements in a representation corresponding to their type.

Note that elements within an array can be aligned, so in general for

    TYPE A = ARRAY [0..N-1] OF T;

SIZE(A) may be not equal to SIZE(T) * N.

Open arrays, as well as procedure formal parameters of type ARRAY OF ... ARRAY OF T, are represented by an open array descriptor. For an N-dimensional open array, the descriptor is an array of 2N 32-bit elements, which are:

Let A be a dynamic 3-dimensional array of INTEGER (SIZE(INTEGER)=2 in Oberon-2) created as

    NEW(A,4,3,6)

then its descriptor is a 6-element array containing:

     #0:  Address of array itself
     #1:   6
     #2:  12   (6*2)
     #3:   3   
     #4:  36   (12*3)
     #5:   4


Super Prev Next

Sequence parameters

The array of bytes which is passed to a procedure in place of a formal SEQ-parameter is formed as follows:

Super Prev Next

Example

    PROCEDURE write(SEQ args: SYSTEM.BYTE);
    BEGIN
    END write;

    VAR i: INTEGER;
        c: SYSTEM.CARD8;
        r: LONGREAL;
        S: RECORD a: LONGINT; c: CHAR END;
        p: POINTER TO ARRAY OF CHAR;
     .  .  .

    write(i,c,S,r,p^);

For this call the actual byte array passed to write will contain:


Super Prev Next

Calling and naming conventions

The calling and naming conventions for Modula-2, Oberon-2, and foreign procedures are described in this section.


Super Prev Next

General considerations

All parameters are always passed on the stack. The number of bytes occupied by a parameter is a multiple of 4. High-order bytes of parameters which are of shorter types (e.g. CHAR, SYSTEM.CARD16) are undefined.

Value parameters of scalar types (boolean, character, enumeration, whole, range, real, pointer, opaque, and procedure) and sets of size not greater than 32 bit are placed onto the stack. A complex type value parameter is passed as a pair of real.

Value parameters of all other types (even an array of a single CHAR) are passed by reference. A procedure is responsible for copying its non-scalar value parameter onto the stack, unless it is marked as read-only.

Warning: In C, a caller should copy value parameters of structure type onto the stack. You should provide a wrapper C function which receives these parameters by reference. Fortunately, this is a very rare case.

Note: The number of 4-byte words pushed onto the stack is passed in the AL register to a "SysCall" foreign procedure.


Super Prev Next

Open arrays

For an N-dimensional open array parameter N+1 parameters are actually passed — the address of the array and its sizes in all dimensions from left to right. This is true for Modula-2 and Oberon-2 procedures only. In case of a foreign procedure, only the address is passed.


Super Prev Next

Oberon-2 records

To a formal VAR-parameter which type is an Oberon-2 record type, the address of the actual parameter and the address of its dynamic type descriptor are passed.


Super Prev Next

Result parameter

If a function procedure result type is not scalar, it receives one extra parameter — the address of a temporary variable in which the procedure should store the reslut. Note: This may be incompatible with C.

A complex result is returned as a record with two real fields.


Super Prev Next

Nested procedures

A nested Modula-2 or Oberon-2 procedure, which access scopes of outer procedures, receives their bases as extra parameters. More precisely, the procedure P receives bases of all outer procedures which scopes are accessed by P or any procedure nested in P.

The base of a procedure is the address at which the procedure’s return address resides on the stack.


Super Prev Next

Oberon-2 receivers

An extra parameter — receiver — is passed to an Oberon-2 type bound procedure. A reference to its dynamic type descriptor is also passed if the receiver is declared as a VAR-parameter.


Super Prev Next

Sequence parameters

Sequence parameters for a Modula-2/Oberon-2 procedure are collected into a temporary variable, which is then passed as an ARRAY OF BYTE (i.e. its address and size are passed). For foreign procedures, a C-compatible approach is used — parameters are pushed onto the stack. In either case, all ordinal type parameters are extended to 4 bytes, REALs to LONGREALs, non-scalar type parameters are passed by reference.


Super Prev Next

Order of parameters

The abstract order of parameters (all categories are optional):

Actual order, in all cases except "Pascal" foreign procedures, is from-right-to-left, i.e. the last sequence parameter is pushed onto the stack first, the result parameter is pushed last.


Super Prev Next

Stack cleanup

The stack space allocated for parameters has to be freed upon return from a procedure. Depending on the language of the procedure, it is performed by the caller ("C" and "SysCall") or the procedure itself (Modula-2, Oberon-2, "StdCall", "Pascal").


Super Prev Next

Register usage

A procedure must preserve reisters ESI, EDI, EBP, and EBX registers, keep ES=DS, and clear the D flag.

The FPU stack must be empty before a call to a procedure and upon return from it. Exceptions are procedures which return REAL or LONGREAL. In this case, the result is placed in ST(0).

Note: If the CC equation is set to either "WATCOM" or "SYMANTEC", foreign procedures declared as "C" are considered to return REAL results in EAX, and LONGREAL results in EAX (low order bytes) and EDX (high order bytes).


Super Prev Next

Naming conventions

External names of exported procedures in object modules are built accroding to the following rules:

Convention Name is As in
"Modula" prepended with the module name and "_" Module_Proc
"Oberon" ditto Module_Proc
"C" prepended with "_" (see note) _Proc
"Pascal" capitalized PROC
"StdCall" unchanged Proc
"SysCall" unchanged Proc

Note: If the CC equation is set to "WATCOM", external names of "C" foreign procedures are not prepended with an underscore character.