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 |
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.
A value of the type BOOLEAN occupies 1 byte of memory.
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.
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.
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.
Procedure types are represented by 4 bytes which hold an address of a procedure entry point in the task code segment.
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 *)
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
The array of bytes which is passed to a procedure in place of a formal SEQ-parameter is formed as follows:
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:
The calling and naming conventions for Modula-2, Oberon-2, and foreign procedures are described in this section.
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.
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.
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.
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.
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.
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.
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.
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.
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").
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).
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.