DEFINITION MODULE LowLevel;

        (********************************************************)
        (*                                                      *)
        (*         Miscellaneous low-level procedures           *)
        (*                                                      *)
        (*  Programmer:         P. Moylan                       *)
        (*  Last edited:        11 February 2024                *)
        (*  Status:             Working                         *)
        (*                                                      *)
        (*      Now appears to be working, but:                 *)
        (*       (a) untested, more checking needed;            *)
        (*       (b) it's still not clear that what's           *)
        (*           provided is what the clients needed,       *)
        (*           particularly in relation to 16 bit/32 bit  *)
        (*           distinctions.                              *)
        (*                                                      *)
        (*      Note that the implementation of this module     *)
        (*      is heavily compiler-dependent.  This version    *)
        (*      is for use with the XDS compiler,               *)
        (*                                                      *)
        (*      NOTE: Much of this module was originally        *)
        (*      designed for 16-bit words, and I'm gradually    *)
        (*      changing that.  What I need to look at is what  *)
        (*      the client modules really need (e.g. who really *)
        (*      uses a procedure like "HighByte").              *)
        (*                                                      *)
        (*      NOTE: Some procedures commented out, because I  *)
        (*      haven't yet worked out the details.             *)
        (*                                                      *)
        (********************************************************)

FROM SYSTEM IMPORT
    (* type *)  CARD8, INT8, CARD16, INT16, LOC, ADDRESS;

FROM Types IMPORT
    (* type *)  FarPointer;

(************************************************************************)
(*                         TOUCHING STACK PAGES                         *)
(************************************************************************)

PROCEDURE TouchNextPages (N: CARDINAL);

    (* Touches N new stack pages, prior to calling a routine that will  *)
    (* use lots of local storage.  The goal here is to force the guard  *)
    (* page exception handler to allocate more stack space, before we   *)
    (* get into code that uses lots of local variable space.            *)

    (* This is a workaround for a fault in the XDS run-time support.    *)
    (* Most applications start out with a small stack, but OS/2 has     *)
    (* provision for growing the stack as needed.  When application     *)
    (* code accesses a stack page that has not yet been allocated, a    *)
    (* guard page exception occurs. The exception handler is supposed   *)
    (* to allocate the extra page (where page size = 4k bytes), and     *)
    (* then let the application code continue.  Unfortunately,          *)
    (* the relevant handler in the XDS runtime has a flaw: if you're    *)
    (* close enough to a stack page boundary, the handler can create a  *)
    (* second guard page exception, and the resulting recursion causes  *)
    (* a fatal crash.  The only really good fix for this would be to    *)
    (* rewrite the exception handler, and I don't feel competent to     *)
    (* do that.                                                         *)

    (* The risk of this happening is greatest when calling a procedure  *)
    (* with more than 4k of local variables, but there are no clear     *)
    (* guidelines as to when this "touch" is desirable, and indeed this *)
    (* procedure could even trigger the fault.  It all depends on       *)
    (* whether we're close to a page boundary.  Checking the stack      *)
    (* pointer before each call would help, but would lead to messy     *)
    (* code.  The only rule of thumb is that calling this procedure     *)
    (* should be considered if there's evidence that program crashes    *)
    (* are being triggered by guard page exceptions.                    *)

(************************************************************************)
(*                           MISCELLANEOUS                              *)
(************************************************************************)

PROCEDURE EVAL (f: ARRAY OF LOC);

    (* A do-nothing procedure - we use it for evaluating a function and *)
    (* ignoring the result.                                             *)

PROCEDURE Assert (arg: BOOLEAN);

    (* Halts the program if arg is FALSE. *)

(************************************************************************)
(*                         STORAGE ALLOCATION                           *)
(************************************************************************)

PROCEDURE ALLOCATE64 (VAR (*OUT*) p: ADDRESS;  amount: CARDINAL);

    (* Like Storage.ALLOCATE, but this procedure tries to ensure that   *)
    (* the allocated block does not cross a 64K boundary in memory.     *)
    (* This restriction is required when passing data to and from the   *)
    (* 16-bit API procedures (Vio, etc.)                                *)

(************************************************************************)
(*                          BITWISE LOGIC                               *)
(************************************************************************)

PROCEDURE SwapIt (VAR (*INOUT*) arg: ARRAY OF LOC);

    (* Reverses the byte order of its argument.  Used for conversion    *)
    (* between BigEndian and LittleEndian format.                       *)

PROCEDURE IAND (first, second: CARDINAL): CARDINAL;

    (* Bit-by-bit logical AND.  *)

PROCEDURE IANDB (first, second: CARD8): CARD8;

    (* Bit-by-bit logical AND for bytes. *)

PROCEDURE IOR (first, second: CARDINAL): CARDINAL;

    (* Bit-by-bit inclusive OR. *)

PROCEDURE IORB (first, second: CARD8): CARD8;

    (* Bit-by-bit inclusive OR. *)

PROCEDURE IXOR (first, second: CARDINAL): CARDINAL;

    (* Bit-by-bit exclusive OR. *)

PROCEDURE IXORB (first, second: CARD8): CARD8;

    (* Bit-by-bit exclusive OR. *)

PROCEDURE INOT (value: CARDINAL): CARDINAL;

    (* Bit-by-bit Boolean complement.   *)

PROCEDURE INOTB (value: CARD8): CARD8;

    (* Bit-by-bit Boolean complement.   *)

PROCEDURE ROL (value: CARDINAL;  count: CARDINAL): CARDINAL;

    (* Left rotation of "value" by "count" bit positions.       *)

PROCEDURE ROLB (value: CARD8;  count: CARDINAL): CARD8;

    (* Left rotation of "value" by "count" bit positions.       *)

PROCEDURE LS (value: CARDINAL;  count: CARDINAL): CARDINAL;

    (* Left shift of "value" by "count" bit positions, with zero fill.  *)

PROCEDURE LSB (value: CARD8;  count: CARDINAL): CARD8;

    (* Left shift of "value" by "count" bit positions, with zero fill.  *)

PROCEDURE ROR (value: CARDINAL;  count: CARDINAL): CARDINAL;

    (* Right rotation of "value" by "count" bit positions.      *)

PROCEDURE RORB (value: CARD8;  count: CARDINAL): CARD8;

    (* Right rotation of "value" by "count" bit positions.      *)

PROCEDURE RS (value, count: CARDINAL): CARDINAL;

    (* Right shift of "value" by "count" bit positions, with zero fill. *)

PROCEDURE RSB (value: CARD8;  count: CARDINAL): CARD8;

    (* Right shift of "value" by "count" bit positions, with zero fill. *)

(************************************************************************)
(*                          POINTER OPERATIONS                          *)
(************************************************************************)

PROCEDURE Far (A: ADDRESS): FarPointer;

    (* Converts a pointer to a far pointer. *)

PROCEDURE MakePointer (segment: CARD16; offset: CARDINAL): FarPointer;

    (* Creates a pointer, given the segment and offset within segment.  *)

PROCEDURE SEGMENT (A: ADDRESS): CARD16;

    (* Returns the segment part of an address.  *)

PROCEDURE FarSEGMENT (A: FarPointer): CARD16;

    (* Returns the segment part of an address.  *)

PROCEDURE OFFSET (A: ADDRESS): CARDINAL;

    (* Returns the offset part of an address.   *)

PROCEDURE AddOffset (A: ADDRESS;  increment: CARDINAL): ADDRESS;

    (* Returns a pointer to the memory location whose physical address  *)
    (* is Physical(A)+increment.  In the present version, it is assumed *)
    (* that the caller will never try to run off the end of a segment.  *)

PROCEDURE SubtractOffset (A: ADDRESS;  decrement: CARDINAL): ADDRESS;

    (* Like AddOffset, except that we go backwards in memory.  Running  *)
    (* off the beginning of the segment is an undetected error.         *)

PROCEDURE FarAddOffset (A: FarPointer;  increment: CARDINAL): FarPointer;

    (* Like AddOffset, except for the parameter types. *)

PROCEDURE FarSubtractOffset (A: FarPointer; decrement: CARDINAL): FarPointer;

    (* Like SubtractOffset, except for the parameter types. *)

(*PROCEDURE Virtual (PA: LONGCARD): FarPointer;*)

    (* Converts a physical address to a virtual address, if possible.   *)
    (* There are no guarantees in the case where there is no such       *)
    (* virtual address.                                                 *)

(*PROCEDURE Physical (A: ADDRESS): LONGCARD;*)

    (* Converts a virtual address to a physical address.  Use with care!*)

(************************************************************************)
(*                      BYTE/WORD/LONGCARD CONVERSIONS                  *)
(************************************************************************)

PROCEDURE LowByte (w: CARDINAL): CARD8;

    (* Returns the low-order byte of its argument.      *)

PROCEDURE HighByte (w: CARD16): CARD8;

    (* Returns the high-order byte of its argument.     *)

PROCEDURE MakeWord (high, low: CARD8): CARD16;

    (* Combines two bytes into a word.  The first argument becomes the  *)
    (* most significant byte of the result.                             *)

PROCEDURE SignExtend (val: INT8): INTEGER;

    (* Converts a signed 8-bit number to signed integer. *)

PROCEDURE LowWord (w: CARDINAL): CARD16;

    (* Returns the low-order word of its argument.      *)

PROCEDURE HighWord (w: CARDINAL): CARD16;

    (* Returns the high-order word of its argument.     *)

PROCEDURE MakeLongword (high, low: CARD16): CARDINAL;

    (* Combines two words into a longword.  The first argument becomes  *)
    (* the most significant word of the result.                         *)

(************************************************************************)
(*                      MISCELLANEOUS ARITHMETIC                        *)
(************************************************************************)

PROCEDURE INCV (VAR (*INOUT*) dest: CARDINAL;  src: CARDINAL): BOOLEAN;

    (* Computes dest := dest + src, and returns TRUE iff the addition   *)
    (* produced a carry.                                                *)

PROCEDURE INCVB (VAR (*INOUT*) dest: CARD8;  src: CARD8): BOOLEAN;

    (* Computes dest := dest + src, and returns TRUE iff the addition   *)
    (* produced a carry.                                                *)

PROCEDURE DECV (VAR (*INOUT*) dest: CARDINAL;  src: CARDINAL): BOOLEAN;

    (* Computes dest := dest - src, and returns TRUE iff the            *)
    (* subtraction produced a borrow.                                   *)

PROCEDURE DECVB (VAR (*INOUT*) dest: CARD8;  src: CARD8): BOOLEAN;

    (* Computes dest := dest - src, and returns TRUE iff the            *)
    (* subtraction produced a borrow.                                   *)

PROCEDURE Mul (A, B: CARD16): CARDINAL;

    (* Same as A*B, except for the type of the result.  We provide this *)
    (* as a general-purpose function since this combination of operands *)
    (* is often precisely what is wanted.                               *)

PROCEDURE MulB (A, B: CARD8): CARD16;

    (* Same as A*B, except for the type of the result.  We provide this *)
    (* as a general-purpose function since this combination of operands *)
    (* is often precisely what is wanted.                               *)

PROCEDURE IMul (A, B: INT16): INTEGER;

    (* Like Mul, but signed. *)

PROCEDURE IMulB (A, B: INT8): INT16;

    (* Like MulB, but signed. *)

PROCEDURE DivB (A: CARD16;  B: CARD8): CARD8;

    (* Same as A DIV B, except for the type of A.  We provide this as   *)
    (* a general-purpose function since this combination of operands    *)
    (* is often precisely what is wanted.                               *)

PROCEDURE Div (A: CARDINAL;  B: CARD16): CARD16;

    (* Same as A DIV B, except for the type of A.  We provide this as   *)
    (* a general-purpose function since this combination of operands    *)
    (* is often precisely what is wanted.                               *)

(************************************************************************)
(*                           BLOCK MOVES                                *)
(************************************************************************)

PROCEDURE Copy (source, destination: ADDRESS;  bytecount: CARDINAL);

    (* Copies an array of bytes from the source address to the          *)
    (* destination address.  In the case where the two arrays overlap,  *)
    (* the destination address should be lower in physical memory than  *)
    (* the source address.                                              *)

PROCEDURE FarCopy (source, destination: FarPointer;  bytecount: CARDINAL);

    (* Copies an array of bytes from the source address to the          *)
    (* destination address.  In the case where the two arrays overlap,  *)
    (* the destination address should be lower in physical memory than  *)
    (* the source address.                                              *)

PROCEDURE CopyUp (source, destination: FarPointer;  bytecount: CARDINAL);

    (* A variant of Copy which does the move backwards, in order        *)
    (* to handle the case where the destination address is inside the   *)
    (* source array.  In this special case Copy cannot be used,         *)
    (* because it would overwrite data it was about to copy.            *)

PROCEDURE BlockFill (destination: FarPointer;
                                bytecount: CARDINAL;  value: CARD8);

    (* Fills the destination array with the given value.        *)

PROCEDURE BlockFillWord (destination: FarPointer;  wordcount: CARDINAL;
                                                        value: CARD16);

    (* Fills the destination array with the given value.        *)

(************************************************************************)
(*                          INPUT AND OUTPUT                            *)
(*           I haven't yet worked out how to do this in OS/2            *)
(************************************************************************)

(*PROCEDURE OutByte (port: CARDINAL; value: BYTE);*)

    (* Puts the value out to an output port.    *)

(*PROCEDURE InByte (port: CARDINAL): BYTE;*)

    (* Reads a byte from an input port. *)

(*PROCEDURE InStringWord (port: CARDINAL;  BufferAddress: ADDRESS;
                                                count: CARDINAL); *)

    (* Reads count words from an input port.    *)

(*PROCEDURE OutStringWord (port: CARDINAL;  BufferAddress: ADDRESS;
                                                count: CARDINAL);*)

    (* Writes count words to an output port.    *)

END LowLevel.
