                The utilities library
%ARCSWI

  CALL ARCSWI( SWI, IREGS, IERR, ERMSG)

 Purpose: forces a standard RISC-OS Software Interrupt (SWI)

 Parameters:

    SWI   - CHARACTER SWI name (N.B. this is case sensitive)
            or INTEGER SWI number (less than FFFFFF{hex})

    IREGS - INTEGER array of length at least 10 words, one for the contents
                    of each register which may be sent to or returned from
                    the SWI. These will be updated with the contents
                    of registers R0 to R7 returned by the SWI
 
    IERR  - INTEGER an error number or zero if no error.

    ERMSG - CHARACTER string to receive an error message 
                      (e.g space for 50 characters)
                      or 'N' for no error message required.


 Example:
      CHARACTER ERTEXT*50, DIRNAM*2
      DIMENSION IREGS(0:9)
C         OS_FSControl type 5 catalogues a directory
      IREGS(0)=5
C         null terminated directory name
      DIRNAM='$'//CHAR(0)
      IREGS(1)=LOCC(DIRNAM)
C         print catalogue of $ directory
      CALL ARCSWI('OS_FSControl',IREGS,IERR,ERTEXT)
      IF(IERR.NE.0) THEN
C         error found here, get it and print it
        PRINT *,'Error from ARCSWI: ',ERTEXT
        STOP 'Help!'
      ENDIF

 Results in: a standard catalogue of the current directory as if you had
             typed:
        cat
             on the command line

 Comments: 1) ARCSWI recognises the difference between a character string
             for its first parameter (SWI) and an integer by looking at the
             fourth byte. This is zero for an integer within the defined
             limit whereas all SWI names are longer than 3 bytes. Hence it
             is important to keep within the limit of allowed SWI numbers.
           2) It is much faster though less transparent to call it with an
             SWI number rather than a name.
           3) ARCSWI always returns the values of registers 0 to 9 so IREGS
             must be dimensioned to at least 10. It is usually convenient to
             declare:
      DIMENSION IREGS(0:9)
             when the index corresponds to the register number used by the
             SWI.

%BEEP
 
 CALL BEEP

 Purpose: sounds the 'bell'

 Parameters: none

%COPY2L
 
 CALL COPY2L(A,B)

 Purpose: copy a string, converting upper case to lower

 Parameters:
   A - CHARACTER input string
   B - CHARACTER output string.

 Comments: B need not be the same length as A
      1) if it is longer, the remainder of B will be blank filled
      2) if it is shorter, only the first part of A will be copied. 

%COPY2U
 
 CALL COPY2U(A,B)

 Purpose: copy a string, converting lower case to upper

 Parameters:
   A - CHARACTER input string
   B - CHARACTER output string.

 Comments: B need not be the same length as A
      1) if it is longer, the remainder of B will be blank filled
      2) if it is shorter, only the first part of A will be copied. 

%EVALF

  RESULT = EVALF(STRING,IERR)

 Purpose: to evaluate an arithmetic expression 

 Parameters:

  STRING - CHARACTER expression which may contain: 
            a) Decimal numbers including a decimal point
            b) the operators + - * / and ** (or equivalently ^)
            c) any level of parentheses
            d) the functions: SQRT, LOG, LOGX (for LOG10), EXP, SIN,
                              COS, TAN, ASIN, ACOS, ATAN, ABS, AINT
            e) the constant PI and a user constant (see comments)

  IERR   - INTEGER returned error with values:
            0: OK
            1: unknown character
            2: found an operator when expecting a value
            3: expression does not end with a value
            4: found a value when expecting an operator
            5: parentheses not closed
            6: expression too long
            7: unknown keyword
            8: function followed by function

  Result: the evaluated arithmetic expression.

  Comments: 1) EVALF works in extended precision so you should declare
               DOUBLE PRECISION EVALF if you need this accuracy. It also
               works as an ordinary REAL function.
            2) blanks are ignored and alphabetic characters may be in either
               case.
            3) you can define one user constant (similar to the pre-stored
               constant PI) using the function JVALK (q.v.)

  Examples:

     PRINT *,EVALF('SIN(ACOS(-1))',IER)
C        gives -5.01652E-20
     R = EVALF('1+2(3-4)',IERR)
C        gives IERR=4 because (3-4) is a 'value'
C        when it was expecting an operator e.g. *, so
     R = EVALF('1+2*(3-4)',IERR)
C        gives R=-1.0 with IERR=0
   
 
%FLACCS

  CALL FLACCS(SET,NAME,ACCESS,IERR)

 Purpose:  gets or sets file access attributes

 Parameters:

  SET    - LOGICAL .TRUE. to set the access, .FALSE. to read them

  NAME   - CHARACTER file name

  ACCESS - CHARACTER access attributes in a string; these are returned if
                          SET is .FALSE. They may be any of:
                     R : file is readable by the owner
                     W : file is writable by the owner
                     L : file is locked against deletion
                     r : file is readable by anyone else
                     w : file is writable by anyone else
       additionally  D : is returned if the 'file' is a directory

  IERR   - INTEGER error returned
                -1: file does not exist
                 0: ACCESS returmed or set OK
                +1: trying to change the access for a directory
                +2: unknown character in ACCESS string

 Example:
C          stop !Boot being altered or deleted
     CALL FLACCS(.TRUE.,'$.!Boot','RL',IERR)
     IF(IERR.NE.0) PRINT *, '!Boot not available' 

%FLDATE
  
  CALL FLDATE(NAME,DATE,IERR)

 Purpose:  gets the date and time of the creation of a file

 Parameters:

  NAME   - CHARACTER name of the file

  DATE   - CHARACTER returned date and time in the format seen in a
                     'full info' filer window: 'hh:mm:ss dm Mon Year'
                     where: hh mm ss are hours, minutes and seconds,
                            dm is the day in the month
                            Mon are the first 3 charayters of the month name
                            Year is the full value of the year
                     in addition, at the end of DATE are ' d my':
                            d is the day of the week (1=Sun..., 7=Sat)
                            my is the month (01=Jan..., 12=Dec)
                     DATE should be at least 25 characters long to hold all
                     the information but it wil be truncated or extended with
                     blanks if necessary.

  IERR   - INTEGER error information
                   -1: file does not exist
                    0: DATE returned OK

  Example:
      CHARACTER DATIM*20
      CALL FLDATE('$.!Boot',DATIM,IERR)
      IF(IERR.EQ.0) PRINT *,'!Boot was created on ',DATIM

%FLDIRE

  CALL FLDIRE(NUMB,DIREC,FNAME,IERR)

 Purpose:  gets the names of files in a directory

 Parameters:

  NUMB   - INTEGER file index within the directory (>0)

  DIREC  - CHARACTER directory name (may be '@' for current directory etc.)

  FNAME  - CHARACTER returned file name (should be at least 10 long)

  IERR   - INTEGER returned error status
                -1: DIREC is not a directory
                 0: file NUMB does not exist
                +1: FNAME contains a file name
                +2: FNAME contains a sub-directory name
                +3: FNAME contains a zip-file with Sparkfs live

 Example:
      CHARACTER*10 FNAME
      PRINT *,'Files in the library directory are:'
      DO 10 I=1,77
        CALL FLDIRE(I,'%',FNAME,IERR)
        IF(IERR.EQ.1) PRINT *,FNAME
   10 CONTINUE

%FLLIST

  CALL FLLIST(DIREC,FNAME,NUMB)

 Purpose:  gets the names of all the files in a directory

 Parameters:

  DIREC  - CHARACTER directory name
              (may be '@' for current directory etc.)

  FNAME  - CHARACTER returned list of file names null separated
               this should be long enough to hold all the file names in the directory

  NUMB   - INTEGER number of file names in FNAME or...
                -1: DIREC is not a directory


 Example:
      PROGRAM TFLIST
C       to test FLLIST
      CHARACTER*8192 LIST
      NUMB = 500
      CALL FLLIST('$',LIST,NUMB)
      PRINT *,'NUMB = ',NUMB
C              now extract and print the file names
      J = 1
      DO 10 I = 1, NUMB
        K = INDEX(LIST(J:),CHAR(0))
        IF(K.EQ.0) STOP ' terminator not found'
        PRINT *,LIST(J:J+K-1)
        J = J + K
   10 CONTINUE
      END

%FLLOAD

  CALL FLLOAD(NAME,BUF,LENG,IERR)

 Purpose:  loads a file directly into a buffer 

 Parameters:

  NAME  - CHARACTER name of file from which the data are to be loaded

   BUF  - ARRAY or CHARACTER to receive the contents of the file

  LENG  - INTEGER size of BUF in bytes

  IERR  - INTEGER error status
               -1: file does not exist
               -2: file can not be read
                0: LENG is OK
               +1: file is a directory

 Comment: only the first LENG bytes of the file will be read into BUF, 
          the rest will be lost. Conversely, any bytes in BUF beyond the
          size of the file will remain unaltered.

%FLSAVE

  CALL FLSAVE(NAME,BUF,LENG,KTYPE,IERR)

 Purpose:  saves a buffer in memory to a file

 Parameters:

  NAME  - CHARACTER name of file to which the data are to be saved

   BUF  - ARRAY or CHARACTER to save to the file

  LENG  - INTEGER size of BUF in bytes

  KTYPE - INTEGER type to give to file (in range 1 to 4097)

  IERR  - INTEGER error status
                0: file written OK
               +1: can not write file
               +2: file name too long

 Comment: KTYPE will be set to FFD(hex) if it is outside the allowed range;
          This is the 'Data' type.

%FLSIZE

  CALL FLSIZE(NAME,LENG,IERR)

 Purpose:  finds length of a file in bytes

 Parameters:

  NAME  - CHARACTER name of file of which to find the size

  LENG  - INTEGER (returned) file size in bytes

  IERR  - INTEGER error status
               -1: file does not exist
                0: LENG is OK
               +1: file is a directory

%FLTIME

  CALL FLTIME(SET,NAME,KTIME,IERR)

 Purpose:  reads or sets the datestamp of a file

 Parameters:

  SET   - LOGICAL .TRUE. to set the time, .FALSE. to read it

  NAME  - CHARACTER name of file to set or read the time

  KTIME - INTEGER ARRAY dimension 2; 1st word contains m.s.byte
                                     2nd word, the 4 l.s. bytes

  IERR  - INTEGER error status
               -1: file does not exist
                0: KTIME read or set OK
               +1: file is a directory
 Comments:
      The upper 3 bytes of KTIME(1) are ignored
      This is most useful for transerring the time from one file to another.
      Changing the time is possible but 5-byte arithmetic is tricky.

 Example:
C       get the time of file 'Picture' in ITIM
      DIMENSION ITIM(2)
      CALL FLTYPE(.TRUE.,'Picture',ITIM,IERR)
      IF(IERR.NE.0) PRINT *,'error ',IERR,' getting file time'

%FLTYPE

  CALL FLTYPE(SET,NAME,KTYPE,IERR)

 Purpose:  reads or sets the type of a file

 Parameters:

  SET   - LOGICAL .TRUE. to set the type, .FALSE. to read it

  NAME  - CHARACTER name of file to set or read the type

  KTYPE - INTEGER (returned) file type (e.g. ?IFFF for text)

  IERR  - INTEGER error status
               -1: file does not exist
                0: KTYPE read or set OK
               +1: file is a directory
               +2: KTYPE not in the allowed range [000:FFF](Hex)

 Example:
C       set the type of file 'Picture' to be 'Sprite' (FF9)Hex
      CALL FLTYPE(.TRUE.,'Picture',?IFF9,IERR)
      IF(IERR.NE.0) PRINT *,'error ',IERR,' setting file type'

%FPSSET

  CALL FLSSET(INX,UFL,OFL,DVZ,IVO)

 Purpose:  sets the floating point exception flags

 Parameters:

  INX   - LOGICAL .TRUE. sets the INeXact flag           (default: not set)

  UFL   - LOGICAL .TRUE. sets the UnderFLow flag         (default: not set)

  OFL   - LOGICAL .TRUE. sets the OverFLow flag          (default: set)

  DVZ   - LOGICAL .TRUE. sets the DiVide-by-Zero flag    (default: set)

  IVO   - LOGICAL .TRUE. sets the InValid-Operation flag (default: set)

 Comments: 1) setting these .FALSE. turns off the corresponding floating
           point exception so that instead of the program dying, a suitable
           result is supplied.
           2) the default values are usually the best unless you want to
           detect an underflow.
           3) use FLSTAT to get the current values of the exception flags

%FPSTAT

  CALL FLSTAT(INX,UFL,OFL,DVZ,IVO)

 Purpose:  reads the floating point exception flags

 Parameters:

  INX   - LOGICAL returns the INeXact flag           (default: .FALSE.)

  UFL   - LOGICAL returns the UnderFLow flag         (default: .FALSE.)

  OFL   - LOGICAL returns the OverFLow flag          (default: .TRUE.)

  DVZ   - LOGICAL returns the DiVide-by-Zero flag    (default: .TRUE.)

  IVO   - LOGICAL returns the InValid-Operation flag (default: .TRUE.)

 Comments: 1) if these are .FALSE. then a suitable result is supplied
           instead of the program dying. 
           2) the default values are usually the best unless you want to
           detect an underflow.
           3) use FLSSET to set new values for the exception flags

              
%GTARGS

 CALL GTARGS( STRING )

 Purpose: returns the command line used to start the program
                             
 Parameters:
 
  STRING - CHARACTER string for the returned command line.
             If it is too short, the information is truncated.
             Otherwise it will be left adjusted and blank filled.

 Example:
      CHARACTER*25 COMMND
      CALL GTARGS(COMMND)
      PRINT *,COMMND
 Results in: Tutils
  (or similar)

%IGET

  IRESUL = IGET()

 Purpose: reads one character from the keyboard, without waiting for
           <RETURN> to be pressed

 Parameters: none

 Result: INTEGER containing the ASCII value of the next key pressed

 Example:
    10 PRINT *,' Do you want to continue? (Y/N)'
       KEY = IGET()
 C         print it out! Otherwise it will not appear on the screen
       PRINT *,CHAR(KEY)
       IF(KEY .EQ. ICHAR('N')) STOP
       IF(KEY .NE. ICHAR('Y')) GO TO 10
       ... continuation code
      
 Comment: must not be used in a Wimp program
 
          behaves in a similar way to the Acorn BASIC statement 'GET'

%INKEY

  IRESUL = INKEY( NUM )

 Purpose: scans the keyboard buffer under various conditions, or returns
          the version number of the RISC-OS operating system.

 Argument:

  NUM - INTEGER whose value on entry controls the behaviour of the function,
        possible values are: 
        (1) = positive or zero, but less than 32768
        (2) = - (an internal key number + 1) (in the range -1 to -128)     
        (3) = - 256

 Result: INTEGER, varies depending on the value of NUM
        (1) The ASCII value of any key pressed within NUM centiseconds of
            the call to INKEY; or -1 if none pressed.
        (2) 255 if the key is currently pressed; zero otherwise
        (3) The OS version number 
                   Arthur:       160
                   RISC-OS 2.00: 161
                   RISC-OS 2.01: 162
                   RISC-OS 3.10: 164
 Errors: 

    if NUM < -256  it is treated modulo 256 so that -257 is equivalent to -1
    if -256 < NUM < -128 it will return the internal key number of any key
                         pressed between -(NUM+128) and 127, 255 if none.
    if NUM > 32767 it is treated modulo 32768

 Example (1):
       IF(INKEY(100) .NE. ICHAR(' ')) GO TO 100
 C         space bar pressed within 1 second here
       ... 
   100 more code

 Example (2):
       IF(INKEY(-2) .EQ. 0) GO TO 100
 C        Control key is pressed here
       ...
   100 more code

 Example (3):
       PRINT *,' RISC-OS version is: ',INKEY(-256)

 Comments: the internal key numbers are related to the keyboard layout, not 
 the ASCII values for the character inscribed on the key. There is a list 
 in the Programmer's Reference Manual, and in a BASIC manual where they are
 called 'INKEY' numbers and are given as - (the internal key number +1) as
 required for the input type (2) above

 must not be used in a Wimp program

%JVALK

 IERR = JVALK(VALUE,KEYW)

 Purpose: to set up a user-defined value to be used in EVALF (q.v.)

 Parameters:

  VALUE  - CHARACTER a numeric expression defining a value

  KEYW   - CHARACTER a keyword to be used in EVALF; it must be 
                   1) alphabetic
                   2) no more than 4 characters

  Result: IERR=0 if OK, 
             =-1 if more than 4 characters in KEYW
             =-2 if non-alphabetic characters in KEYW
             >0  if problem in VALUE (see EVALF for errors).

  Comments: 1) spaces are inored in KEYW and VALUE

  Example

      IF(JVALK('(1+SQRT(5))/2','Tau').NE.0) STOP 'Problem in JVALK'
      PRINT *,'Tau**2-Tau = ',EVALF('Tau**2-Tau',IERR),', error =',IERR
C    gives the result Tau**2-Tau = 1.0

%MOUSE

 CALL MOUSE(MX,MY,MBUTTN)

 Purpose: gets the current position of the mouse pointer and which button is
          being pressed.

 Parameters:

  MX,MY  - INTEGER returns the current screen coordinates of the mouse
                   pointer

  MBUTTN - INTEGER returns the current mouse butons pressed;
                   1: adjust
                   2: menu
                   4: select
                      or the sum if more than one is pressed

 Comment: this routine returns the current mouse parameters.
          Use WPGTPI in the Wimp library to get similar information in the
          wimp environment to get the values stored at the time of a
          mouse click.
          
%MSGCLS
 CALL MSGCLS

 Purpose: closes the messages file

 No Parameters

 No errors

 Comments: 1) Not available in RISC-OS 2
           2) This does nothing if the file is not open
           3) It should be called before trying to open
              a new messages file with MSGOPN
           4) Uses SWI MessgeTrans_CloseFile PRM3 3-750

%MSGGET
 IERR=MSGGET(NSUBS,TOKEN,<SUB1,SUB2,SUB3,SUB4>,TEXT)

 Purpose: gets a string from the messages file with
          possible parameter substitutions

 Parameters:

    NSUBS - INTEGER the number of substitutions in the message
                    in the range 0 to 4; the number of SUBn parameters
                    following must be equal to this
                   
    TOKEN - CHARACTER token to be found in the messages file

     SUB1 - optional CHARACTER to be substituted for %0 in the result

     SUB2 - optional CHARACTER to be substituted for %1 in the result

     SUB3 - optional CHARACTER to be substituted for %2 in the result

     SUB4 - optional CHARACTER to be substituted for %3 in the result

     TEXT - CHARACTER returned string with message. The message will be
                     padded with blanks or truncated to fit into RESULT

 Result:  INTEGER returned error
                 0: OK
                 5: unable to find TOKEN
                 6: nsubs <0 or >4

 Comments: 1) Not available in RISC-OS 2
           2) The number of SUBn parameters MUST be the same as the value
              of NSUBS otherwise a computer crash is almost inevitable.
           3) The simplest messages file has lines with a token terminated
              with ':' and followed by the text to be substituted. The full
              file format is in the RISC-OS 3 PRM p 3-738
           4) It will use only the global messages if no message file has
              been 'opened' with MSGOPN.
           5) Uses MessageTrans_Lookup  PRM3 3-745
       

%MSGGGS
 IERR=MSGGGS(NSUBS,TOKEN,<SUB1,SUB2,SUB3,SUB4>,TEXT)

 Purpose: gets a string from the messages file with possible
          parameter substitutions and translation of
          system variables and special characters

 Parameters:

    NSUBS - INTEGER the number of substitutions in the message
                    in the range 0 to 4; the number of SUBn parameters
                    following must be equal to this
                   
    TOKEN - CHARACTER token to be found in the messages file

     SUB1 - optional CHARACTER to be substituted for %0 in the result

     SUB2 - optional CHARACTER to be substituted for %1 in the result

     SUB3 - optional CHARACTER to be substituted for %2 in the result

     SUB4 - optional CHARACTER to be substituted for %3 in the result

     TEXT - CHARACTER returned string with message. The message will be
                     padded with blanks or truncated to fit into RESULT

 Result:  INTEGER returned error
                 0: OK
                 5: unable to find TOKEN
                 6: nsubs <0 or >4

 Comments: 1) Not available in RISC-OS 2
           2) The number of SUBn parameters MUST be the same as the value
              of NSUBS otherwise a computer crash is almost inevitable.
           3) The simplest messages file has lines with a token terminated
              with ':' and followed by the text to be substituted. The full
              file format is in the RISC-OS 3 PRM p 3-738
           4) system variables inside <> will be translated and characters
              after '|' are usually translated into a binary equivalent.
              E.g. |M for CR.
           5) It will use only the global messages if no message file has
              been 'opened' with MSGOPN.
           6) Uses MessageTrans_GSLookup  PRM3 3-755
       
%MSGOPN
  IERR=MSGOPN(FNAME,FBUF)

 Purpose: opens a messages file for use by MSGGET and MSGGGS

 Paramters:

    FNAME - CHARACTER file name to be opened

     FBUF - CHARACTER buffer to hold the messages array

 Result: INTEGER returned error code:
                 0: file opened OK
                 1: file can not be found
                 2: file already open
                 3: FBUF is not big enough

 Comments: 1) Not available in RISC-OS 2
           2) Only one messages file may be open at a time
           3) The buffer length needed can be found with MSGSIZ
              but appears to be 4 bytes more than the file length
           4) Uses MessageTrans_OpenFile PRM3 3-743

%MSGSIZ
 ISIZE=MSGSIZ(FNAME)

 Purpose: finds the buffer size needed for MSGOPN to open
          a messages file

 Parameters:

    FNAME - CHARACTER file name to be opened

 Result:     INTEGER returned buffer size in BYTES or
                 0 if the file exists in memory
                -1 if the file does not exist

 Comments: 1) Not available in RISC-OS 2
           2) Uses MessageTrans_FileInfo

%NEXTWD
 CALL NEXTWD(STRING,JS,WORD,LW)

 Purpose: finds the next word, terminated by a blank, (ASCII 32)in a string.
          Leading blanks are ignored

 Parameters:

    STRING - CHARACTER contains the text to be parsed

    JS     - INTEGER points to the character in STRING to start looking for
                     a word. Updated to point to the character after the
                     word, so that it is ready to look for the next word.
                     JS is set to LEN(STRING)+1 if there are no blanks after
                     the last word

    WORD   - CHARACTER of sufficient length to receive the longest word;
                       updated with the word, left justified and blank fill

    LW     - INTEGER returns the length of the word. Zero if the rest of
                     STRING is blank or JS > LEN(STRING)

 Errors: If WORD is too short, the returned word will be truncated, and LW
         will be set to -(true length of the word). JS is updated as if WORD
         were long enough

 Example:
      CHARACTER STRING*26,NAME(10)
      DATA STRING/'Angela   Barbara  Caroline'/
      JS=12
      CALL NEXTWD(STRING,JS,WORD,LW)

 Results in: WORD = 'rbara     ', JS=17 and LW=5 

%OSBYTE

 CALL  OSBYTE( IFUNC, IARG1, IARG2 )

 Purpose: accesses the operating system OS_Byte routines which return
          no values

 Parameters:

    IFUNC - INTEGER in the range 0 to 255 specifying the function
 
    IARG1 - INTEGER usually the value to be set, varies with the IFUNC

    IARG2 - INTEGER usually 0, most values of IFUNC need only IARG1

 Examples:     
C  reset the keyboard auto-repeat delay to 32 centiseconds
      CALL OSBYTE(11,32,0)

C  enable the cursor keys to be read using IGET or INKEY
      CALL OSBYTE(4,1,0)

C  print the title, version, and completion date of the operating system.
      CALL OSBYTE(0,0,0)

             
 Comments: see the Programmers Reference Manual for possible values of IFUNC

           same as the Acorn BASIC *FX

           similar routine available in Fortran77 (release 2)

%OSBYTE1

 CALL OSBYTE1( IFUNC, IARG1, IARG2, IRES1 )

 Purpose: accesses the operating system OS_Byte routines which return
          a single number

 Parameters:

    IFUNC - INTEGER in the range 0 to 255 specifying the function
 
    IARG1 - INTEGER first value to be set, varies with the IFUNC

    IARG2 - INTEGER second value to be set, 
            (either IARG1 or IARG2 may be set to 0 if not needed)

    IRES1 - INTEGER returns the required value

 Example:
C          find the 'printer ignore' character
     CALL OSBYTE1(182,0,255,IRES1)

 Results in: IRES1 negative when there is no printer ignore character
             IRES1 zero or positive there is; to find out which
             CALL OSBYTE1(246,0,255,IRES1) which returns the ASCII value
             of the ignore character in IRES1

 Comments: see the Programmers Reference Manual for possible values of IFUNC

           similar routine available in Fortran77 (release 2)

%OSBYTE2

 CALL OSBYTE2( IFUNC, IARG1, IARG2, IRES1, IRES2 )

 Purpose: accesses the operating system OS_Byte routines which return
          two values

 Parameters:

    IFUNC - INTEGER in the range 0 to 255 specifying the function
 
    IARG1 - INTEGER first value to be set, varies with the IFUNC

    IARG2 - INTEGER second value to be set, 
            (either IARG1 or IARG2 may be set to 0 if not needed)

    IRES1 - INTEGER returns the first requested value
 
    IRES2 - INTEGER returns the second requested value

 Examples:
C        find the current screen mode in MODE, (IDUM is unused)
      CALL OSBYTE2(135,0,0,IDUM,MODE)

C        find the current position of the text cursor in ICOL,IROW
      CALL OSBYTE2(134,0,0,ICOL,IROW)

    
 Comments: see the Programmers Reference Manual for possible values of IFUNC

           similar routine available in Fortran77 (release 2)

%OSCLI
 
  LRESUL = OSCLI( STRING )

 Purpose: passes a text string to the Command Line Interpreter for execution

 Parameters:

   STRING - CHARACTER variable contains the command to be executed
 
 Result: returns .TRUE. if the command was successful

 Errors:  when the Command Line Interpreter detects an error, the
          function returns .FALSE.; to find what the error was,
          use OSGETERROR

 Example:  
      LOGICAL L, OSCLI
      L = OSCLI('CAT')
           sends the catalogue of the current directory to the screen

 Comments: emulates the *commands 

           similar routine available in Fortran77 (release 2) 

%OSGETERROR

 CALL OSGETERROR( IERRNO, STRING )

 Purpose: determines cause of an error in OSCLI  (see above).

 Parameters:

    IERRNO - INTEGER returns the error number from the last OSCLI call
           (zero if none); they range from &1E000 to &1EFFF.
           Bit 31 is set for serious errors, usually hardware problems.

    STRING - CHARACTER string for the returned error message. If it is too
             short, the message is truncated. (*40 is usually big enough).
             Otherwise it will be left adjusted and blank filled.

 Example:
      CHARACTER*40 ERTEXT
      LOGICAL OSCLI
      IF(.NOT.OSCLI('dir fred')) THEN
C        error found here, get it and print it
        CALL OSGETERROR(IERR,ERTEXT)
        PRINT *,'Error from OSCLI: ',ERTEXT
        STOP 'Help!'
      ENDIF

 Results in:

 Error from OSCLI: directory 'fred' not found
 Stop: Help!

 Comments: Error numbers are listed in the Programmers Reference Manual,
            
           similar routine available in Fortran77 (release 2)

%OSWORD

 CALL OSWORD( ICODE, IARRAY )

 Purpose: system utility similar to OSBYTE but returns an array of data;
          can read the time and date, set the screen palette, read the
          definition of a re-definable character, etc.

 Parameters:

    ICODE - INTEGER specifying the operation to do
 
    IARRAY - an INTEGER array in which values are sent or returned, the size
             varies depending on ICODE. If the values are in byte format
             rather than in words, it is permissible to use a CHARACTER
             variable of sufficient length instead.

 Errors: Nothing is returned if you call it with an unknown ICODE. 
         Nothing usually happens if you call it with unexpected input in
         IARRAY. 
         If IARRAY is too small, memory is likely to be overwritten, 
         and program behaviour become unpredictable.

 Example: to use the interval timer 
        INTEGER ITIME(2),JTIME(2)
        CALL OSWORD(3,ITIME)
        .......code here.....
        CALL OSWORD(3,JTIME)
        PRINT 102,FLOAT(JTIME(1)-ITIME(1))*0.01
    102 FORMAT(' Time taken (seconds)=',F8.2)

 Comments: a list of ICODE values for OSWORD is given in the Programmers
           Reference Manual
           
           similar routine available in Fortran77(release 2)

%PRCLOS

 CALL PRCLOS

 Purpose: closes print job and sends it to the printer

 Parameters: none

 Errors: none (does nothing if not printing)

 Comments: 1)see PROPEN for usage
           2) uses PDriver_EndJob, PRM3: 3-617

%PRINFO

 CALL PRINFO(TYPE,VERS,NAME,IXDPI,IYDPI,IXTONE,IYTONE,JFEAT)

 Purpose: finds information on the current printer driver

 Parameters:

   TYPE  CHARACTER returned printer type; e.g. 'Postscript'

   VERS  returned version number e.g. 4.42
                  negative if there is no printer driver loaded

   NAME  CHARACTER returned name of driver

 IXDPI,IYDPI returned dots/inch in x and y directions

 IXTONE,IYTONE returned half-toning resolution per inch

  JFEAT  returned word of printer features

 Errors: VERS returned =-1.0 if there is no printer driver loaded

 Comments: 1) The printer features are returned as bits in JFEAT:
           bit set
           0: colour           1: limited colour    2: discrete colours
           8: no filled shapes 9: no thick lines   10: no overwriting
          11: transformed sprites                  12: new fonts
          24: no screen dumps 25: arbitrary transformations
          26: inserts illustrations (not supported by Fortran)
          27: miscellaneous operations (not supported by Fortran)
          28: configurable driver (not supported by Fortran)
          29: allows declared fonts
           2) uses PDriver_Info, PRM3: 3-602

%PRKILL

 CALL PRKILL

 Purpose: stops the print immediately and returns output to the screen

 No parameters

 Error: does nothing if printer has not been opened

 Comments: 1) use this to abandon the current printing immediately,
              not sending the current page to be printed
           2) subsequent calls to PRPBEG, PRPEND or PRCLOS are ignored
              until another call to PROPEN
           3) use PRCLOS to end a sequence normally
           4) uses PDriver_AbortJob: PRM3 3-619

%PROPEN

 CALL PROPEN(JOBNAM,ORIENT,SCALE,IERR)

 Purpose: starts a print job, redirecting output to the printer.

 Parameters:

 JOBNAM  CHARACTER job name (may be just ' ')

 ORIENT  CHARACTER*1 orientation:
                     either 'P' for normal paper orientation
                     or 'L' for 90 rotation

  SCALE  factor to multiply the supplied coordinates to get
         them into OS units 
         (should not be wildly different from 1.0)

   IERR  returned error number
         =0 for OK
         =1 if no printer driver loaded
         =2 if ORIENT is neither 'P' nor 'L'
         =3 if printer stream is already in use

 Errors: (see IERR above)

 Comments: 1) PROPEN just prepares for printing; the sequence of calls
              subsequently needed is:
                PRPBEG(NCOPY,IBGCOL,IXY,IERR) to start printing a page
                  normal graphics commands and PRINT (but not GRWOG)
                  FTPRNT to print in a font
                PRPEND to end the page print
                repeat these for more pages
              PRCLOS to end the print job and return output to the screen
           2) printing may require many passes through the code between
              PRPBEG and PRPEND. This is handled automatically but you
              must have PRBEG and PREND in the same routine and expect
              the code between to be called many times. This is because
              less sophisticated printers expect to print small areas
              at a time. The area currently being printed is given you
              in the IXY array of PRBEG.
           3) units are standard OS units but you can scale them a bit
              with SCALE.
           4) The origin is at the bottom left of the paper
              for Portrait orientation and at the top left for
              Landscape with the image rotated to the right by 90
              so you must add the margins to your coordinates.
           5) use PRSIZE to find the page size and printable area
           6) SCALE scales the all the coordinates so scale the margins
              from PRSIZE by 1/SCALE before adding them to your coordinates
           7) uses many PDriver routines.

%PRPBEG

 CALL PRPBEG(NCOPY,IBGCOL,IXY,IERR)

 Purpose: starts sending a page of graphics to the printer

 Parameters:

  NCOPY   the number of copies required

  IBGCOL  background colour, normally -1 for white but can be
          BBGGRRxx (Hex) for amounts of Blue, Green and Red;
          white is -1 = FFFFFFFF (Hex);
          the last two Hex digits are ignored

    IXY   array (4) to receive the graphics window which will be used
          (x-left, y-bottom, x-right and y-top in OS units)

    IERR  should be returned =0
          returned errors:
          1: PRPBEG has already been called without a call to PRPEND
          2: PROPEN has not been called
            You MUST call PRKILL after an error here so that
            the printer driver is not left hanging.

 Comments: 1) PRPBEG should be called after PROPEN which starts the print
              sequence.
           2) the graphics, font print and ordinary print requests following
              PRPBEG for sending to the printer should be terminated by a
              call to PRPEND.
           3) you only need to send information within the graphics window
              given by IXY; all other information will be masked off.
           4) BEWARE! the code between PRPBEG and PRPEND may be passed
              through several times for covering all areas of the page, so
              only put code there which is essential for the printing and
              setting up colours.
              E.g. a call to FTFIND must NOT be in this piece of code.
           5) The window is in the landscape reference frame if landscape
              orientation has been selected by PROPEN
           6) Uses PDriver_DrawPage  PRM3 3-626.

%PRPEND

 CALL PRPEND

 Purpose: signals the end of commands sending information to a page of print.

 No parameters:

 No errors: (calling it at the wrong time will do nothing)

 Comments: 1) See PRPBEG and PROPEN for how to use this call in the right
              sequence for printing
           2) Uses PDriver_GetRectangle PRM3 3-628.

%PRSIZE

 CALL PRSIZE(IPAGSZ,IPAREA)

 Purpose: finds the page dimensions for printing

 Parameters:

 IPAGSZ  array (2) to receive the full page size for printing in OS units

 IPAREA  array (4) to receive the printable area in OS units:
         (x-left, y-bottom, x-right and y-top)
         so IPAREA(1) is the left margin,
         IPAGSZ(1)-IPAREA(3) is the right margin etc.
         But see comment 1) below.

 Error: returns IPAGSZ = (-1,-1) if there is no printer driver loaded

 Comment: 1) the returned dimensions will be in Portrait orientation and
             in standard OS units - UNLESS - you are printing within a 
             PROPEN - PRCLOS sequence when the orientation and scale
             of IPAREA will be transformed according to SCALE and ORIENT
             defined in PROPEN.
             IPAGSZ will always be in the Portrait orientation in OS units.

          2) uses PDriver_PageSize: PRM3 3-609


%RSPACE

 CALL RSPACE(SUBR,NWORD [,P1,P2, ...])

 Purpose: allocates memory space for dynamic use in a subroutine
          which it calls

 Parameters:

  SUBR    EXTERNAL subroutine name to receive the memory

  NWORD   INTEGER  number of words to allocate

  P1,P2 ... optional parameters to be passed to SUBR (see note 2)

 Errors: only if you try to allocate more memory than is available
         to applications

 Comments: 1) the subroutine will be called with at least two arguments
              so that its definition must be:
                    SUBROUTINE SUB(ARRAY,NWORD)
                    DIMENSION ARRAY(NWORD)
           2) the optional parameters will be passed as arguments 3, 4 etc.
              to the subroutine.
           3) the array will disappear when you RETURN from SUB
           4) the array is NOT initialised to zero

 Example:
      EXTERNAL DUMPER
C        get file size (bytes)
      CALL FLSIZE(FLNAM,NBYTES,IER)
C        dump it. (Note we round up to next word length)
      CALL RSPACE(DUMPER,(NBYTES+3)/4),IOK)
   ......
      SUBROUTINE DUMPER(IBUF,NWORD,IOK)
      DIMENSION IBUF(NWORD)
      LOGICAL IOK
C       load file into buffer
      CALL FLLOAD(FLNAM,IBUF,NWORD*4,IER)
      PRINT 101,IBUF
  101 FORMAT(10Z9.9)
      IOK = .TRUE.
      RETURN
      END

%SOUND
 
 CALL SOUND(IAMPL,IPITCH,LENGTH)

 Purpose: makes a musical sound 

 Parameters:

  IAMPL  INTEGER amplitude of the sound in the range:
                 0 = inaudible to 127 = loudest possible 

  IPITCH INTEGER pitch of the note in the range -47 to +47 with nominal 
                 middle C at IPITCH = 0; 1 unit = 1 semitone

  LENGTH INTEGER length of the note in units of 1/20 second
                 in the range 1 to 254  

 Errors: none, the parameters are forced to be within range 

 Comments: 1) waits for any previous sound to stop before sounding the
              new note. 
           2) use VOICE to set the timbre of the note and the stereo
              position 
           3) no sound will be heard if the sound system has been turned off 
              with *Speaker OFF, or *Audio OFF. A call to VOICE will turn
              them on the *audio, but leave the *speaker unaltered.

%TRACEP

 CALL TRACEP(N,NAME,LBEG,LCALL) 

 Purpose:  finds name and location of routine and where it was called from
           in a trace-back.

 Parameters:

   N     INTEGER depth to trace back:
                 =0 to report on TRACEP itself
                  1 to report on the routine calling TRACEP
                  2 to report on the routine calling the routine which
                         calls TRACEP
                  etc.
   NAME  CHARACTER to receive the name of the routine. The name will be
                   truncated or extended with blanks to fill NAME.

   LBEG  INTEGER returned absolute location of the beginning of routine NAME
                 =0 if the trace-back is requested beyond the main program

   LCALL INTEGER returned absolute location of where the routine wass called
                 from.

 Errors: none

 Example:

      SUBROUTINE TRACEQ(LUN)
C                 to make a subroutine call traceback
      CHARACTER*12 NAME
C                 print header
      WRITE (LUN,101)
  101 FORMAT(/' Routine         at    called from'/)
      N = 0
   10 N = N + 1
C                 get name and locations of Nth routine
      CALL TRACEP(N,NAME,LBEG,LCALL)
C                 check it exists
      IF(LBEG.GT.0) THEN
        WRITE (LUN,102)NAME,LBEG,LCALL
  102   FORMAT(1X,A12,2Z8)
        GO TO 10
      ENDIF
      WRITE (LUN,103)
  103 FORMAT(/' End of trace-back'/)
      RETURN
      END

%VOICE 

 CALL VOICE(IVOICE,ISTERE)

 Purpose: sets the timbre and stereo position for SOUND 

 Parameters:

  IVOICE INTEGER a voice number 
                 1:
                 2:
                 etc (max 32, but see comments below)
                -1: restore original state (ISTERE is not needed)

  ISTERE INTEGER stereo position 
                    0 = in the middle 
                 +127 = extreme right 
                 -127 = extreme left 

 Errors: none, parameters are forced to be within the allowed ranges 

 Comments: 1) waits for any sound activity to stop before changing the voice
           2) use SOUND to make the actual notes 
           3) call VOICE(-1) when you have finished to tidy things up 
           4) if the voice number is undefined, the previous voice will be 
              used. Type *VOICES on the command line to find which ones
              are set up.
