			 Compiler Extensions
			 ===================

    The compiler has been extended with extra features. Most of which are
    designed to make interfacing with the amiga operating system easier.
    Other features are designed to make programming in Modula-2 less annoying.
    Avoid using these extensions if you wish to develop portable modules.

Underscore
==========

    The underscore is a valid 'letter'.
    eg, '_Turbo_Modula_2' is a legal identifier.

SHORTCARD, SHORTINT AND SHORTREAL
=================================

    The types SHORTINT, SHORTCARD & SHORTREAL are not normally
    predeclared in other Modula-2 compilers:

    SHORTCARD is predeclared as [0..255]    (* byte sized cardinal *)
    SHORTINT  is predeclared as [-128..127] (* byte sized integer  *)
    SHORTREAL Motorola fast floating point.

Conversion Rules
================

    Signed (integer) and unsigned(cardinal) numeric subrange types may be freely
    mixed in expressions. The following conversion rules apply:

    VAR
      shortint  : SHORTINT  ;
      shortcard : SHORTCARD ;
      integer   : INTEGER ;
      ...

    shortint+shortcard   => VAL(INTEGER,shortint)+VAL(INTEGER,shortcard)
    shortint+cardinal    => VAL(LONGINT,shortint)+VAL(LONGINT,cardinal)

    shortint+integer     => VAL(INTEGER,shortint)+integer
    shortint+longint     => VAL(LONGINT,shortint)+longint

    shortcard+cardinal	 => VAL(CARDINAL,shortcard)+cardinal
    shortcard+integer	 => VAL(INTEGER,shortcard)+integer
    shortcard+longint    => VAL(LONGINT,shortcard)+longint

    integer+cardinal	 => VAL(LONGINT,integer)+VAL(LONGINT,cardinal)

    integer+longint	 => VAL(LONGINT,integer)+longint

    cardinal+longint	 => VAL(LONGINT,cardinal)+longint

    Where '+' could be any binary operator.
    The result type in each conversion should be obvious.

Qualified import identifier aliasing
====================================

    An imported global module name may be aliased.

    IMPORT Graphics , Intuition ;

    VAR
      rp  : Graphics.RastPortPtr ;
      win : Intuition.WindowPtr ;

    Can be rewritten:

    IMPORT G := Graphics , I := Intuition ;

    VAR
      rp  : G.RastPortPtr ;
      win : I.WindowPtr ;

    In V1.2 of the compiler, idenfifiers imported in local modules can also
    be aliased.

Imported library versions
=========================

    When importing an Amiga library module ( Dos, Intuition, Graphics etc ),
    an optional version number can be specified:

    IMPORT Intuition{33} ;

    FROM Dos{36} IMPORT System ;

    The values 33 & 36 will be passed to Exec.OpenLibrary by Intuition.o & Dos.o
    If no version number is specified, zero is assumed.

    Intuition.o, Dos.o etc will cache the highest version opened so far
    in order to reduce calls to Exec.OpenLibrary.

    The version number must be a decimal literal (not an arbitrary expression)
    this simplifies the parser in programs like M2B.

Structure assignment
====================

    It is possible to assign to an array or record structure in a single
    statement, the right hand side being a list of bracketed expressions.

    The syntax of the assignment statement is extended to:

    $ assignment  =  designator ":=" ass_rhs .
    $ ass_rhs  = "["[asslist]"]" | expression.
    $ asslist = ass_rhs{,ass_rhs}

    VAR x : ARRAY [0..6] OF INTEGER ;

    x := [f(y),2,4,a,0,0,0] ; (* The expressions need NOT be constant *)

    Trailing elements/fields need not be assigned to,in which case they will
    be zeroed.Any automatically inserted compiler padding fields will also be 0.

    x := [f(y),2,4,a] ; (* same as above *)

    If a record contains variant fields then the compiler assumes (and checks
    against) the first variant.In other words you can only assign to the
    first variant field(s).

    examples:

    VAR
      matrix  = ARRAY [0..3],[0..2],[0..2] OF LONGINT  ;

    PROCEDURE InitMatrix ;
    BEGIN
      matrix:=[[[7FFFH,   0,   0],[    0,7FFFH,   0],[    0,    0,7FFFH]],
	       [[32642,   0,2856],[    0,7FFFH,   0],[-2856,    0,32642]],
	       [[32642,2856,   0],[-2856,32642,   0],[    0,    0,7FFFH]],
	       [[7FFFH,   0,   0],[    0,32642,2856],[    0,-2856,32642]]] ;
    END InitMatrix ;

    TYPE
      NewMenu = RECORD (* Gadtools structure *)
        nm_Type		 : SHORTCARD ;
        CASE : INTEGER OF
        | 0 : nm_Label	 : STRING   ;
        | 1 : nm_Image	 : ImagePtr ;
        END ;
        nm_CommKey	 : STRING  ;
        nm_Flags	 : BITSET  ;
        nm_MutualExclude : LONGINT ;
        nm_UserData	 : ADDRESS ;
      END ;

    VAR
      demomenu : ARRAY [0..11] OF NewMenu ;

    BEGIN
      demomenu :=
         [
          [ NM_TITLE,"Project" ],
	  [ NM_ITEM, "Run",               "R", {}, 0, MENU_RUN   ],
	  [ NM_ITEM, "Step",              "S", {}, 0, MENU_STEP  ],
	  [ NM_ITEM, NM_BARLABEL ],
	  [ NM_ITEM, "Slower Horizontal", "1", {}, 0, MENU_HSLOW ],
	  [ NM_ITEM, "Faster Horizontal", "2", {}, 0, MENU_HFAST ],
	  [ NM_ITEM, "Slower Vertical",   "3", {}, 0, MENU_VSLOW ],
	  [ NM_ITEM, "Faster Vertical",   "4", {}, 0, MENU_VFAST ],
	  [ NM_ITEM, NM_BARLABEL ],
	  [ NM_ITEM, "Quit",              "Q", {}, 0, MENU_QUIT  ],
 	  [ NM_END ]
 	 ] ;

Open array heap variables
=========================

    In Modula-2 it is possible to declare open arrays only as formal parameters.
    The compiler allows open arrays to be declared as pointer base types.

    VAR
      p : POINTER TO ARRAY OF CHAR ;

    Like formal parameters, the open array must be 1 dimensional.

    The NEW substitution mechanism has to be extended to allow declarations
    of such heap variables.

    NEW(p,exp) ; where exp is an integer/cardinal expression.

    This expands to ALLOCATE( p , exp*SIZE(p^[0]));

    There is no bounds checking associated with this type of array.
    It is not possible to do any operation on the open array,only individual
    elements may be accessed:

      The designator p^ may never appear on its own, the '^' must always be
      followed by a '[' eg p^[10].

    If the follow declarations exist:

      VAR
        x : POINTER TO ARRAY OF type ;
        y : ARRAY [low..hi] OF type ; (* normal array *)

    Then the assignment

     x := y ; is allowed. This is equivalent to
     x := ADR( y ) ;

     If the base type of the open array(x^[0]) is CHAR then,

     x := "help" ;      is allowed and is equivalent to
     x := ADR("help") ;

     The SYSTEM.STRING type is predeclared as POINTER TO ARRAY OF CHAR.
     The array pointed to by a STRING variable should always be 0C terminated.


     This kind of pointer occurs in the C language, and so is quite useful
     when programming with the amiga OS and the Ansi C libraries. However it is
     not true to Modula's strong typing philosophy and therefore its
     indiscriminate use should be avoided.

Pointer casting in selector lists
=================================

    It is possible to insert type casts in variable selector lists:

    foo := bar(NewMenuPtr)^.nm_Label(ImagePtr) ;

    The syntax of the designator is extended to:

    $ designator = qualident {"."ident|"["ExpList"]"|"("qualident")"| "^" }.

    This method of casting is restricted to (and from) pointer types.
    It is very useful when using Intuitions BOOPSI system.

Variable length argument lists
==============================

    A procedure may be declared to take a variable number of arguments.
    To declare such a procedure, the formal parameter list must end with '..':

    PROCEDURE VarArgsProc( x : INTEGER ; .. ) ;
    (* There must be at least 1 normal formal parameter *)

    There is no standard way of accessing the unknown parameters from Modula-2.
    This kind of declaration normally occur in Amiga OS & C interface modules.
    However you can declare them in implementation & program modules:

    PROCEDURE CreateGad( kind:LONGINT; VAR ng:GT.NewGadget; tag1:LONGINT; .. ) ;
    (* topazAttr, visualInfo, window, lastAdded are global variables *)
    BEGIN
      ng.ng_TextAttr    := ADR( topazAttr ) ;
      ng.ng_VisualInfo  := visualInfo ;
      INC( ng.ng_LeftEdge, window^.BorderLeft ) ;
      INC( ng.ng_TopEdge , window^.BorderTop  ) ;

      lastAdded := GT.CreateGadgetA( kind, lastAdded, ng, ADR( tag1 ) )
    END CreateGad ;

    If a SHORTREAL or REAL actual corresponds to a '..' formal
    then the parameter is converted to a LONGREAL before being passed.

Newline and tab characters
==========================

    String literals may contain newline (\n) & tab(\t) characters.

    InOut.WriteString("Hello\tworld\n");

    If '\' in a string is not followed by one of 't' 'n' '\' then the compiler
    will report an error.
    To represent '\' use '\\'.

    "\n" & "\t" are also legal character constants as well as strings.

Exit codes
==========

    A return statement inside the initialization statement sequence of the root
    program module may contain an optional integer expression. This expression,
    if executed, will represent the return code for the program.
    If no expression is supplied, or if execution falls through, 0 is returned.

    MODULE foo ;
     ...
    BEGIN
      RETURN 20
    END foo.

    The standard AmigaDOS return codes are:

    00: success
    05: warning
    10: something's wrong
    20: complete or severe failure

    Alternatively StdLib.exit(X), will terminate the program with return code X.

Coroutines
==========

    The pseudo module SYSTEM does not contain any coroutine facilities,
    instead the library module 'Coroutines' should be used
    (modula:m2lib/Coroutines.def).

SHORTSET BITSET & LONGSET constants
===================================

    The compiler predefines 3 set types.

    SHORTSET = SET OF [0..07] ; SIZE = 1 Byte
    BITSET   = SET OF [0..15] ; SIZE = 2 Byte
    LONGSET  = SET OF [0..31] ; SIZE = 4 Byte

    The type of an unqualified set constant has been extended to support these
    types.

       {}, {0}, {0,1} ... {0..7} are compatible with SHORTSET,BITSET & LONGSET
       {8}, {8,9} ... {8..15} are compatible with BITSET & LONGSET
       {16} .. {16..31} are only LONGSETs

       By compatible I mean both expression compatible & assignment compatible.

    VAR
      shortset : SHORTSET ; bitset : BITSET ; longset  : LONGSET  ;

    shortset:= {1} (*valid*); shortset:= {8} (*wrong*); shortset:= {16}(*wrong*)
    bitset  := {1} (*valid*); bitset  := {8} (*valid*); bitset  := {16}(*wrong*)
    longset := {1} (*valid*); longset := {8} (*valid*); longset := {16}(*valid*)

INCL & EXCL
===========

    As well as the normal definitions of the standard procedures INCL/EXCL
    the second parameter of these procedures may be a set expression.

    example:

      INCL(bitset,{1,2}) is identical to bitset := bitset+{1,2}.
      EXCL(bitset,{1,2}) is identical to bitset := bitset-{1,2}.

      However the INCL/EXCL versions generate atomic code.

BCPL pointers
=============

    The DOS operating subsystem was partly implemented in the BCPL programming
    language, which used long word pointers,as opposed to normal byte pointers.
    It is possible to declare a BCPL long word pointer in Turbo Modula-2

    TYPE
      FileLockPtr = BCPL POINTER TO FileLock ; (* BCPL is a reserved keyword *)

    Instances of this pointer can be dereferenced as normal.

    The type SYSTEM.BADDRESS exists and is analogous to the normal
    SYSTEM.ADDRESS type.

    If an assignment to a BCPL pointer from an ADDRESS typed expression occurs
    then the compiler will automatically perform the necessary conversion:

    VAR lock : FileLockPtr ; v : FileLock ;

      lock := ADR(v) ; (* will work, but only if v is long word aligned *)

    The reverse assignment from a BADDRESS expression to a normal pointer will
    also be converted properly.

    You must be careful not to cast between the 2 different kinds of pointers
    as no conversion will take place. Instead there are 2 functions in the
    SYSTEM module that can be used.

    PROCEDURE SYSTEM.BTOA( bp : BADDRESS ) : ADDRESS ;
    PROCEDURE SYSTEM.ATOB(  p : ADDRESS  ) : BADDRESS ;

    The base variable of a BCPL pointer should always be long word aligned.
    Modula-2 global variables are always longword aligned as is the memory
    allocated using the StdLib.malloc & Storage.ALLOCATE functions.

SHORTFLOAT & LONGFLOAT
======================

    As well as the normal FLOAT conversion function, which always converts
    to REAL, there are 2 other integer/cardinal->real conversion functions:

    SHORTFLOAT, converts an integer/cardinal into a SHORTREAL
    LONGFLOAT , converts an integer/cardinal into a LONGREAL

Psuedo Module System
====================

    The following types are declared in SYSTEM:

	ADDRESS
	BADDRESS	see above
	BYTE
	WORD
	LONGWORD	like BYTE & WORD except 32-bits long

	SHORTSET	(also pervasive)
	BITSET		(also pervasive)
	LONGSET		(also pervasive)
	SHORTREAL	(also pervasive)
	FFP		Motoral Fast Floating Point (FFP=SHORTREAL)
	STRING		see above

    Functions declared in SYSTEM:

	ADR
	TSIZE
	BTOA		see above
	ATOB		see above

	OFFSET		OFFSET( recordType , field ) : LONGINT
			Returns the byte offset of the field in the record.

	CAST		CAST( TYPEid,expression) : TYPEid
			Performs a typecast: same as to TYPEid(expression).
			eg str := CAST(STRING,98)
			   str := STRING(98)

	MAKEID		MAKEID( STRING ) : LONGINT
			String should <= 4 characters.

Comment Options
===============

    Unnested comments in your source code can be used to override command line
    options. The format is (* @X+ *) to enable an option or (* @X- *) to disable
    it. These comments should normally be placed at the top of your code.

    The options are:

    @C(+/-)	Enable/Disable Large Code option.
    @D(+/-)	Enable/Disable Large Data option.
    @B(+/-)	Enable/Disable Array Bounds Checking.
    @P(+/-)	Enable/Disable Pointer Checking.
    @A(+/-)	Enable/Disable Alternative BSS naming convention.

    There are also two other options which relate to individual procedures:

    @G See examples/src/Hook.mod
    @O See ansi-c/SetJmp.def

    Procedure switches should be placed between the procedure keyword & the
    procedure name:

    PROCEDURE (* @G *) CallBack( ) ;
    BEGIN ...

    The @G comment tells the compiler to generates code to set the A4 register
    to point to the global variable space when the procedure is entered
    (the old value is saved on entry an restored at exit), this is required
    if your program performs out of context calls when using the small data
    addressing model.
    You MUST use @G on procedures that are passed to the operating system
    for later invocation, eg Hook functions, CreateTask entry points etc.
    Unfortunately using @G means the program can not be made resident.

    The @O comement tells to the compiler not to allocate any locals to
    registers (not needed unless you use the SetJmp functions).

    When an open array is passed by value, the caller normally passes its
    address via the stack, its then the callees job to make a copy of it.
    The copying can be suppressed by using (* @N *) option, this option applies
    to a parameter list, it should be placed just afer the ':'

    PROCEDURE X( a , b : (*@N*) ARRAY OF CHAR ; c : ARRAY OF CHAR ) ;
    BEGIN

    In the above a & b will not be copied, while c will be.

    Use @N for efficiency if you know an open array value parameter will be
    strictly read-only. You must be careful, since it effectively converts
    value parameters into variable parameters, bypassing the compiler's normal
    safety checks. The PROGRAMMER must make sure not to make any assignments to
    such parameters, otherwise undesirable side effects may occur.

    The @G @O @N comments should only be used in a declaration for
    an implementation NOT in any forward declaration or definition module
    (in which case they will be ignored).

DEFINITION FOR C MODULE'S
=========================

    To make it easy to interface with code written in the 'C' language it
    is possible to declare non standard definition modules.
    There are 3 closely related kinds:

    1.DEFINITION FOR C MODULE (eg StdLib.def)

      There is no corresponding implementation module.
      This kind of definition module normally contains a list of declarations
      which are defined in some C library or object file.
      A normal Modula-2 module that wishes to use the functions/variables
      declared in this kind of module simply imports them as normal.
      To enable correct linking however, the library or object files must be
      specified on the command line

      >m2b CallsX.mod X.o X.lib

      See examples/for_c/ for an example of calling a 'C' function.

      The library (c.lib) which contains all the function declared in
      StdLib.def,CType.def etc is automatically included by DCC,
      so we dont have to specify it on the command line.

      Note that function procedures declared in this kind of module can be
      treated as proper procedures (ie the function result can be ignored).

    2.DEFINITION FOR AMIGALIB MODULE (eg Console.def)

      Mainly used to define AmigaDos devices (functions in amiga.lib).
      Like FOR C except all variable and strings declarations declared in the
      definition module are expected to be found in the coressponding Modula-2
      object file. For the compiler to generate such an object file there must
      be a corressonding implementation module,which is normally empty
      eg Console.mod or opens the device).
      The implementation module may contain procedure declaration which
      should only impelement macros not coded in the object file or library
      eg StdIO.mod .

    3.DEFINITION FOR LIBRARY MODULE (eg GadTools.def)

      Used to open and define AmigaDos libraries.
      Identical to FOR AMIGALIB except when such a module is imported an
      optional library version number can be specified (see line 75 ).
      This version number can be read using the pervasive cardinal variable
      'VERSION', for an example see GadTools.mod, and is noramlly passed to
      to function OpenLib, which inturn calls Exec.OpenLibrary.
      The initialisation body of an implementaion for library module must be
      capable of being called more than once i.e. with different versions
      arguments.

Clean up code 'CLOSE'
=====================

    In order to free any allocated resources (files, memory etc) a module may
    contain a CLOSE area:

    MODULE Test ;

    FROM Exec IMPORT AllocMem, FreeMem ;

    VAR
      x : POINTER TO INTEGER ;

    BEGIN
      x := AllocMem( 2 )
    CLOSE (* optional *)
      IF x # NIL THEN FreeMem( x ) END
    END Test.

    A CLOSE area is allowed in every module and is called when the program
    terminates (succesfully or through a runtime error).
    The order in which the CLOSE statement sequences are called is the exact
    opposite to to the initialisation body calling order. i.e. the main modules
    CLOSE sequence is called first...
    A CLOSE statement sequence cannot be declared for procedures or local
    modules.
    The CLOSE mechanism is implemented using StdLib.atexit, which can be
    called directly if you prefer.
    Make sure that the CLOSE statements cannot fail (cause a run-time error),
    as this will result in an unbreakable loop.
    CLOSE is a reserved keyword.
