# System Modules

System modules are not compilation modules, in the sense that they do not have an actual definition or implementation provided in a library, or by the user. They encapsulate predefined types, constants and procedures, for which the predefined identifiers are not pervasive.

For information only, the interfaces to system modules are shown informally in this International Standard as definition modules. The required semantics cannot be obtained by interpreting these system definition modules according to the rules for separate modules as given in section 6. In particular, as predefined entities, the procedures and functions of a system module are not assignment-compatible with any procedure type. Also, types appearing as opaque types are system basic types and cannot be used to implement user-defined opaque types.

This International Standard defines five system modules, namely `SYSTEM, COROUTINES, TERMINATION, EXCEPTIONS`, and `M2EXCEPTION`.

Change: In Programming in Modula-2 the only system module is SYSTEM and this is described as a pseudo-module. Coroutine facilities have been moved from SYSTEM to the system module COROUTINES in this International Standard.

- The Module SYSTEM
- The Module COROUTINES
- The Module EXCEPTIONS
- The Module TERMINATION
- The Module M2EXCEPTION

## The Module SYSTEM

The system module `SYSTEM` provides low-level facilities for gaining access to the addresses and the underlying storage of variables, for performing address arithmetic, and for manipulating the representation of values. Programs that use these facilities may be non-portable.

- The Interface to SYSTEM
- The Constants of SYSTEM
- The Types of SYSTEM
- The Functions of SYSTEM
- Address Arithmetic Functions
- Address Construction and Enquiry Functions
- Packedset Functions
- The Function CAST
- The Function TSIZE

### The Interface to SYSTEM

The interface to `SYSTEM` behaves as if the following were its definition module:

        DEFINITION MODULE SYSTEM;

          (* Gives access to system programming facilities that are probably non portable. *)

          (* The constants and types define underlying properties of storage *)

        CONST
          BITSPERLOC = <implementation-defined constant> ;
          LOCSPERWORD = <implementation-defined constant> ;

        TYPE
          LOC; (* A system basic type. Values are the uninterpreted contents of the smallest
       	      addressable unit of storage *)
          ADDRESS = POINTER TO LOC;
          WORD = ARRAY [0 .. LOCSPERWORD-1] OF LOC;

          (* BYTE and LOCSPERBYTE are provided if appropriate for machine *)
        CONST
          LOCSPERBYTE = <implementation-defined constant> ;

        TYPE
          BYTE = ARRAY [0 .. LOCSPERBYTE-1] OF LOC;

        PROCEDURE ADDADR (addr: ADDRESS; offset: CARDINAL): ADDRESS;
          (* Returns address given by (addr + offset), or may raise an exception if this
       	 address is not valid.
          *)

        PROCEDURE SUBADR (addr: ADDRESS; offset: CARDINAL): ADDRESS;
          (* Returns address given by (addr - offset), or may raise an exception if this address
       	 is not valid.
          *)

        PROCEDURE DIFADR (addr1, addr2: ADDRESS): INTEGER;
          (* Returns the difference between addresses (addr1 - addr2), or may raise an exception
       	 if the arguments are invalid or if the address space is non-contiguous.
          *)

        PROCEDURE MAKEADR (val: <some type>; ... ): ADDRESS;
          (* Returns an address constructed from a list of values whose types are
       	 implementation-defined, or may raise an exception if this address is not valid.
          *)

        PROCEDURE ADR (VAR v: <anytype>): ADDRESS;
          (* Returns the address of variable v. *)

        PROCEDURE ROTATE (val: <a packedset type>; num: INTEGER): <type of first parameter>;
          (* Returns a bit sequence obtained from val by rotating up or down (left or right) by
       	 the absolute value of num. The direction is down if the sign of num is negative,
       	 otherwise the direction is up.
          *)

        PROCEDURE SHIFT (val: <a packedset type>; num: INTEGER): <type of first parameter>;
          (* Returns a bit sequence obtained from val by shifting up or down (left or right) by
       	 the absolute value of num, introducing zeros as necessary. The direction is down
       	 if the sign of num is negative, otherwise the direction is up.
          *)

        PROCEDURE CAST (<targettype>; val: <anytype>): <targettype>;
          (* CAST is a type transfer function. Given the expression denoted by val, it returns
       	 a value of the type <targettype>. An invalid value for the target value or a
       	 physical address alignment problem may raise an exception.
          *)

        PROCEDURE TSIZE (<type>; ... ): CARDINAL;
          (* Returns the number of LOCS used to store a value of the specified <type>. The
       	 extra parameters, if present, are used to distinguish variants in a variant record.
          *)

        END SYSTEM.

Declaration Semantics

The identifiers available for import from the module `SYSTEM` shall include the constant identifiers `BITSPERLOC`, and `LOCSPERWORD`, the type identifiers `LOC, ADDRESS`, and `WORD`, and the procedure identifiers `ADDADR`, `SUBADR`, `DIFADR`, `MAKEADR`, `ADR`, `ROTATE`, `SHIFT`, `CAST`, and `TSIZE`.

In addition, where appropriate for the target hardware architecture, the identifiers available for import from the module `SYSTEM` shall include the constant identifier `LOCSPERBYTE`, and the type identifier `BYTE`.

Note: An implementation may extend the module `SYSTEM` by including additional types, procedures, variables and constants appropriate to the facilities of the target hardware. The identifiers `BYTE` and `LOCSPERBYTE` are, however, reserved for use with appropriate target hardware architectures, and may not be used in `SYSTEM` with other meanings --- see ???.

Changes:

1. The type `LOC` has been introduced as a mechanism to resolve the problem that, in many implementations of Modula-2, both `BYTE` and `WORD` occur in `SYSTEM`. Its introduction allows a consistent handling of both of these types, and also enables further `WORD`-like types to be introduced in an implementation, for example, where `TSIZE(CARDINAL)6=TSIZE(INTEGER)`.

2. The constants `BITSPERLOC, LOCSPERWORD` and `LOCSPERBYTE` have been introduced to denote the number of bits in a LOC, the number of `LOC`s in a word and the number of `LOC`s in a `BYTE` respectively.

3. Variables of type `ADDRESS` are no longer expression-compatible with `CARDINAL`, i.e. they cannot directly occur in expressions that include arithmetic operators.

4. The function `MAKEADR` has been introduced as a mechanism for constructing a value of type `ADDRESS`, and new functions `ADDADR, SUBADR`, and `DIFADR` have been introduced for address arithmetic, thereby obviating the necessity for assignment compatibility between `ADDRESS` and a whole number type.

5. New functions `ROTATE` and `SHIFT` have been introduced to extend the bit-level manipulation operations on packedset values.

6. The function `CAST` has been introduced for type transfer.

### The Constants of SYSTEM

The module `SYSTEM` provides the constants `BITSPERLOC` and `LOCSPERWORD`. Additionally, if appropriate for the target hardware architecture, the constant `LOCSPERBYTE` is provided.

Declaration Semantics

The constant identifier `BITSPERLOC` shall denote the whole number literal value that is the number of bits used in the representation of values of the location type and other types occupying a single location.

The constant identifier `LOCSPERWORD` shall denote the whole number literal value that is the number of locations used in the representation of values of the type denoted by `WORD` and other types occupying a single word.

The constant identifier `LOCSPERBYTE`, if provided, shall denote the whole number literal value that is the number of locations used in the representation of values of the type denoted by `BYTE` and other types occupying a single byte.

### The Types of SYSTEM

The module `SYSTEM` provides system storage types to represent uninterpreted storage, and a type to represent logical addresses.

- System Storage Types
- The Address Type

#### System Storage Types

System storage types include the location type, and types based on the location type.

Declaration Semantics

The type identifier `LOC` shall denote the location type. A value of this type shall correspond to the uninterpreted contents of the smallest addressable unit of storage in an implementation; the value of the call `TSIZE(LOC)` shall therefore be one.

Notes:

1. The only operation directly defined on the location type is assignment.

2. There are special rules affecting parameter compatibility for system storage types; where a formal parameter is expressed in terms of system storage types, the corresponding actual parameter may be of any type that is represented in a corresponding number of storage locations --- see System Storage Parameter Compatibility.

The type denoted by `WORD` shall be defined in terms of `LOC` as follows:

        LOCSPERWORD = <implementation-defined constant> ;
        WORD = ARRAY [0 .. LOCSPERWORD-1] OF LOC;

and the value of the call `TSIZE(WORD)` shall be `LOCSPERWORD`.

The type denoted by `BYTE`, if provided, shall be defined in terms of `LOC` as follows: if `TSIZE(BYTE) = 1` then

        LOCSPERBYTE = 1;
        BYTE = LOC;

and if `TSIZE(BYTE) > 1` then

        LOCSPERBYTE = <implementation-defined constant> ;
        BYTE = ARRAY [0 .. LOCSPERBYTE-1] OF LOC;

and the value of the call `TSIZE(BYTE)` shall be `LOCSPERBYTE`.

#### The Address Type

Declaration Semantics

The declaration:

        TYPE ADDRESS = POINTER TO LOC;

shall define the address type. Values of this type shall correspond to the logical addresses of storage locations.

Notes:

1. The term `logical addresses' is used since it is required that the address type is assignment-compatible with all pointer types and vice-versa --- see Assignment Compatibility.

2. A formal variable parameter of the address type is parameter-compatible with an actual variable parameter of any pointer type --- see Variable Parameter Compatibility.

3. As well as the operations that are normally applicable to values of pointer types, the system function procedures `ADDADR`, `SUBADR`, and `DIFADR` allow address arithmetic to be applied to all pointer types. In the case of `ADDADR` and `SUBADR` the result type is the address type; in the case of `DIFADR` the result type is the signed type.

4. Values of the address type may be used in the optional expression that follows the identifier in a variable declaration to fix the storage location of that variable; they may be constructed with the function `MAKEADR` --- see The Function MAKEADR.

### The Functions of SYSTEM

The module `SYSTEM` provides function procedures for address arithmetic (`ADDADR, DIFADR`, and `SUBADDR`), for address value generation (`MAKEADR` and `ADR`), for packedset operations (`ROTATE` and `SHIFT`), for type transfer (`CAST`), and for determining the storage size of values of a given type (`TSIZE`).

### Address Arithmetic Functions

The function procedures `ADDADR, SUBADDR` and `DIFADR` are provided for address arithmetic operations on values of pointer types.

- The Function ADDADR
- The Function SUBADR
- The Function DIFADR
- Properties of the address arithmetic functions

#### The Function ADDADR

The function `ADDADR` can be used to construct a value of the address type by adding an offset to the value of a pointer type.

Static Semantics

A call of `ADDADR` shall have two actual parameters. The first parameter shall be an expression that is of a pointer type, and the second parameter shall be an expression of a type with which the unsigned type is value parameter compatible. The type of a call of `ADDADR` shall be the address type.

Dynamic Semantics

The value of the call `ADDADR(addr, offset)` shall be an address calculated by adding `offset` to `addr` in an implementation-defined manner that satisfies the properties given in Properties of the address arithmetic functions.

An exception shall be raised if `addr` is the nil value.

An exception may occur and may be raised if the `ADDADR` function increments an address out of address range.

Notes:

1. An exception may occur, in particular, where the address space is non-contiguous.

2. The subsequent use of a calculated address may raise an exception.

3. Adding `N` to a pointer which points to a type whose size is greater than one `LOC` will not add `N*TSIZE(typePointedTo)`, but will add `N`.

#### The Function SUBADR

The function `SUBADR` can be used to construct a value of the address type by subtracting an offset from the value of a pointer type.

Static Semantics

A call of `SUBADR` shall have two actual parameters. The first parameter shall be an expression that is of a pointer type, and the second parameter shall be an expression of a type with which the unsigned type is value parameter compatible. The type of a call of `SUBADR` shall be the address type.

Dynamic Semantics

The value of the call `SUBADR(addr, offset)` shall be an address calculated by subtracting `offset` from `addr` in an implementation-defined manner that satisfies the properties given in Properties of the address arithmetic functions.

An exception shall be raised if `addr` is the nil value.

An exception may occur and may be raised if the `SUBADR` function decrements an address out of address range.

Notes:

1. An exception may occur, in particular, where the address space is non-contiguous.

2. The subsequent use of a calculated address may raise an exception.

3. Subtracting `N` from a pointer which points to a type whose size is greater than one `LOC` will not subtract `N*TSIZE(typePointedTo)`, but will subtract `N`.

#### The Function DIFADR

The function `DIFADR` can be used to calculate the offset between two addresses, by subtracting the second address from the first.

Static Semantics

A call of `DIFADR` shall have two actual parameters. Both parameters shall be expressions of a pointer type. The type of a call of `DIFADR` shall be the signed type.

Note: The two parameters need not be expressions of the same pointer type, since the address type is compatible with all pointer types.

Dynamic Semantics

The value of the call `DIFADR(addr1, addr2)` shall be a signed number calculated by subtracting `addr2` from `addr1` in an implementation-defined manner that satisfies the properties given in Properties of the address arithmetic functions.

An exception shall occur if `addr1` is the nil value or if `addr2` is the nil value.

An exception may occur and may be raised if the result of the `DIFADR` function is out of the range of the signed type, or if the result is meaningless because the address space is non-contiguous.

#### Properties of the address arithmetic functions

Given the declarations `VAR adr: ADDRESS; n: CARDINAL;` the following shall be true of the address arithmetic func- tion procedures if an exception is not raised:

        SUBADR(ADDADR(adr,n), n) = adr
        ADDADR(SUBADR(adr,n), n) = adr
        DIFADR(ADDADR(adr,n), adr) = n
        DIFADR(adr, SUBADR(adr,n)) = n
        ADDADR(adr, 0) = adr

### Address Construction and Enquiry Functions

The function procedures `MAKEADR` and `ADR` are provided for the construction and determination of address values.

- The Function MAKEADR
- The Function ADR

#### The Function MAKEADR

The function `MAKEADR` can be used to construct a value of the address type from one or more values of implementation- defined types. The actual number and types of the parameters are not defined in this International Standard in order that implementations may offer one or more alternatives appropriate to the target architecture. On some implementations, one of the parameters might be used to distinguish between the generation of logical addresses and the generation of hardware addresses, such as apply to memory-mapped I/O.

A value constructed by `MAKEADR` from constant parameters can be used in the optional expression that follows the identifier in a variable declaration to fix the address of storage used for that variable --- see Variable Declarations.

Static Semantics

A call of `MAKEADR` shall have an implementation-defined number of actual parameters of implementation-defined types. The type of a call of `MAKEADR` shall be the address type.

Dynamic Semantics

The value of the call `MAKEADR(val1, ... valN)` shall be an implementation-defined conversion of `val1, ... valN`. An exception may occur and may be raised if the result is not in address range.

Note: The subsequent use of an address constructed in this way may raise an exception.

#### The Function ADR

The function `ADR` can be used to obtain the address of a variable.

Static Semantics

A call of `ADR` shall have one actual parameter that shall be a variable designator. The type of a call of `ADR` shall be the address type.

Dynamic Semantics

The value of the call `ADR(v)` shall be the address of the variable `v` such that if

        TYPE
          Val = ... ;
          PtoVal = POINTER TO Val;
        VAR
          v : Val;
          p : PtoVal;
        ...
        p := ADR(v)

then `p^` and `v` shall designate the same variable.

### Packedset Functions

The functions `ROTATE` and `SHIFT` augment the facilities provided by packedset infix operations and packedset relational operations for the bit-level manipulation of values of packedset types.

- Packedset mapping
- The Function ROTATE
- The Function SHIFT

#### Packedset mapping

Values of a packedset type shall be considered as sequences of bits, numbered according to the ordinal values of the members of the the host type of the base type of the packedset. Bit number n is set (i.e. has the value 1) if n is an element of the packedset, and bit number n is unset or clear (has the value 0) if n is not an element of the packedset.

The bit-numbering shall be such that bit number 0 is the least significant bit in a machine register. Bit number 1 is the next least significant bit etc. Adjacent bit numbers shall be adjacent bits in the machine register.

The ordinal values of the lower and upper bounds of the interval used to define a packedset define a bit field within which bit-level operations are to be applied. An implementation may restrict the lower and upper bounds of the interval used to define a packedset --- see ???.

In shift and rotate operations on bit sequences, a move `up' by a number of bit positions will move the bit values within the bit field in the direction of increasing bit numbers; this is also described as a `left' move since bit positions of increasing significance are often shown as being laid out from right to left. Similarly, a move `down', or `right', will move the bit values within the bit field in the direction of decreasing bit numbers. In the case of shift operations, 0 bit values replace those moved away from a boundary of a bit field; in the case of rotate operations, bit values moved out of the bit field at one boundary are moved into the bit field at the other boundary.

Notes:

1. For example, the lower bound may be restricted to 0 and the upper bound to a value that is a multiple of `BITSPERLOC`.

2. The restriction on the upper and lower bounds of the defining interval might apply to packedsets passed as parameters to `SHIFT` and `ROTATE` if such manipulation of only part of a machine word might be impossible.

3. Set operations applied to packedsets are equivalent to the following bit-level operations within bit fields: `+` union bitwise inclusive-or operation on two bit sequences `-` set difference the bits of the first sequence that are not in the second `*` intersection bitwise and operation on two bit sequences `/` symmetric set difference bitwise exclusive-or operation on two bit sequences `INCL(s,x)` set bit x (to 1) `EXCL(s,x)` clear bit x (to 0) `=` equality are the two bit sequences equal? `<>` inequality are the two bit sequences different? `<=` subset is the first bit sequence included in the second? `>=` superset does the first bit sequence include the second? `x IN s` membership is bit number x set to 1?

4. It is intended that packed sets be mapped on to machine register bits in such a way that in an implementation having a binary representation for `CARDINAL` and where, in addition, `TSIZE(CARDINAL)=TSIZE(BITSET)`, then the following would be true.

   `CAST(CARDINAL,BITSET{0}) = VAL(CARDINAL,1)`

   and if `v` is a variable of type `CARDINAL`, then

   `CAST(CARDINAL,SHIFT(CAST(BITSET,v),-1)) = v DIV 2`

   and provided `v < = MAX(CARDINAL) DIV 2`, then

   `CAST(CARDINAL,SHIFT(CAST(BITSET,v),1)) = v * 2`

#### The Function ROTATE

The function `ROTATE` can be used to rotate a bit sequence up or down (left or right) by a specified number of positions within a bit field. The bit sequence is defined by the value of a packedset type, and the direction of rotation is down (right) if the specified number is negative; otherwise the direction is up (left).

Static Semantics

A call of `ROTATE` shall have two actual parameters. The first parameter shall be an expression that is of a packedset type. The second parameter shall be an expression of a type with which the signed type is value parameter compatible.

The type of a call of `ROTATE` shall be the type of the first parameter.

An implementation may impose restrictions on the types that are allowed as the first parameter, save that ROTATE will at least accept a value of the type `BITSET` --- see ??? and The Bitset Type.

Dynamic Semantics

The value of the call `ROTATE(val, num)` shall be a packedset whose elements are such that the offset of their ordinal values from the ordinal value of the minimum value of the base type of the packedset, is given by the offset of the ordinal values of the elements of `val` increased by `num` if it is positive, or decreased by the absolute value of `num` if it is negative, and scaled by the number of values in the base type.

Note: With the interpretation of a value of a packedset type as a sequence of bits, the rotate operation can be considered as a rotation of the bits upwards (leftwards), or a rotation of the bits downwards (rightwards), according to the value of the second operand, and within the bit field defined by the base type of the packedset.

#### The Function SHIFT

The function `SHIFT` can be used to shift a bit sequence up or down (left or right) by a specified number of positions within a bit field. The bit sequence is defined by the value of a packedset type, and the direction of shift is down (right) if the specified number is negative; otherwise the direction is up (left).

Static Semantics

A call of `SHIFT` shall have two actual parameters. The first parameter shall be an expression that is of a packedset type. The second parameter shall be an expression of a type with which the signed type is value parameter compatible.

The type of a call of `SHIFT` shall be the type of the first parameter.

An implementation may impose restrictions on the types that are allowed as the first parameter, save that `SHIFT` will at least accept a value of the type `BITSET` --- see ??? and The Bitset Type.

Dynamic Semantics

The value of the call `SHIFT(val, num)` shall be a packedset whose elements are such that the offset of their ordinal values from the ordinal value of the minimum value of the base type of the packedset, is given by the offset of the ordinal values of the elements of `val` increased by `num` if it is positive, or decreased by the absolute value of `num` if it is negative, provided that offset is not negative and is less than the number of values of the base type.

Note: With the interpretation of a value of type packedset as a sequence of bits, the shift operation can be considered as a zero-propagating logical-shift-up (left) operation or a logical-shift-down (right) operation, according to the value of the second operand, and within the bit field defined by the base type of the packedset.

### The Function CAST

The function `CAST` can be used (as a type transfer function) to interpret a value of any type other than a numeric literal value as a value of another type.

Change: In Programming in Modula-2 , type transfer is effected by using a type identifier in the role of a function name, as exemplified by `CARDINAL(IntExpr)`.

Static Semantics

A call of `CAST` shall have two actual parameters. The first parameter shall be a qualified identifier that denotes a type. The second shall be an expression whose type is not that of a numeric literal.

Note: The type of the expression that is the second parameter cannot be a numeric literal type, since such types do not have a defined storage size.

Dynamic Semantics

The value of the call `CAST(Type, val)` shall be given by a possibly unchecked conversion of `val` to the type `Type` in an implementation-defined manner satisfying the properties given below.

If the number of storage units that `val` occupies is the same as the number of storage units used by a variable of the type `Type`, the bit pattern representation of the result shall be the same as the bit pattern representation of `val`; otherwise the result and the value of `val` shall have the same bit pattern representation for a size that is equal to the smaller of the number of storage units.

An exception may occur and may be raised if a call of `CAST` causes an implementation-dependent alignment problem to occur, or if the resulting bit pattern is not a valid representation for a value of the target type.

properties

Let `ARRAY [0 .. t] OF LOC` correspond to `Type` where `t = TSIZE(Type) - 1`

Let `ARRAY [0 .. v] OF LOC` correspond to `val` where `v = TSIZE(type of SourceExpression) - 1`

The value of the call `CAST(Type, val)` shall be a value of type `Type` that when interpreted as an `ARRAY [0 .. t] OF LOC` has, for its first `min` elements, values that are the same as the first `min` elements of the value of `val` when interpreted as an `ARRAY [0 .. v] OF LOC`, where `min` is the minimum of `t` and `v`.

Note: If `t > v`, the values of remaining elements of the array from `v+1` to `t` are not defined.

If a call of `CAST` causes an implementation-dependent alignment problem to occur, the following may result: non-mandatory-detection (invalid-alignment) handler

If the resulting bit pattern is not a valid representation for a value of the target type, the following may result: non-mandatory-detection (invalid-value) handler

### The Function TSIZE

The function `TSIZE` can be used to ascertain the amount of storage that is used for a variable of a particular type. There are two versions of this function. The first has a type identifier as a parameter. It returns the size occupied by values of the type in terms of the smallest addressable unit of storage. This version is closely equivalent to the standard function `SIZE` --- see The Function SIZE. The second version has a record type identifier as the first parameter and one or more constant expressions as the remaining parameters; the values of these constant expressions must be associated with tags of variant components of that record type. It returns the size of the variant record in terms of the smallest addressable unit of storage that would be needed to store a value of the record type, with the tag fields having the values supplied in the call.

Static Semantics

The first, and possibly only parameter, shall be a qualified identifier denoting a type.

If the second and subsequent parameters are present, they shall be constant expressions, and the first parameter shall denote a record type with variant fields. The types of the constant expressions shall correspond to the types of the tag fields contained in the record type.

Note: Values may not be given for the tag fields of any record type embedded within the record type specified as the first parameter.

The type of a call of `TSIZE` shall be the Z-type.

Dynamic Semantics

The value of the call `TSIZE(Type)` shall be the size occupied by values of the type `Type` in terms of the smallest addressable unit of storage.

The value of the call `TSIZE(Type, val ... )` shall be the size of the variant record in terms of the smallest addressable unit of storage that would be needed to store values of the record with the tag fields having the values `val ...`.

Auxiliaries

annotations Check that the tag fields contained in a fields-list match the values given by an expression sequence.

annotations Check that the tag fields contained in a fields match the values given by an expression sequence.

annotations Check that the type of the expression in the sequence of tag values is assignment-compatible with the type of the corresponding tag component and the expression that corresponds to any tag field is a constant expression.

annotations Check the tag values in a sequence of values correspond to any tag fields in a variant component.

annotations Calculate the number of tags in a fields-list.

annotations Calculate the number of tags in a fields.

annotations Calculate the number of tags in a variant component of a record structure, selected by the value of the tag field.

annotations Calculates the number of tags in a variant component of a record structure.

## The Module COROUTINES

The system module `COROUTINES` provides facilities for creating coroutines, for the explicit transfer of control between coroutines, and for the handling of interrupts by coroutine transfer. Programs that use the interrupt handling facilities may be non-portable since the type used to identify sources of interrupts is implementation-defined.

- The Interface to COROUTINES
- The Types of COROUTINES
- The Procedures of COROUTINES
- The Functions of COROUTINES

### The Interface to COROUTINES

The interface to `COROUTINES` behaves as if the following were its definition module.

        DEFINITION MODULE COROUTINES;

        (* Facilities for coroutines and the handling of interrupts *)

        IMPORT SYSTEM;

        TYPE
        COROUTINE; (* Values of this type are created dynamically by NEWCOROUTINE
       		  and identify the coroutine in subsequent operations *)
        INTERRUPTSOURCE = <implementation-defined>;

        PROCEDURE NEWCOROUTINE (procBody: PROC; workspace: SYSTEM.ADDRESS; size: CARDINAL;
       			    VAR cr: COROUTINE[; initProtection: PROTECTION]);
          (* Creates a new coroutine whose body is given by procBody, and returns the identity
       	 of the coroutine in cr. workspace is a pointer to the work space allocated to
       	 the coroutine; size specifies the size of this workspace in terms of SYSTEM.LOC.
       	 initProtection is an optional parameter that specifies the initial protection
       	 level of the coroutine.
          *)

        PROCEDURE TRANSFER (VAR from: COROUTINE; to: COROUTINE);
          (* Returns the identity of the calling coroutine in from, and transfers control to
       	 the coroutine specified by to.
          *)

        PROCEDURE IOTRANSFER (VAR from: COROUTINE; to: COROUTINE);
          (* Returns the identity of the calling coroutine in from and transfers control to
       	 the coroutine specified by to. On occurrence of an interrupt, associated with the
       	 caller, control is transferred back to the caller, and the identity of the
       	 interrupted coroutine is returned in from. The calling coroutine must be
       	 associated with a source of interrupts.
          *)

        PROCEDURE ATTACH (source: INTERRUPTSOURCE);
          (* Associates the specified source of interrupts with the calling coroutine. *)

        PROCEDURE DETACH (source: INTERRUPTSOURCE);
          (* Dissociates the specified source of interrupts from the calling coroutine. *)

        PROCEDURE IsATTACHED (source: INTERRUPTSOURCE): BOOLEAN;
          (* Returns TRUE if and only if the specified source of interrupts is currently
       	 associated with a coroutine; otherwise returns FALSE.
          *)

        PROCEDURE HANDLER (source: INTERRUPTSOURCE): COROUTINE;
          (* Returns the coroutine, if any, that is associated with the source of interrupts.
       	 The result is undefined if IsATTACHED(source) = FALSE.
          *)

        PROCEDURE CURRENT (): COROUTINE;
          (* Returns the identity of the calling coroutine. *)

        PROCEDURE LISTEN (p: PROTECTION);
          (* Momentarily changes the protection of the calling coroutine to p. *)

        PROCEDURE PROT (): PROTECTION;
          (* Returns the protection of the calling coroutine. *)

        END COROUTINES.

Declaration Semantics

The identifiers available for import from the module `COROUTINES` shall be the type identifiers `COROUTINE` and `INTERRUPTSOURCE`, and the procedure identifiers `NEWCOROUTINE`, `TRANSFER`, `IOTRANSFER`, `ATTACH`, `DETACH`, `IsATTACHED`, `HANDLER`, `CURRENT`, `LISTEN`, and `PROT`.

### The Types of COROUTINES

The module `COROUTINES` provides the coroutine type (denoted by `COROUTINE`) in terms of which coroutines are identi- fied, and the implementation-defined interrupt source type (denoted by `INTERRUPTSOURCE`) in terms of which sources of interrupts are identified.

Changes:

1. The type `PROCESS` is called `COROUTINE` in this International Standard. The change from `PROCESS` to `ADDRESS` which occurred in the third edition of Programming in Modula-2 has not been adopted.

2. A particular coroutine is identified by the same value of the coroutine type throughout the lifetime of that coroutine.

3. The interrupt source type denoted by `INTERRUPTSOURCE` is not described in Programming in Modula-2.

Declaration Semantics

The type identifier `COROUTINE` shall denote the coroutine type, and the type identifier `INTERRUPTSOURCE` shall denote the implementation-defined interrupt source type.

The coroutine type shall be a system basic type, distinct from any other type, whose values identify a coroutine throughout the lifetime of that coroutine. The interrupt source type may be identical to a basic type or may be a predefined constructed type.

Note: In addition to use with the `COROUTINES` procedures `NEWCOROUTINE, TRANSFER, IOTRANSFER`, and `CURRENT`, as a system basic type, the operations of assignment and equality testing are applicable to the coroutine type --- see System Basic Types Relational Operations.

annotations Values of the coroutine type are represented by values of the type Coroutine in the formal model --- see ???. The INTERRUPTSOURCE-type is an implementation-defined type --- see Interrupt Handlers. Values of the interrupt source type are represented by values of the type Interrupt-source in the formal model --- see Interrupt Handlers.

### The Procedures of COROUTINES

The module `COROUTINES` provides procedures for creating coroutines (`NEWCOROUTINE`), transferring between coroutines (`TRANSFER` and `IOTRANSFER`), for associating and dissociating interrupt sources and coroutines (`ATTACH` and `DETACH`), and for unmasking interrupts momentarily (`LISTEN`).

- The Procedure ATTACH
- The Procedure DETACH
- The Procedure IOTRANSFER
- The Procedure LISTEN
- The Procedure NEWCOROUTINE
- The Procedure TRANSFER

#### The Procedure ATTACH

The procedure `ATTACH` can be used to associate a coroutine with an interrupt source.

Static Semantics

A call of `ATTACH` shall have one actual parameter that shall be an expression that is of the interrupt source type.

Dynamic Semantics

The call `ATTACH(source)` shall associate the implementation-defined source of interrupts identified by `source` with the calling coroutine. If the source of interrupts is already associated with a coroutine, then that association shall first be broken.

Notes:

1. More than one source of interrupts may be associated with a single coroutine.

2. If interrupt requests are possible from the attached source, execution of the calling coroutine must first be protected from interrupts --- see Protected Modules. The protection of the calling coroutine must continue as long as interrupt requests are possible.

#### The Procedure DETACH

The procedure `DETACH` can be used to dissociate a coroutine from an interrupt source.

Static Semantics

A call of `DETACH` shall have one actual parameter that shall be an expression that is of the interrupt source type.

Dynamic Semantics

If the calling coroutine is associated with the source of interrupts identified by `source`, the call `DETACH(source)` shall dissociate that coroutine from that source.

Note: The call has no effect if the coroutine is not associated with source.

#### The Procedure IOTRANSFER

The procedure IOTRANSFER can be used to transfer control from one coroutine to another, pending an interrupt.

Static Semantics

A call of `IOTRANSFER` shall have two actual parameters. The first actual parameter shall designate a variable that is of the coroutine type. The second actual parameter shall be an expression that is of the coroutine type.

Change: In Programming in Modula-2, the association with the source of interrupts is made on each call of `IOTRANSFER`. In this International Standard, the association is made beforehand by a call or calls to `ATTACH`.

Dynamic Semantics

Provided that the calling coroutine is associated with a source of interrupts, the call `IOTRANSFER(from,to)` shall assign the identity of that coroutine to the variable `from`, and shall transfer control to the coroutine identified by `to`. An exception shall be raised if the calling coroutine is not associated with a source of interrupts.

On the granting of a subsequent interrupt, following a request from one of the sources associated with the calling coroutine, the coroutine that is then executing shall be suspended, the identity of that interrupted coroutine shall be assigned to `from`, and the calling coroutine shall be resumed.

Note: The coroutine that is transferred to by a call of `IOTRANSFER`, or one that is subsequently transferred to, must execute without protection from interrupts in order for an interrupt request to be granted.

annotations This operation models the interruption of a coroutine following an interrupt request from source. The source will be associated with a coroutine that has called `IOTRANSFER`. The operation is called by the coroutine that is executing when the interrupt request is granted.

#### The Procedure LISTEN

The procedure `LISTEN` can be used to alter the protection momentarily (usually lowering it so as to allow interrupt requests to be granted).

Static Semantics

A call of `LISTEN` shall have one parameter that is an expression that is of the protection type.

Dynamic Semantics

The call `LISTEN(p)` shall momentarily change the protection of the currently executing coroutine to `p`.

annotations If an interrupt request is granted, the effect is for a call of interrupt-occurs to be made.

#### The Procedure NEWCOROUTINE

The procedure `NEWCOROUTINE` can be used to create a new coroutine. Control can later be passed to this coroutine by calling the procedures `TRANSFER` or `IOTRANSFER`.

Static Semantics

A call of `NEWCOROUTINE` shall have four or five actual parameters.

The first actual parameter shall be an expression of a type with which the proc type is value-parameter-compatible.

Note: The value of the expression that is the first actual parameter in a call of `NEWCOROUTINE` must therefore be a parameterless procedure declared at declaration level 0.

The second actual parameter shall be an expression that is of a pointer type.

The third actual parameter shall be an expression of a type with which the unsigned type is value parameter compatible.

The fourth actual parameter shall designate a variable that is of the coroutine type.

The fifth actual parameter, if present, shall be an expression that is of the protection type.

CHANGE 1 --- In Programming in Modula-2 , no provision is made for the initial protection of a coroutine to be specified in a call of `NEWCOROUTINE`.

Dynamic Semantics

Provided that the value of `size` is not less than the minimum workspace size, the call

`NEWCOROUTINE(procBody,workspace,size,cr,initProtection)`

shall create a new coroutine and shall assign a value identifying the created coroutine to the variable `cr`. The initial protection of the coroutine shall be given by `initProtection`.

Provided that the value of `size` is not less than the minimum workspace size, the alternative call

`NEWCOROUTINE(procBody,workspace,size,cr)`

shall create a new coroutine and shall assign a value identifying the created coroutine to the variable `cr`. The initial protection of the coroutine shall be given by the current protection of the caller of `NEWCOROUTINE`.

The created coroutine shall be initialized in such a way that when control is first transferred to that coroutine, the procedure given by the value of `procBody` will be called as the body of that coroutine. The effect shall be for the caller of `procBody` to be in the state of normal execution with no block body as an exception handler. The effect shall also be for the caller of `procBody` to have a protection from interrupts given by the initial protection for the coroutine.

Change: Programming in Modula-2 does not deal with the initial protection of a new coroutine. This International Standard defines rules for determining that initial protection.

If a procedure called as the body of a coroutine attempts to return, an exception shall be raised.

Notes:

1. If the procedure has local dynamic modules, these are finalized before the exception is raised.

2. Since the effective caller has no exception handler, the raising of this exception is a termination event.

Clarification: Programming in Modula-2 states that a program terminates when control reaches the end of a proce- dure which is the body of a coroutine. This International Standard requires that an unhandled exception is raised on any attempt to return to the effective caller of the body of a dynamically created coroutine.

The storage for a variable occupying a number of locations given by the value of `size` and having an address given by the value of `workspace` shall be given to the coroutine as its workspace. The use made of this workspace shall be implementation-dependent.

The minimum workspace size shall be implementation-defined. An exception shall be raised if the value of `size` is smaller than the minimum workspace size.

Notes:

1. In addition to coroutines created by `NEWCOROUTINE`, there is an original coroutine, or main coroutine, by which the static modules of the program are initialized. The initial protection of the main coroutine is implementation-defined and the workspace is supplied by the implementation.

2. The creation of a coroutine does not cause a transfer of control.

3. To ensure that no interrupts are allowed on the initial transfer to a coroutine, it is not sufficient for the procedure given as the value of the procBody parameter in a call of `NEWCOROUTINE` to be a protected procedure with a protection of `UNINTERRUPTIBLE`. That protection will only be established when the procedure is called (automatically) in the new coroutine.

4. A call of `NEWCOROUTINE` does not check if the given workspace overlaps with that of other coroutines; provided that a variable remains in existence until the end of program execution, the storage for that variable may be given to a coroutine for use as its workspace.

5. The giving of coroutine workspace allows the possibility of aliasing. By accessing the workspace through the variable whose storage is given in the call of `NEWCOROUTINE` it may be possible for a program to alter the values of coroutine variables or to affect control information such as procedure return addresses. Two consequences that follow are: a) Since the use of coroutine workspace is implementation-dependent, no meaning is given to such a program by this International Standard. b) Depending on the implementation, it may not be possible to perform a static check that a for loop control variable in a coroutine is not threatened in this way.

6. Workspace overflow is checked during coroutine execution. The check may be carried out when a procedure is activated when storage is allocated for its local variables (see Operations to Allocate and Deallocate Storage)

#### The Procedure TRANSFER

The procedure `TRANSFER` can be used to transfer control from one coroutine to another.

Static Semantics

A call of `TRANSFER` shall have two actual parameters. The first actual parameter shall designate a variable that is of the coroutine type. The second actual parameter shall be an expression that is of the coroutine type.

Dynamic Semantics

The call `TRANSFER(from,to)` shall assign the identity of the calling coroutine to the variable `from`, and shall transfer control to the coroutine identified by `to`.

Note: If at the end of its previous execution (if any) the coroutine identified by to had transferred control to another by a call of `IOTRANSFER`, it will appear as if an interrupt has occurred.

### The Functions of COROUTINES

The module `COROUTINES` provides functions for determining the identity of the current coroutine (`CURRENT`), the protection of the current coroutine (`PROT`), and for determining whether and which interrupt sources are associated with coroutines (`HANDLER` and `IsATTACHED`).

- The Function HANDLER
- The Function IsATTACHED
- The Function CURRENT
- The Function PROT

#### The Function HANDLER

The function `HANDLER` can be used to identify which coroutine is attached to a specified interrupt source.

Static Semantics

A call of `HANDLER` shall have one actual parameter that is an expression that is of the interrupt source type. The type of a call of `HANDLER` shall be the coroutine type.

Dynamic Semantics

The value of the call `HANDLER(source)` shall be the identity of the coroutine, if any, that is associated with the source of interrupts given by `source`. An exception shall occur and may be raised if that source is not currently associated with any coroutine.

#### The Function IsATTACHED

The function `IsATTACHED` can be used to determine whether a coroutine is attached to a specified interrupt source.

Static Semantics

A call of `IsATTACHED` shall have one actual parameter that is an expression that is of the interrupt source type. The type of a call of `IsATTACHED` shall be the coroutine type.

Dynamic Semantics

The value of the call `IsATTACHED(source)` shall be true if any coroutine is associated with the source of interrupts denoted by `source`, and false otherwise.

#### The Function CURRENT

The function `CURRENT` can be used to determine the identity of the currently executing coroutine.

Static Semantics

A call of `CURRENT` shall have no actual parameters. The type of a call of `CURRENT` shall be the coroutine type.

Dynamic Semantics

The value of the call `CURRENT()` shall be the identity of the calling coroutine.

#### The Function PROT

The function `PROT` can be used to obtain the protection of the currently executing coroutine, that is, the extent to which interrupts are currently masked.

Static Semantics

A call of `PROT` shall have no actual parameters. The type of a call of `PROT` shall be the protection type.

Dynamic Semantics

The value of the call `PROT()` shall be the value of the current protection against interrupts.

## The Module EXCEPTIONS

The system module `EXCEPTIONS` provides low-level facilities for identifying the source of user-defined exceptions, for reporting their occurrence, and for making enquiries concerning the execution state of the current coroutine.

User-defined exceptions are identified uniquely by an exception source/number pair. When the source of a user-defined exception is a separate module, that module may encapsulate the exception source value, thus preventing the defined exceptions of the module from being raised directly by other sources. The exception number may also be mapped to values of an enumeration type defined by the module. An example of the use of these facilities is given in The Use of EXCEPTIONS.

- The Interface to EXCEPTIONS
- The Types of EXCEPTIONS
- The Procedures of EXCEPTIONS
- The Functions of EXCEPTIONS

### The Interface to EXCEPTIONS

The interface to `EXCEPTIONS` behaves as if the following were its definition module.

        DEFINITION MODULE EXCEPTIONS;

        (* Provides facilities for raising user exceptions
        and for making enquiries concerning the current execution state.
        *)

        TYPE
          ExceptionSource;		 (* values of this type are used within library modules to
      				    identify the source of raised exceptions *)
          ExceptionNumber = CARDINAL;

        PROCEDURE AllocateSource (VAR newSource: ExceptionSource);
          (* Allocates a unique value of type ExceptionSource *)

        PROCEDURE RAISE (source: ExceptionSource; number: ExceptionNumber; message: ARRAY OF CHAR);
          (* Associates the given values of source, number and message with the current context
       	 and raises an exception.
          *)

        PROCEDURE CurrentNumber (source: ExceptionSource): ExceptionNumber;
          (* If the current coroutine is in the exceptional execution state because of the raising
       	 of an exception from source, returns the corresponding number, and otherwise
       	 raises an exception.
          *)

        PROCEDURE GetMessage (VAR text: ARRAY OF CHAR);
          (* If the current coroutine is in the exceptional execution state, returns the possibly
      	  truncated string associated with the current context.
       	 Otherwise, in normal execution state, returns the empty string.
          *)

        PROCEDURE IsCurrentSource (source: ExceptionSource): BOOLEAN;
          (* If the current coroutine is in the exceptional execution state because of the raising
       	 of an exception from source, returns TRUE, and otherwise returns FALSE.
          *)

        PROCEDURE IsExceptionalExecution (): BOOLEAN;
          (* If the current coroutine is in the exceptional execution state because of the raising
       	 of an exception, returns TRUE, and otherwise returns FALSE.
          *)

        END EXCEPTIONS.

Declaration Semantics

The identifiers available for import from the module `EXCEPTIONS` shall be the type identifiers `ExceptionSource` and `ExceptionNumber`, and the procedure identifiers `AllocateSource`, `RAISE`, `CurrentNumber`, `GetMessage`, `IsCurrentSource`, and `IsExceptionalExecution`.

### The Types of EXCEPTIONS

The module `EXCEPTIONS` provides the exception source type (denoted by `ExceptionSource`) in terms of which the sources of user-defined exceptions are identified, and the exception number type (denoted by `ExceptionNumber`) in terms of which exceptions from a particular source are identified.

Declaration Semantics

The type identifier `ExceptionSource` shall denote the exception source type, and the type identifier `ExceptionNumber` shall denote the whole number type.

Note: In addition to use with the `EXCEPTIONS` procedures `AllocateSource, RAISE, CurrentNumber`, and `IsCurrentSource`, as a system basic type, the operations of assignment and equality testing are applicable to the ex- ception source type --- see System Basic Types Relational Operations.

annotations Values of the exception source type are represented by values of the type Exception-source in the formal model --- see Exceptions.

### The Procedures of EXCEPTIONS

The module `EXCEPTIONS` provides procedures for allocating a unique value of the exception source type (`AllocateSource`), raising a user-defined exception (`RAISE`), and obtaining the message associated with the current exception context (`GetMessage`).

- The Procedure AllocateSource
- The Procedure RAISE
- The Procedure GetMessage

#### The Procedure AllocateSource

The procedure `AllocateSource` can be used as a server for previously unallocated values of the type `ExceptionSource`. Values of this type are used to identify a source of exceptions, such as a library module, when an exception is raised with the procedure `RAISE`. The procedure is normally called once during initialization of a separate module, and the resulting value is then used in all calls of `RAISE` for exceptions that may be handled by clients of the module.

Static Semantics

A call of `AllocateSource` shall have one actual parameter which shall be a variable that is of the exception source type.

Dynamic Semantics

The call `AllocateSource(newSource)` shall allocate a value of the exception source type that is unique within the program and shall assign this value to the variable `newSource`. If a unique value cannot be allocated, an exception shall be raised.

#### The Procedure RAISE

The procedure `RAISE` can be used by a specified source to raise a user-defined exception.

The effect of raising an exception is the same for exceptions raised by the language implementation as for exceptions raised by explicit use of the procedure `RAISE` --- see Language Exceptions. Thus the raising of a user-defined exception is a termination event (see Program Termination) unless the exception is handled by an exception body (see Block Bodies and Exception Handling).

Static Semantics

A call of `RAISE` shall have three actual parameters. The first actual parameter shall be an expression that is of the exception source type, the second actual parameter shall be an expression of a type with which the unsigned type is value parameter compatible, and the third actual parameter shall be an expression of a type with which an open array of `CHAR` is value parameter compatible.

Dynamic Semantics

The call of `RAISE(source, number, message)` shall set the current exception source to the value of `source`, the current exception number to the value of `number`, and the current exception message to the value of `message` interpreted as a character string and limited to an implementation-defined length.

#### The Procedure GetMessage

The procedure `GetMessage` can be used to obtain the message passed when an exception is raised. This may give further information about the nature of the exception for use by the program, or in the construction of other messages.

Static Semantics

A call of `GetMessage` shall have one actual parameter which shall be an array variable having components of the character type.

Dynamic Semantics

The call `GetMessage(text)` shall assign a string value to the variable `text`. If the calling coroutine is in the state of exceptional execution, the string value shall be formed from the message stored when the corresponding exception was raised, but with a length limited by the capacity of `text`.

### The Functions of EXCEPTIONS

The module `EXCEPTIONS` provides functions for determining the exception number for the current user-defined excep- tion (CurrentNumber), whether a user-defined exception has been raised by a given source (`IsCurrentSource`), and whether the execution state is that of exceptional execution (`IsExceptionalExecution`).

- The Function CurrentNumber
- The Function IsCurrentSource
- The Function IsExceptionalExecution
- The Use of EXCEPTIONS

#### The Function CurrentNumber

The function `CurrentNumber` can be used to obtain the exception number for the current user-defined exception that has been raised.

Static Semantics

A call of `CurrentNumber` shall have one actual parameter which shall be an expression of the exception source type. The type of a call of `CurrentNumber` shall be the unsigned type.

Dynamic Semantics

If the calling coroutine is in the state of exceptional execution because of the raising of a user-defined exception from source, the value of the call `CurrentNumber(source)` shall be the number that is associated with the raised exception; otherwise an exception shall be raised.

#### The Function IsCurrentSource

The function `IsCurrentSource` can be used to test whether the calling coroutine is in the state of exceptional execution because of the raising of a user-defined exception from a specified source.

Static Semantics

A call of `IsCurrentSource` shall have one actual parameter which shall be an expression of the exception source type. The type of a call of `IsCurrentSource` shall be the Boolean type.

Dynamic Semantics

If the calling coroutine is in the state of exceptional execution because of the raising of a user-defined exception from `source`, the value of the call `IsCurrentSource(source)` shall be true ; otherwise the value shall be false .

#### The Function IsExceptionalExecution

The function `IsExceptionalExecution` can be used to test whether the calling coroutine is in the state of exceptional execution.

Static Semantics

A call of `IsExceptionalExecution` shall have no actual parameters. The type of a call of `IsExceptionalExecution` shall be the Boolean type.

Dynamic Semantics

If the calling coroutine is in the state of exceptional execution, the value of the call `IsExceptionalExecution()` shall be true ; otherwise the value shall be false .

#### The Use of EXCEPTIONS

The example given below shows the use of `EXCEPTIONS` in the implementation of a separate module that provides facilities to allow a general user-defined exception to be raised and identified.

Example: A general user exception separate module.

        DEFINITION MODULE GeneralUserExceptions;

        (* Provides facilities for general user-defined exceptions *)

        TYPE
          GeneralExceptions = (problem, disaster);

        PROCEDURE RaiseGeneralException (exception: GeneralExceptions; text: ARRAY OF CHAR);
          (* Raises exception using text as the associated message *)

        PROCEDURE IsGeneralException (): BOOLEAN;
          (* Returns TRUE if the current coroutine is in the exceptional execution state
             because of the raising of an exception from GeneralExceptions;
             otherwise returns FALSE.
          *)

        PROCEDURE GeneralException(): GeneralExceptions;
          (* If the current coroutine is in the exceptional execution state because of the
             raising of an exception from GeneralExceptions, returns the corresponding
             enumeration value, and otherwise raises an exception.
          *)

        END GeneralUserExceptions.

        IMPLEMENTATION MODULE GeneralUserExceptions;

        IMPORT EXCEPTIONS;

        VAR
          generalSource: EXCEPTIONS.ExceptionSource;

        PROCEDURE RaiseGeneralException (exception: GeneralExceptions; text: ARRAY OF CHAR);
          BEGIN
            EXCEPTIONS.RAISE(generalSource, ORD(exception), text)
          END RaiseGeneralException;

        PROCEDURE IsGeneralException (): BOOLEAN;
          BEGIN
            RETURN EXCEPTIONS.IsCurrentSource(generalSource)
          END IsGeneralException;

        PROCEDURE GeneralException(): GeneralExceptions;
          BEGIN
            RETURN VAL(GeneralExceptions, EXCEPTIONS.CurrentNumber(generalSource))
          END GeneralException;

        BEGIN
          EXCEPTIONS.AllocateSource(generalSource)
        END GeneralUserExceptions.

## The Module TERMINATION

The system module `TERMINATION` provides facilities for a program to enquire whether a termination event has occurred --- see Program Termination.

- The Interface to TERMINATION
- The Functions of TERMINATION

### The Interface to TERMINATION

The interface to `TERMINATION` behaves as if the following were its definition module.

        DEFINITION MODULE TERMINATION;

        (* Provides facilities for enquiries concerning the occurrence of termination events. *)

        PROCEDURE IsTerminating (): BOOLEAN ;
          (* Returns TRUE if any coroutine has started program termination and FALSE otherwise. *)

        PROCEDURE HasHalted (): BOOLEAN ;
          (* Returns TRUE if a call to HALT has been made and FALSE otherwise. *)

        END TERMINATION.

Declaration Semantics

The identifiers available for import from the module `TERMINATION` shall be the procedure identifiers `IsTerminating`, and `HasHalted`.

### The Functions of TERMINATION

The module `TERMINATION` provides the function procedures `IsTerminating` and `HasHalted` for enquiries concerning the occurrence of termination events.

- The Function IsTerminating
- The Function HasHalted

#### The Function IsTerminating

The function `IsTerminating` can be used to determine whether any coroutine has started program termination.

Static Semantics

A call of `IsTerminating` shall have no parameters. The type of a call of `IsTerminating` shall be the Boolean type.

Dynamic Semantics

The value of the call `IsTerminating()` shall be true if any coroutine has started program termination and shall be false otherwise.

#### The Function HasHalted

The function `HasHalted` can be used to determine whether a call has been made to `HALT`.

Static Semantics

A call of `HasHalted` shall have no parameters. The type of a call of `HasHalted` shall be the Boolean type.

Dynamic Semantics

The value of the call `HasHalted()` shall be true if a call to `HALT` has been made and shall be false otherwise.

## The Module M2EXCEPTION

The system module `M2EXCEPTION` provides facilities for identifying language exceptions that have been raised.

The language (which includes the system modules) is regarded as one source of exceptions. Similar facilities are defined by library modules that are sources of user-defined exceptions --- see, for example, ???.

- The Interface to M2EXCEPTION
- The Types of M2EXCEPTION
- The Functions of M2EXCEPTION
- Language Exceptions
- Messages Associated with Language Exceptions
- Aggregation and Raising of Language Exceptions

### The Interface to M2EXCEPTION

The interface to `M2EXCEPTION` behaves as if the following were its definition module.

        DEFINITION MODULE M2EXCEPTION;

        (* Provides facilities for identifying language exceptions *)

        TYPE
          M2Exceptions =
            (indexException, rangeException, caseSelectException, invalidLocation,
        	 functionException, wholeValueException, wholeDivException, realValueException,
        	 realDivException, complexValueException, complexDivException, protException,
        	 sysException, coException, exException
            );

        PROCEDURE M2Exception (): M2Exceptions;
          (* If the current coroutine is in the exceptional execution state because of the raising
       	 of a language exception, returns the corresponding enumeration value, and otherwise
       	 raises an exception.
          *)

        PROCEDURE IsM2Exception (): BOOLEAN;
          (* If the current coroutine is in the exceptional execution state because of the raising
       	 of a language exception, returns TRUE, and otherwise returns FALSE.
          *)

        END M2EXCEPTION.

Declaration Semantics

The identifiers available for import from the module `M2EXCEPTION` shall be the enumeration type identifier `M2Exceptions`, the corresponding enumeration value identifiers, and the procedure identifiers `M2Exception` and `IsM2Exception`.

### The Types of M2EXCEPTION

The module `M2EXCEPTION` provides the m2exceptions enumeration type (denoted by `M2Exceptions`) in terms of which the language exceptions are raised. The enumeration values represent aggregations of the exceptions detected by the formal model --- see Aggregation and Raising of Language Exceptions.

Declaration Semantics

The type identifier `M2Exceptions` shall denote the m2exceptions type which shall be an enumeration type correspond- ing to the following enumeration:

        (indexException,	 rangeException,	 caseSelectException,	 invalidLocation,
        functionException,	 wholeValueException,	 wholeDivException,	 realValueException,
        realDivException,	 complexValueException,	 complexDivException,	 protException,
        sysException,	 coException,		 exException
        );

annotations Values of the m2exceptions type are represented by values of the type Exception-value in the formal model --- see Exceptions.

### The Functions of M2EXCEPTION

The module `M2EXCEPTION` provides the function procedures `M2Exception` and `IsM2Exception` for enquiries concerning language exceptions.

- The Function M2Exception
- The Function IsM2Exception

#### The Function M2Exception

The function `M2Exception` can be used to determine the language exception that has been raised.

Static Semantics

A call of `M2Exception` shall have no actual parameters. The type of a call of `M2Exception` shall be the m2exceptions type.

Dynamic Semantics

If the calling coroutine is in state of exceptional execution because of the raising of a language exeption, the value of the call `M2Exception()` shall be the value of type `M2Exceptions` that is associated with the raised exception; otherwise an exception shall be raised.

#### The Function IsM2Exception

The function `IsM2Exception` can be used to test whether the calling coroutine is in the state of exceptional execution because of the raising of a language exception.

Static Semantics

A call of `IsM2Exception` shall have no parameters. The type of a call of `IsM2Exception` shall be the Boolean type.

Dynamic Semantics

If the calling coroutine is in the state of exceptional execution because of the raising of a language exception, the value of the call `IsM2Exception()` shall be true ; otherwise the value shall be false .

### Language Exceptions

The exceptions defined by the dynamic semantics of the language, which includes the system modules, are categorized into those which an implementation is required to detect and those which an implementation is not required to detect. Detected exceptions are raised by the implementation and constitute a termination event (see Program Termination) unless the exception is handled by an exception body (see Block Bodies and Exception Handling). Exceptions that are distinguished in the dynamic semantics are aggregated into a smaller number of identified exceptions for the purposes of exception handling.

An implementation must define which of the exceptions whose detection is not required are in fact detected in some or all circumstances. It need not define the circumstances in which such exceptions are detected. If such an exception occurs, but is not detected, this International Standard gives no meaning to the program --- see ???.

- Exceptions whose detection is required
- Exceptions whose detection is not required

#### Exceptions whose detection is required

Dynamic Semantics

annotations Construct an appropriate message based on the exception and the current state, and construct an exception context in which to raise the exception.

#### Exceptions whose detection is not required

Dynamic Semantics

annotations If the exception is detected by the implementation, construct an appropriate message based on the exception, the environment and the current state, and construct an exception context in which to raise the exception. Otherwise, the behaviour is implementation-dependent.

annotations The result is true if the exception is actually detected and otherwise false .

### Messages Associated with Language Exceptions

Dynamic Semantics

The character string associated with each of the exceptions that may be raised as specified in Chapters The Language and System Modules shall be implementation-defined.

Note: The character string associated with a raised exception may be accessed using the `GetMessage` procedure --- see The Procedure GetMessage.

annotations Using the exception and the current state, construct an appropriate message for a language exception context.

### Aggregation and Raising of Language Exceptions

annotations Build a language exception context from: an enumeration value defined by the mapping exception-value ; and the message. Raise an exception that has the exception context.