In Oberon it is impossible to declare handles in the way as in Pascal, since it is not allowed to define a type to be a pointer to a pointer. Consider the ControlRecord defined by the Control Manager. Such a record is allocated and initialized by the Control Manager for every control created. The application gets a handle as its reference to the new control. If a ControlRecord and the corresponding ControlHandle should be declared in standard Oberon, a "dummy" record type is needed to access the master pointer.
TYPE ControlPtr* = POINTER TO ControlRecord; ControlHandle* = POINTER TO RECORD mp*: ControlPtr END; ControlRecord* = RECORD nextControl*: ControlHandle; (* more fields *) END;Accessing the field nextControl in a control whose handle is stored in variable x requires to write x.mp.nextControl. The intermediate field mp makes it harder to read the program, since the specifier gets longer. Handles and pointers, both pointing to dynamic variables, should be treated the same way, i.e. is should be possible to access a handle's field without the intermediate record type.
MPW Oberon supports the additional keyword HANDLE which may be used instead of POINTER to define a handle to a dynamic variable. The new keyword HANDLE simplifies both declaring and accessing a handles. Fields within dynamic variables are treated equally and the compiler takes care of dereferencing the handle. The declaration of ControlHandle would be written in MPW Oberon as:
TYPE ControlHandle* = HANDLE TO ControlRecord;The specifier for field nextControl in control x may now be written as x.nextControl simplifying access to handles considerably. Types declared using HANDLE are called handle types.
Note: If you use an anonymous type within a handle declaration no tag field will be added to the dynamic variable. The first field within the record will start at the first byte of the handle. A declaration involving a anonymous type will look like
TYPE AHandle = HANDLE TO RECORD field1: LONGINT; (* more fields *) END;In this example the record containing field1 is anonymous and field1 has an offset of 0 within the handle. MPW Oberon will assign offset 0 to field1 whether or type tags are requested. You should not rely on this feature as it may change with the introduction of garbage collection.
The standard procedure NEW was extended to handle types declared using HANDLE. If NEW is passed a variable whose type is a handle-type, NewHandle will be called to allocate the dynamic variable. The standard procedure DISPOSE used to release dynamic variables handles handle types correctly.
The HANDLE extension of MPW Oberon allows to use handles instead of pointers without any changes requires to the source code. By using handles the usage of memory may become more efficient than by using pointers since the Memory Manager may move handles around.
Warning: There is an incompatibility between pointers and handles namely
there behavior when new blocks are allocated. Dynamic variables referred to by
pointers never change their position within the heap whereas handles may be moved
by the Memory Manager unless they are locked. You should be careful when you pass
fields within dynamic variables to a procedure which may move memory. See the
chapter "Memory Manager" in Inside Macintosh: Memory for further details.
Inline and external procedures
The routines contained in the Macintosh ROMs are accessed using the A-trap
mechanism of the M68000. Activation of a trap is similar to the activation of a
regular procedure written in Oberon. Parameters are pushed onto the stack and the
trap is called. The compiler inserts the trap word instead of a subroutine call
to activate the trap. In Oberon there is no provision for such a scheme. MPW
Oberon supports additional keyword INLINE for declaring inline procedures.
Inline procedures are not strictly necessary but you would have to provide a glue routine for every toolbox trap. This glue routine would be called at run time to execute the trap. In standard Oberon the problem of calling these glue routines which are written in assembly language remains.
The syntax for declaring inline procedure is very similar to the one used by MPW Pascal. This convention makes porting existing sources and interface files easier. Consider the procedure HLock provided by the Memory Manager. In MPW Oberon this routine may be declared as:
PROCEDURE HLock*(h: Types.Handle); INLINE PASCAL $205F,$A029;The keyword INLINE indicates that the procedure HLock has no body written in Oberon. For an inline procedure you may specify up to 16 opcodes. These opcodes are inserted instead of a procedure call after the procedure's parameters were pushed onto the stack.
The routines of the Macintosh' toolbox use the Pascal calling convention. MPW Oberon supports this convention. For inline procedures the Pascal calling convention is indicated by the word PASCAL following INLINE. If PASCAL is omitted, the calling convention used by MPW Oberon applies (see "Oberon calling convention").
Note: To make porting easier MPW Oberon supports a Motorola notation for hexadecimal numbers. A hexadecimal number is indicated by a leading $-sign which is followed by the digits of the number.
External procedure are treated very similarly. Instead of INLINE the keyword EXTERNAL is used to indicate that the procedure is not implemented in Oberon. For external procedures the name of the procedure is passed to the linker as it is written in Oberon. Names of external procedures written in Pascal are converted to their uppercase equivalence by MPW Oberon. If you write a procedure Great in Pascal, the Pascal compiler will name it GREAT in the object file. MPW Oberon does the same for procedures declared as EXTERNAL PASCAL.
Note: The two additional keywords INLINE and EXTERNAL may be used only
when module SYSTEM is imported. Otherwise the compiler will report an error. By
enforcing this convention, system dependent modules are easily identified.
Compiler directives for the Macintosh
environment
Some requirements of the Macintosh environment can be handled by directives given
to the compiler. Directives are embedded into comments and the Oberon language is
not changed to support the additional features. Other compilers are able to
compile the module since comments are ignored.
This section describes the compiler directives needed in the Macintosh environment. For a complete list of the directives supported by MPW Oberon see "Compiler directives".
Segmentation control
A segment is a part of code that can be separately loaded into memory. Your
program can be written without explicit segmentation control or it can contain a
number of different segments. Segmentation helps reducing a program's run-time
memory requirements. Routines seldom executed are typically put into separate
segment that is not loaded at program begin. The segment is loaded when one of
the routines in it are executed.
Routines may be placed in different segments in two way. This section describes the standard segmentation scheme provided by the compiler and how to use the $S directive to specify segmentation. The documentation of MPW explains how to use the Link command to modify segmentation.
MPW Oberon puts the routines contained in one modules into three segments. One segment contains the routines in defined in that module. This segment has the same name as the corresponding module. The initialization code of the module is put into a special segment named "%InitModules". All type tags are stored into a separate segment called "%OberonTags".
The $S directive lets you specify several segments within a single source file. To assign source code to a segment, precede the code with a $S directive:
(*$S segment-name*)Code following this directive is placed in the specified segment until the compiler reads another $S or the end of the file is reached.
Note: Segment names are case sensitive. Leading spaces are ignored, but all characters up to the end of the comment are used.
Code for a given segment does not have to be contiguous within the source file. The program may take the following form:
(*$S SegA*) function (*$S SegB*) function (*$S SegA*)The compiler marks each routine with the name of its segment. The linker collects all procedures for a segment from various input files and places them into a single code segment.
(*$Parameter HLock(A0) *) PROCEDURE HLock*(h: Types.Handle); INLINE PASCAL $A029;The $Parameter directive must be given directly before the declaration of the corresponding procedure. The procedure names and parameter lists must match. Every parameter required by the procedure must be assigned to a register.
The registers are specified in the standard notation defined by Motorola. You may use the registers D0-D7, A0-A7 and FP0-FP7 for passing parameters. The registers of the MC68881 may be used only if code for the MC68881 is generated.
Note: MPW Oberon currently supports the $Parameter directive only for inline procedures. You may not used it with external procedures or procedure variables.
Passing dynamic structures to the operating
system
Every dynamic variable allocated on heap by the Oberon standard procedure NEW has
a type tag used to test the type of the variable (see "Type tags in MPW Oberon" for further information). The
so-called tag field is invisible to the user and precedes the information stored
in the dynamic variable. The Macintosh's operating system expects that data
within a dynamic block start at the first byte within that block.
Note: Tag fields used in Oberon to distinguish types of dynamic variables should not be confused with the tag fields used in Pascal. In Pascal tag fields are used to distinguish the variants of a variant record. Variant records are not supported by Oberon. Type extension is used in Oberon instead of variant records.
Note: See "Specifying string constants" for further details about passing literal strings to the operating system.
MPW Oberon supports the $TAGS- directive for declaring data types without type tags. If a dynamic variable is allocated using the standard procedure NEW, no type tag will be added. All data types defined in Inside Macintosh are defined using the $TAGS- directive. For example the type ControlRecord defined by the Control Manager is declared in MPW Oberon as:
TYPE (*$TAGS-*) ControlPtr* = POINTER TO ControlRecord; ControlHandle* = HANDLE TO ControlRecord; ControlRecord* = RECORD nextHandle*: ControlHandle; (* more fields *) END; (*$TAGS+*)All data types declared after a $TAGS- directive are marked by the compiler as untagged and dynamic variables allocated for those types will not carry a type tag. The $TAGS- directive remains in effect until the end of the source file or until a $TAGS+ directive is encountered. You may use any number of $TAGS- directives in a module.
If you extend an untagged type, its extension is untagged too. There is no way to add a type tag to an untagged type. Since dynamic variables of untagged types contain no type tags, type test or type guards are not possible with those types. You may use the $TYPECHECK directive to override the behavior of MPW Oberon in this respect.
Warning: The $TYPECHECK directive lets you to use the WITH statement as a type cast. Consider a procedure updating either a window or a dialog in response to an update event. The following code fragment show the WITH statement used as a type cast.
(*$TYPECHECK- ; no type checks please*) IF window.windowKind=Windows.dialogKind THEN (* window refers to a dialog *) WITH window: Dialogs: DialogPtr DO (* do something useful *) END ELSE END (*$TYPECHECK+; turn type checks on*)This fragment is correct because the windowKind field maintained by the Window Manager indicates the type of window and hence the size of the corresponding data structure (see the chapter "Window Manager" in Inside Macintosh: Macintosh Toolbox Essentials for further information about the windowKind field). Nevertheless this technique is rather dangerous and should be used with caution.
Integers may also be specified using a binary notation which is also the standard
Motorola notation. A binary number is denoted by a %-sign followed by the
number's digits. A binary number must not contain more than 32 digits. This
notation is especially useful when you want to assemble opcodes.
Additional notation for operators
Oberon is defined with ASCII as its alphabet. The extended character set of the
Macintosh contains a few characters that may be used to enhance the readability
of source code. The following table indicates the alternate notation supported by
MPW Oberon for some Oberon operators.
| <= | ≤ |
| >= | ≥ |
| # | ≠ |
| ~ | ¬ |
| & | AND |
In the Oberon interface files OSType is defined as a 32-bit integer. For the assignment of constants to variables of this type the standard procedure LONG was extended. Besides integers a string with four visible characters may be passed to LONG. For example
x:=LONG("TEXT")
assigns $54455854 to the variable x. You may notice that LONG("TEXT") is much
more readable than $54455854.
MPW Oberon supports the Modula-2's notation for casting types. Using this
notation expression e is casted to type T by writing T(e). The alternate notation
may by used only when the module SYSTEM is imported.
Specifying string constants
Strings in standard Oberon may contain only the visible characters of the
international alphabet No. 5 (ASCII) since this is the alphabet of the Oberon
programming language. The Macintosh provides an extended character set with many
useful characters like umlauts, German double-s and accented characters.
Furthermore a string should contain invisible character line carriage returns or
line feeds. Since these characters are use for terminating a line and literal
strings must not span lines, these characters are not allowed in a string
constant.
MPW Oberon supports an extended string notation similar to the one supported by the C language. You may use the backslash character "\" to include characters into a string by specifying their numerical values. Character values must be specified as decimal values with three digits. The following string contains "hello world" followed by a carriage return and a line feed:
"hello world\013\$0A"The Macintosh toolbox uses strings in the Pascal format. This format is different from the one used by Oberon. The Pascal format consists of a length byte followed by the string's characters. In Oberon strings are terminated with a null characters just like in C. MPW Oberon supports the same mechanism for defining literal strings in the Pascal format as MPW C. To defines a literal string in Pascal format write "\P" after the string's opening quote. The following string is the same string as above but this time as a Pascal string:
"\Phello world\013\$0A"However MPW Oberon tries to be clever about strings. If you call a procedure that uses the Pascal convention for passing parameters, MPW Oberon converts a literal strings passed to that procedure to the Pascal format. If you want to pass the string "Item 1;Item 2" to the Menu Manager's function AddMenu, write:
Menus.AddMenu(menu, "Item 1;Item 2")This conversion takes place at compile time for every parameter that is declared as an array of characters.
Note: The standard procedures and functions supported by MPW Oberon for strings expect their strings to be in the Oberon format. You should be careful about mixing strings of the two formats.