






  I. GETTING STARTED . . . . . . . . . . . . . . . . . . . . . . . . .    5
    1 Hardware requierements . . . . . . . . . . . . . . . . . . . . .    5
    2 Software requirements  . . . . . . . . . . . . . . . . . . . . .    5
    3 Creating a backup disk . . . . . . . . . . . . . . . . . . . . .    5
    4 Installing Comal on a floppy disk system . . . . . . . . . . . .    6
    5 Installing Comal on a hard disk system . . . . . . . . . . . . .    6
    6 Starting Comal from Workbench  . . . . . . . . . . . . . . . . .    6
    7 Starting Comal from the Shell (the CLI)  . . . . . . . . . . . .    7
    8 Setting the Comal environment  . . . . . . . . . . . . . . . . .    7
      8.1 Setting the Comal TOOLTYPES  . . . . . . . . . . . . . . . .    7
      8.2 The Preferences program Pref . . . . . . . . . . . . . . . .   11

  II. TUTORIAL . . . . . . . . . . . . . . . . . . . . . . . . . . . .   12
    1 Your first Comal programs  . . . . . . . . . . . . . . . . . . .   12
    2 Working with graphics  . . . . . . . . . . . . . . . . . . . . .   13
    3 Repetition . . . . . . . . . . . . . . . . . . . . . . . . . . .   15
    4 Selection  . . . . . . . . . . . . . . . . . . . . . . . . . . .   18
    5 Procedures . . . . . . . . . . . . . . . . . . . . . . . . . . .   20
    6 Making modules . . . . . . . . . . . . . . . . . . . . . . . . .   23
    7 Looking into modules . . . . . . . . . . . . . . . . . . . . . .   26
    8 Making gadgets . . . . . . . . . . . . . . . . . . . . . . . . .   27
    9 Event driven programs  . . . . . . . . . . . . . . . . . . . . .   31
    10 Debugging programs  . . . . . . . . . . . . . . . . . . . . . .   33

  III. THE PROGRAMMING ENVIRONMENT . . . . . . . . . . . . . . . . . .   36
    1 The keyboard . . . . . . . . . . . . . . . . . . . . . . . . . .   36
    2 The mouse  . . . . . . . . . . . . . . . . . . . . . . . . . . .   37
    3 The menus  . . . . . . . . . . . . . . . . . . . . . . . . . . .   38
      3.1 The Project menu . . . . . . . . . . . . . . . . . . . . . .   38
      3.2 The Edit menu  . . . . . . . . . . . . . . . . . . . . . . .   39
      3.3 The Search menu  . . . . . . . . . . . . . . . . . . . . . .   40
      3.4 The Macros menu  . . . . . . . . . . . . . . . . . . . . . .   40
      3.5 The Settings menu  . . . . . . . . . . . . . . . . . . . . .   41
      3.6 The Program menu . . . . . . . . . . . . . . . . . . . . . .   42
      3.7 The Trace menu . . . . . . . . . . . . . . . . . . . . . . .   43

  IV. DESCRIPTION OF THE Comal LANGUAGE  . . . . . . . . . . . . . . .   45
    1 Data types, variables and expressions  . . . . . . . . . . . . .   45
      1.1 Identifiers  . . . . . . . . . . . . . . . . . . . . . . . .   45
      1.2 Simple data types  . . . . . . . . . . . . . . . . . . . . .   45
        1.2.1 Constants  . . . . . . . . . . . . . . . . . . . . . . .   46
        1.2.2 Expressions  . . . . . . . . . . . . . . . . . . . . . .   47
        1.2.3 Variables  . . . . . . . . . . . . . . . . . . . . . . .   51
      1.3 Structured data types  . . . . . . . . . . . . . . . . . . .   53
        1.3.1 Indexed variables  . . . . . . . . . . . . . . . . . . .   53
        1.3.2 Strucs (records) . . . . . . . . . . . . . . . . . . . .   55
      1.4 Dynamic variables. Pointer variables . . . . . . . . . . . .   56
      1.5 Type definition  . . . . . . . . . . . . . . . . . . . . . .   61
    2 Program flow control statements  . . . . . . . . . . . . . . . .   63

                                          1





      2.1 Selection  . . . . . . . . . . . . . . . . . . . . . . . . .   63
        2.1.1 The IF statements  . . . . . . . . . . . . . . . . . . .   63
        2.1.2 The CASE statement . . . . . . . . . . . . . . . . . . .   66
      2.2 Repetition . . . . . . . . . . . . . . . . . . . . . . . . .   67
      2.3 Branching  . . . . . . . . . . . . . . . . . . . . . . . . .   72
        2.3.1 The GOTO statement . . . . . . . . . . . . . . . . . . .   72
        2.3.1 The EXIT statement . . . . . . . . . . . . . . . . . . .   73
    3 Procedures and functions . . . . . . . . . . . . . . . . . . . .   73
      3.1 Procedures . . . . . . . . . . . . . . . . . . . . . . . . .   73
        3.1.1 Procedures without parameters  . . . . . . . . . . . . .   74
        3.1.2 Value parameters . . . . . . . . . . . . . . . . . . . .   75
        3.1.3 Reference parameters . . . . . . . . . . . . . . . . . .   77
        3.1.4 Local procedures . . . . . . . . . . . . . . . . . . . .   78
        3.1.5 Local and global variables. CLOSED procedures  . . . . .   79
      3.2 Functions  . . . . . . . . . . . . . . . . . . . . . . . . .   82
    4 Exception handling . . . . . . . . . . . . . . . . . . . . . . .   83
      4.1 The TRAP statement . . . . . . . . . . . . . . . . . . . . .   83
      4.2 The TRAP ESC statement . . . . . . . . . . . . . . . . . . .   85
    5 IO statements  . . . . . . . . . . . . . . . . . . . . . . . . .   86
      5.1 The PRINT statement  . . . . . . . . . . . . . . . . . . . .   86
      5.2 The INPUT statement  . . . . . . . . . . . . . . . . . . . .   88
      5.3 Redirection of IO  . . . . . . . . . . . . . . . . . . . . .   89
      5.4 System functions performing IO . . . . . . . . . . . . . . .   90
      5.5 Other IO related statements and functions  . . . . . . . . .   90
    6 File statements  . . . . . . . . . . . . . . . . . . . . . . . .   92
      6.1 Sequential binary files  . . . . . . . . . . . . . . . . . .   93
      6.2 Random access binary files . . . . . . . . . . . . . . . . .   95
      6.3 ASCII files  . . . . . . . . . . . . . . . . . . . . . . . .   97
      6.4 File system related functions  . . . . . . . . . . . . . . .   99
      6.5 READ and DATA statements . . . . . . . . . . . . . . . . . .  100
    7 Miscellaneous statements, procedures and functions . . . . . . .  103
      7.1 DOS statements and functions . . . . . . . . . . . . . . . .  103
      7.2 Time related statements and functions  . . . . . . . . . . .  106
      7.3 Random numbers . . . . . . . . . . . . . . . . . . . . . . .  107
      7.4 STOP and END statements  . . . . . . . . . . . . . . . . . .  108
      7.5 Interrupt procedures . . . . . . . . . . . . . . . . . . . .  109
      7.6 Mathematical functions . . . . . . . . . . . . . . . . . . .  109
      7.7 Functions involving strings  . . . . . . . . . . . . . . . .  111
      7.8 Other functions  . . . . . . . . . . . . . . . . . . . . . .  112
    8 Objects  . . . . . . . . . . . . . . . . . . . . . . . . . . . .  113
      8.1 Introduction to OOP  . . . . . . . . . . . . . . . . . . . .  113
      8.2 Methods  . . . . . . . . . . . . . . . . . . . . . . . . . .  118
      8.3 Inheritance  . . . . . . . . . . . . . . . . . . . . . . . .  120
      8.4 Virtual methods  . . . . . . . . . . . . . . . . . . . . . .  122
      8.5 Constructors . . . . . . . . . . . . . . . . . . . . . . . .  125
      8.6 Destructors  . . . . . . . . . . . . . . . . . . . . . . . .  126
    9 Modules  . . . . . . . . . . . . . . . . . . . . . . . . . . . .  128
      9.1 Making modules . . . . . . . . . . . . . . . . . . . . . . .  128
      9.2 Using modules  . . . . . . . . . . . . . . . . . . . . . . .  129
      9.3 Signal procedures  . . . . . . . . . . . . . . . . . . . . .  130

                                          2





    10 Description of the standard modules . . . . . . . . . . . . . .  131
      10.1 The System modules  . . . . . . . . . . . . . . . . . . . .  131
        10.1.1 SystemCode  . . . . . . . . . . . . . . . . . . . . . .  131
        10.1.2 System  . . . . . . . . . . . . . . . . . . . . . . . .  133
      10.2 The graphics modules  . . . . . . . . . . . . . . . . . . .  135
        10.2.1 Graphics  . . . . . . . . . . . . . . . . . . . . . . .  135
        10.2.2 Turtle  . . . . . . . . . . . . . . . . . . . . . . . .  145
      10.3 Catalog . . . . . . . . . . . . . . . . . . . . . . . . . .  149
      10.4 Bob and sprite modules  . . . . . . . . . . . . . . . . . .  150
      10.5 Memory  . . . . . . . . . . . . . . . . . . . . . . . . . .  152
      10.6 CodeManInclude and InterpreterInclude . . . . . . . . . . .  153
      10.7 StartProgram  . . . . . . . . . . . . . . . . . . . . . . .  153
      10.9 Timer . . . . . . . . . . . . . . . . . . . . . . . . . . .  153
      10.10 SerialComm . . . . . . . . . . . . . . . . . . . . . . . .  153
      10.11 Operating system modules . . . . . . . . . . . . . . . . .  155
        10.11.1 Library interface routines . . . . . . . . . . . . . .  155
        10.11.2 Definitions for use in the library modules . . . . . .  156
    11 The Comal Intuition Tools (CIT) . . . . . . . . . . . . . . . .  161
      11.1 General description of CIT  . . . . . . . . . . . . . . . .  161
      11.2 Simple CIT examples . . . . . . . . . . . . . . . . . . . .  164
      11.3 CIT reference . . . . . . . . . . . . . . . . . . . . . . .  169
        11.3.1 CITWorkbench  . . . . . . . . . . . . . . . . . . . . .  169
        11.3.2 CITScreen . . . . . . . . . . . . . . . . . . . . . . .  169
        11.3.3 CITWindow . . . . . . . . . . . . . . . . . . . . . . .  171
        11.3.4 CITBorder . . . . . . . . . . . . . . . . . . . . . . .  175
        11.3.5 CITView . . . . . . . . . . . . . . . . . . . . . . . .  176
        11.3.6 CITGadgets  . . . . . . . . . . . . . . . . . . . . . .  176
          11.3.6.1 TextGadget  . . . . . . . . . . . . . . . . . . . .  178
          11.3.6.2 ButtonGadget  . . . . . . . . . . . . . . . . . . .  179
          11.3.6.3 CheckboxGadget  . . . . . . . . . . . . . . . . . .  179
          11.3.6.4 StringGadget  . . . . . . . . . . . . . . . . . . .  179
          11.3.6.5 IntegerGadget . . . . . . . . . . . . . . . . . . .  180
          11.3.6.6 SliderGadget  . . . . . . . . . . . . . . . . . . .  181
          11.3.6.7 ScrollerGadget  . . . . . . . . . . . . . . . . . .  182
          11.3.6.8 CycleGadget . . . . . . . . . . . . . . . . . . . .  183
          11.3.6.9 RadioButtonGadget . . . . . . . . . . . . . . . . .  184
          11.3.6.10 ListViewGadget . . . . . . . . . . . . . . . . . .  185
          11.3.6.11 PaletteGadget  . . . . . . . . . . . . . . . . . .  187
        11.3.7 CITText . . . . . . . . . . . . . . . . . . . . . . . .  188
        11.3.8 CITGraphics . . . . . . . . . . . . . . . . . . . . . .  189
        11.3.9 CITMenus  . . . . . . . . . . . . . . . . . . . . . . .  190
        11.3.10 CITRequester . . . . . . . . . . . . . . . . . . . . .  194
      11.4 Creating your own CIT classes . . . . . . . . . . . . . . .  194

  V. MAKING EXECUTABLE PROGRAMS  . . . . . . . . . . . . . . . . . . .  199
    1 Comal code file  . . . . . . . . . . . . . . . . . . . . . . . .  199
    2 Combined files . . . . . . . . . . . . . . . . . . . . . . . . .  199
      2.1 Starting the Combiner from the editor  . . . . . . . . . . .  200
      2.2 Starting the Combiner from Workbench . . . . . . . . . . . .  200
      2.3 Starting the Combiner from the Shell (CLI) . . . . . . . . .  202

                                          3





    3 Using ToolTypes in executable files  . . . . . . . . . . . . . .  202

  VI. DESCRIPTION OF THE Comal SYSTEM  . . . . . . . . . . . . . . . .  204
    1 The code manipulator Comal.CodeMan . . . . . . . . . . . . . . .  204
    2 The interpreter Comal.Interpreter  . . . . . . . . . . . . . . .  211
    3 The starter program Comal.Starter  . . . . . . . . . . . . . . .  217

  VII. THE Comal AREXX INTERFACE . . . . . . . . . . . . . . . . . . .  218
    1 The editor AREXX interface . . . . . . . . . . . . . . . . . . .  218
    2 The CodeMan AREXX interface  . . . . . . . . . . . . . . . . . .  225
    3 The interpreter AREXX interface  . . . . . . . . . . . . . . . .  229
    4 An AREXX script example  . . . . . . . . . . . . . . . . . . . .  230

  VIII. COMAL IO DEVICES . . . . . . . . . . . . . . . . . . . . . . .  232
    1 What is a Comal device?  . . . . . . . . . . . . . . . . . . . .  232
    2 Making new devices . . . . . . . . . . . . . . . . . . . . . . .  232
    3 Making your own IO window  . . . . . . . . . . . . . . . . . . .  241

  IX. MASHINE CODED MODULES  . . . . . . . . . . . . . . . . . . . . .  243
    1 The format of a mashine coded module . . . . . . . . . . . . . .  243
      1.1 The interface part . . . . . . . . . . . . . . . . . . . . .  243
      1.2 The initialization and signal routines . . . . . . . . . . .  244
      1.3 The routine part . . . . . . . . . . . . . . . . . . . . . .  245
    2 An assembler programmed module . . . . . . . . . . . . . . . . .  245
    3 Programming modules in C . . . . . . . . . . . . . . . . . . . .  250
      3.1 The interface part . . . . . . . . . . . . . . . . . . . . .  250
      3.2 The procedures and functions . . . . . . . . . . . . . . . .  251
      3.3 Linking the object files . . . . . . . . . . . . . . . . . .  252
      3.4 An initialization routine  . . . . . . . . . . . . . . . . .  252
      3.5 A signal routine . . . . . . . . . . . . . . . . . . . . . .  253
      3.4 Using the script file MakeMod  . . . . . . . . . . . . . . .  253
      3.5 The interface compiler CompInterface . . . . . . . . . . . .  254
    4 Calling internal Comal routines from modules . . . . . . . . . .  254
    5 Calling comal programmed routines from a module  . . . . . . . .  257
    6 Making a library interface . . . . . . . . . . . . . . . . . . .  258

  X. APPENDIX  . . . . . . . . . . . . . . . . . . . . . . . . . . . .  261
    A. Customizing the Comal text  . . . . . . . . . . . . . . . . . .  261
    B. The format of the AREXX macro file  . . . . . . . . . . . . . .  262
    C. Customizing Comal.lib . . . . . . . . . . . . . . . . . . . . .  263











                                          4





  I. GETTING STARTED


  1 Hardware requierements.

  The Comal  programming system  will run  on any  Amiga computer having at
  least 1 megabyte of main memory and one diskette drive.

  Although one disk drive will suffice an extra disk drive (or a hard disk) is
  recommended.



  2 Software requirements.

  Comal requires Workbench 1.3 or higher to run.

  To be able to use the Comal Intuition Tool (CIT) Workbench 2.04 or higher
  is needed.



  3 Creating a backup disk.

  Comal is supplied on two 3.5" floppy disks named Comal  and Comal.Extras.
  Before installing Comal, make a backup copy of the original disks and store
  these in a safe place.

  Before doing this make sure that the disks are write protected (you should
  be able to look through the small hole in the plastic disk cover).

  If your Amiga has only one disk drive the backup is done in this way:

  1.  Put the write protected Comal (Comal.Extras) disk into a disk drive.

  2.  Select the Comal (Comal.Extras) disk by clicking once on its icon with
      the left mouse button.

  3.  Choose the Copy item from the Icons menu (in WB1.3 the duplicate item
      from the  Workbench menu)  and follow the instructions in the reques-
      ters.

  4.  After the copy has been made, choose the  Rename item  from the Icons
      menu (the  Workbench menu  in WB1.3)  and remove  the "copy_of_" from
      the name.

  If your Amiga has two disk drives the backup is done in this way:

  1.  Put the write protected Comal (Comal.Extras) disk into one disk drive.


                                          5





  2.  Write enable a blank floppy disk and put it into the other disk drive.

  3.  Select the Comal (Comal.Extras) disk by clicking once on its icon with
      the left mouse button and drag it on top of the icon of the blank disk.

  4.  After the copy, remove the original Comal (Comal.Extras) disk from the
      disk drive, select the "copy_of_Comal" ("copy_of_Comal.Extras") disk by
      clicking on its icon with the left mouse button and choose the Rename
      item from the Icons  menu (the  Workbench menu  in WB1.3)  and remove
      the "copy_of_" from the name.



  4 Installing Comal on a floppy disk system.

  The disk  named Comal  is ready  to use. Put the copy of this disk into a
  disk drive and open the disk by double clicking on its icon.



  5 Installing Comal on a hard disk system.

  Put the copy of your Comal.Extras disk into a disk drive and open the disk
  by double clicking on its icon.

  Make an  empty drawer  on your  hard disk by using the New drawer item in
  the Window menu (use the empty drawer in WB1.3) and  give the  drawer the
  name Comal (or another name). Select InstallHD by clicking on its icon with
  the left mouse button. Press the shift key and double click on the icon of
  the newly created drawer.

  As an alternative to creating a new drawer your self you may just start the
  InstallHD program by double clicking on its icon. Then you will be promp-
  ted for  the name  of the destination drawer (that will be created if not
  found).

  During the installation you will be asked to put in the other disk and you
  will be asked if the ARexx demo scripts should be copied to the Rexx: di-
  rectory (if ARexx is installed).

  The InstallHD program does not do anything else than copying needed files
  to the selected drawer and (if you accepted) ARexx scripts to Rexx:.



  6 Starting Comal from Workbench.

  Comal may  be started  from Workbench  in the  normal Amiga way by double
  clicking on the Comal icon.


                                          6





  If you are using a one drive Amiga system you will be asked to change disk
  once during the loading (Comal has to load some disk based libraries). Nor-
  mally this is only the first time you are starting Comal. The next time the
  libraries are already loaded.

  In stead of double clicking on the Comal icon you may click on the icon of
  a Comal program text (an ASCII text containing a Comal program). By doing
  this Comal will be started and the program will be loaded into the editor.



  7 Starting Comal from the Shell (the CLI).

  Comal may  be started  from the  Shell by executing a command of the for-
  mat:

    Comal [startup parameters] [Program]

  where Program is the name of a Comal  program eventually  preceded by the
  path to the directory containing the program and the optional startup para-
  meters are of the same form as the tool types (se section 8.1).

  If you are specifying a program name, Comal will be started and  the pro-
  gram will be loaded. The current directory will be the directory containing
  the program.

  Example:

    Comal CITDemos/TinyPaint      (Start Comal and load CITDemos/TinyPaint)

    Comal WORKSPACE=100000        (Start Comal with 100000 Kb workspace)



  8 Setting the Comal environment.

  Several parameters used by the Comal  programming environment  may be set
  either by  using the  standard Workbench  info requester  or by using the
  Prefs program found in the Prefs drawer.


  8.1 Setting the Comal TOOLTYPES.
   
  The info requester can be displayed by selecting the Comal  icon and then
  select the  Information... item  from the Icons menu in Workbench (WB1.3:
  select the Info item in the Workbench menu).

  The field Tool Types inside the Info requester is where  you can  set the
  Comal parameters.  Each of the parameter are set by using one line of the
  format:

                                          7






    TOOLTYPE=ToolValue


  The TOOLTYPE's and the ToolValue's you can use are described below. 


    SCREEN

      By using the SCREEN tool type you select if Comal should open its own
      screen or  use the workbench screen. The possible tool values are the
      name of a public screen to be used (if the screen is not found it will
      be created).

      Example:  SCREEN=Comal

      The only legal value for WB1.3 users is Workbench.

      The default value is a private screen.


    DEPTH

      By using  the DEPTH  tool type  you select  depth of  the screen. The
      default value is 2.

      Example:  DEPTH=4


    LANGUAGE

      By using this tool type you may select the language of the texts used
      in the Comal system.

      Example:  LANGUAGE=dansk

      The default language is English.


    WINDOW

      By using this tool type you select the position and size of the editor
      window at start. The format is

        WINDOW=LeftEdge,TopEdge,Width,Height

      Example:  WINDOW=95,30,540,200




                                          8





    ENTERMODE

      Selects the effect of the enter key. The tool values are Down and In-
      sert. If  you choose Insert the cursor is moved down and a new, empty
      line is inserted each time the  ENTER key  is pressed.  If you choose
      Down the  cursor is  moved down  but no new line is inserted. The de-
      fault value is Insert.

      Example:  ENTERMODE=Down

      The behavior of the ENTER key  may  at  any  time  be  changed inside
      Comal.


    KEYWORD

      Selects the letters used to list keywords. The tool values are Capital
      and Small. The default value is Capital.

      Example:  KEYWORD=Small

      This parameter may at any time be changed inside Comal.


    BACKUP

      Selects the backup option. The tool values are  On and  Off. If  on a
      backup file is created each time a program text is stored on disk. The
      default value is On.

      Example:  BACKUP=Off

      This parameter may at any time be changed inside Comal.


    ICON

      Selects the icon option. The tool values are On and Off. If on an icon
      is created when a program is stored on disk. If the program is stored
      as a text file, this icon can be used to start Comal and load the pro-
      gram. If it is stored as a code file, the icon can be used to start exe-
      cution of the program (without loading the editor). The default value is
      On.

      Example:  ICON=Off

      This parameter may at any time be changed inside Comal.




                                          9





    LINEEND

      Selects the lines are terminated in text files. The tool values are LF,
      CR, CR+LF and PC. DThe default value is LF (standard Amiga).

      Example:  LINEEND=CR+LF

      This parameter may at any time be changed inside Comal.


    AUTOVAR

      This tool type is used to select if a running program should create va-
      riables automatically or if all variables has to be declared in a DIM or
      LOCAL statement before use. The default value is On.

      Example:  AUTOVAR=Off

      This parameter may at any time be changed inside Comal.


    EXECWINDOW

      This tool type is used to select if a running program should open the
      standard  execute  window  (used  by  PRINT, INPUT etc.). The default
      value is On.

      Example:  EXECWINDOW=Off

      This parameter may at any time be changed inside Comal.


    PROGRAM

      Selects the size of the program  buffer. The  default value  is 25000
      bytes.

      Example:  PROGRAM=20000



    WORKSPACE

      Selects the  size of the workspace used by a running program. The de-
      fault value is 75000 bytes.

      Example:  WORKSPACE=60000




                                         10





    STACK

      Selects the size of the stack for a running program. The default value
      is 8Kb.

      Example:  STACK=16000


    MACRO

      By using this tool type you select the name of the file that contains
      the macro definitions that will be assigned to the function keys. The
      default file is Comal.Macro.

      Example:  MACRO=Comal:MyMacro



  The tool  types (except  the SCREEN  and DEPTH tool types) can be used in
  icons for program texts. If this is done, only the parameters for that pro-
  ject is affected. If another project is started (see section 3.1) the standard
  parameters or the values set in the Comal icon is used.

  The parameter values put into the  Comal icon  is read  even if  Comal is
  started from the Shell.



  8.2 The Preferences program Pref.

  Most of  the parameters  that can  be set  by using  the TOOLTYPES in the
  Comal info requester can be set in a  much more  comfortable way  by run-
  ning the program Prefs in the Prefs drawer.

  NOTE: Workbench 1.3 users can only set the language in this way.
















                                         11





  II. TUTORIAL

  This chapter is intented to help beginners through the first difficult steps
  in making their first programs. You will learn to make simple programs, to
  use some of the menus in Comal and to debug your program.

  Skilled programmers may skip this chapter or just skim it.


  1 Your first Comal programs.

  Having started  Comal by  clicking on its icon you will se a window - the
  edit window. This window is used to type in your programs.

  Start by entering this program consisting of only one line:

    print "Hello there!"

  and press <ENTER>. Nothing happens except that the word print  is rewrit-
  ten in capital.

  Now, let's  try to execute it. Press the mouse menu button and select the
  Execute item in the Program menu  (or use  the alternative  short form of
  this  menu  selection:  <right  Amiga>+<E>).  As a result a new window is
  opened (the output window) and the text Hello  there! is  printed in this
  window. After that the edit window is brought to front again.

  Let's remove  this program  and make  a little longer program. Select the
  Clear Program Buffer menu item from the Project menu. As  a result  a re-
  quester will  pop up.  This is  because the program you just made has not
  been saved on disk. You will probably not want to save this little program,
  so just click on Yes. The edit window will now be cleared and the execute
  window will disappear.

  Enter this program:

    INPUT AT 10,5: "Type in your name: ": Name$

    PAGE  // Erase window

    PRINT AT 10,20: "Hello ",Name$,"!"
    PRINT
    PRINT AT 12,20: "Welcome to Comal."



  Remember to press <ENTER> after each line. Be careful to type in excactly
  the same  as shown.  If you do not the Comal system will probably ask you
  to correct the line.


                                         12





  Let's execute this program. Select the Execute menu item from the Program
  menu (or  use the short form: <right Amiga>+<E>). As a result the execute
  window will again be opened and you are  asked to  type in  your name. Do
  that and  terminate by pressing <ENTER>. The window will be cleared and a
  text will be printed.

  If you did not typed excactly  as shown  the program  may stop,  an error
  message will be printed in the status line of the edit window and the cur-
  sor will be positioned on (or just after) the place where the error was de-
  tected. Correct the error and execute the program again.

  If the program terminates without error you will notice that the edit win-
  dow will be brought to front, so that you cannot see the text. This is bad.

  Use the cursor keys or the mouse to  move the  cursor to  the end  of the
  program and add this line:

    WAIT 3  // Wait 3 secs


  Run the  program again  (<right Amiga>+<E>).  The program waits 3 seconds
  before it stops. Then you have the time to see the output.



  2 Working with graphics.

  In this and the following sections we will make programs that draws small
  figures in  the window. But before we continue, you should clear the pro-
  gram buffer. Use the the Clear  Program Buffer  menu item  in the Project
  menu.

  Comal is an interactive programming language. This means that you can ex-
  ecute commands immediately without putting them  into a  program. We will
  use this facility to show you how some of the drawing routines works.

  Select the  Open Command  Window item  from the  Project menu (or use the
  short form: <right Amiga>+<K>). As a result a little window  (the command
  window) will appear in the top left corner of your screen. In this window
  some of the Comal  statements may  be executed  as commands.  Try for in-
  stance to write this line:

    PRINT 2+3

  and press  <ENTER>. The  expression will be calculated and the result (5)
  will be printed at once.

  To be able to use the graphics routine you have to link one of the graphics
  modules Graphics and Turtle into the system. We will choose Turtle.


                                         13





  The linking is done by executing the line:

    USE Turtle


  Type  this  line  into  the  command  window  and  terminate  by pressing
  <ENTER>. Comal reads the module from  disk  and  makes  all  the routines
  within this module accessible.

  Before you  can make drawings, you have to open the graphics system. This
  is done by writing:

    graphicscreen(0)

  (remember to press <ENTER>).

  If you have typed in commands as above the execute window will  be opened
  and a small triangle (a turtle) will be drawn in the middle of the window.

  Now we are ready to use the graphics routines in the Turtle module. Let's
  move the turtle forward. This is done by writing the line

    forward(50)

  (remember <ENTER>). The result is that the turtle is moved  50 units for-
  ward and a line is drawn behind it.

  The turtle may be turned in any direction. This is done by writing the line

    right(90)

  which turns the turtle 90o to the right.

  If you repeat these two commands three more times (each repetition is most
  easily done by moving the cursor up two  lines and  then pressing <ENTER>
  twice) a nice square will be drawn.

  In the Turtle module there are a lot of different graphics routines. Read
  more about these in the description of the graphics modules.

  Before we leave this section you should close the graphics system by wri-
  ting the line:

    textscreen

  and then  close the command window by clicking on its close gadget in the
  upper left corner of the window.




                                         14





  3 Repetition.

  The drawing routines used in the preceeding section  may be  used as pro-
  gram statements as well. Here is a program that draws the square from that
  section:


    // Program 3.1

    USE Turtle

    graphicscreen(0)  // Open the graphics system

    forward(50)       // Draw a square
    right(90)
    forward(50)
    right(90)
    forward(50)
    right(90)
    forward(50)
    right(90)

    WAIT 4            // Time to look at the drawing

    textscreen        // Close the graphics system


  You may type in the program in the editor or load it from disk where it is
  stored. Select the Open... menu item in the Project menu (or use the short
  form: <right Amiga>+<O>). A file requester will open.

  Click on the scroll bar in the right side of the requester and move it to
  the bottom.  Then click on the name Tutorial which will be written in the
  drawer field of the requester (alternatively you may just write  the name
  Tutorial in this field). Now select the file Prg3.1 and then press the OK
  gadget. As a result the program will be loaded.

  Try to execute the  program (<right  Amiga>+<E>). It  should draw  a nice
  square in  the execute  window. The program will wait four seconds before
  the edit window will be brought to front.

  In the program, the two statements:

    forward(50)
    right(90)

  are repeated four times. The program can be  made more  elegant by making
  the Comal system automatically repeat the two statements. This can be done
  by using the LOOP statement:


                                         15





    LOOP 4 TIMES
        :
      statements to be repeated
        :
    ENDLOOP


  Let's try to modify the program. Move the cursor to line number 9 (use the
  cursor keys  or the  mouse). Select the Mark Block Start menu item in the
  Edit menu (or short: <right Amiga>+<B>). The editor will change  to block
  mode (which is written in the status line) and the cursor line (line 9) is
  written in blue (red in WB 1.3). Move the cursor  down to  line number 14
  using the  cursor key  (the mouse  cannot be used in block mode). As seen
  the lines 9-14 are marked as a block.

  Now delete this block by selecting the Erase menu item. As a result a re-
  quester will pop up and you are requested to confirm. Press the Yes gadget
  to accept the operation.

  Move the cursor three lines up (to line 6) and press <ENTER> to insert an
  extra line. Write the line

    LOOP 4 TIMES

  and don't  press <ENTER>. If you did press <ENTER> an extra line has been
  inserted. Delete this by pressing <shift>+<Del>. Move the cursor down three
  lines (line 10 just after right(90)). Note that the lines are indented while
  you are moving down.

  Write the line

    ENDLOOP

  and press <ENTER> to insert an extra line.

  May be you should change the program header line (line 1). Move  the cur-
  sor to  the end of this line and delete the last 1 by pressing back space
  (the key on the left side of the Del key  above the  <ENTER> key). Change
  the line to:

    // Program 3.2


  The changed program should now look like:







                                         16





    // Program 3.2

    USE Turtle

    graphicscreen(0)  // Open the graphics system

    LOOP 4 TIMES
      forward(50)     // Draw a square
      right(90)
    ENDLOOP

    WAIT 4            // Time to look at the drawing

    textscreen        // Close the graphics system


  Try to execute the program to make sure that it works just as before.

  Save the  program on the RAM: disk (just as an exercise). Select the Save
  As... menu item in the Project menu (short form: <right  Amiga>+<A>). The
  file requester will open. Select Disks  and RAM: and write the name Prg3.2
  in the File text field of the requester. Then save by clicking  on the OK
  gadget (or by pressing <ENTER>).

  By using  the LOOP  statement it  is easy  to make the program draw other
  figures. Change the LOOP section to

    LOOP 3 TIMES
      forward(50)
      right(120)
    ENDLOOP

  to draw a triangle, or to

    LOOP 6 TIMES
      forward(50)
      right(60)
    ENDLOOP

  to draw a hexagon. Or try this funny little figure by changing it to

    LOOP 20 TIMES
      forward(50)
      right(168)
    ENDLOOP


  Comal contains a number of  other  repetition  statements:  WHILE, REPEAT
  and FOR statements. Se chapter IV for a complete description.


                                         17






  4 Selection.

  In section 3 we learned to draw triangles, squares etc. In this chapter we
  will make a program that can draw any of these  figures dependent  of the
  users choice.

  A skeleton for such a program would be:

    Program figures
      initialize
      print the possible choices
      get users choice
      draw the chosen figure


  The initialization is simply:

    graphicscreen(0)


  The printing section could be made in this way:

    PRINT AT 6,10: "Triangle ........ 3"
    PRINT AT 8,10: "Square .......... 4"
    PRINT AT 10,10: "Pentagon ........ 5"
    PRINT AT 12,10: "Hexagon ......... 6"


  The users choice is made by a single INPUT statement:

    INPUT AT 16,10: "Select figure: ": choice


  To draw  the correct figure we have to use of one of the selection state-
  ments in Comal. Here we will use the IF statement:

    IF choice=3 THEN
      draw triangle
    ELIF choice=4 THEN
      draw square
    ELIF choice=5 THEN
      draw pentagon
    ELIF choice=6 THEN
      draw hexagon
    ELSE
      print error
    ENDIF



                                         18





  The complete program looks like:

    // Program 4.1

    USE Turtle

    graphicscreen(0)
    ht

    PRINT AT 6,10: "Triangle ......... 3"
    PRINT AT 8,10: "Square ........... 4"
    PRINT AT 10,10: "Pentagon ......... 5"
    PRINT AT 12,10: "Hexagon .......... 6"

    INPUT AT 16,10: "Select figure: ": choice

    page

    IF choice=3 THEN    // Triangle
      LOOP 3 TIMES
        forward(50)
        right(120)
      ENDLOOP
    ELIF choice=4 THEN  // Square
      LOOP 4 TIMES
        forward(50)
        right(90)
      ENDLOOP
    ELIF choice=5 THEN  // Pentagon
      LOOP 5 TIMES
        forward(50)
        right(72)
      ENDLOOP
    ELIF choice=6 THEN  // Hexagon
      LOOP 6 TIMES
        forward(50)
        right(60)
      ENDLOOP
    ELSE
      PRINT AT 12,10: "I don't know that figure!"
    ENDIF

    WAIT 4
    textscreen


  You may read this program from the disk (<right Amiga>+<O>).  The name of
  the program is Prg4.1). Execute it. Remember that a selection of a number
  must be terminated by pressing <ENTER>.


                                         19






  5 Procedures.

  The program in section 4 would  be more  readable if  we could  write the
  selection part in this way:

    IF choice=3 THEN
      triangle
    ELIF choice=4 THEN
      square
    ELIF choice=5 THEN
      pentagon
    ELIF choice=6 THEN
      hexagon
    ELS
      PRINT AT 12,10: "I don't know that figure!"
    ENDIF


  To do  this we have to define procedures for each of the figures in ques-
  tion. The square procedure for instance would be:

    PROC square
      LOOP 4 TIMES
        forward(50)
        right(90)
      ENDLOOP
    ENDPROC square


  Let's try to change the program in this way.

  Load the program Prg4.1 into the editor (if it is not already  there) and
  move the  cursor to  line 17  (the first LOOP statement). Select the Mark
  Block Start menu item in the Edit menu (or short:  <right Amiga>+<B>) and
  move  the  cursor  three  lines  down  (so  that  the  whole LOOP-ENDLOOP
  structure is marked as a block).

  Cut the block off the program and move it into the clip board by selecting
  the Cut  menu item  in the Edit menu (short: <right Amiga>+<X>). Move one
  line up and press <ENTER> to make an empty line and write triangle in this
  line (this line works as a replacement for the removed block).

  Move to  the end  of the program by pressing <Ctrl>+<down arrow>, make an
  extra empty line by pressing <ENTER> and write

    PROC triangle

  and press <ENTER> to move to the next line.


                                         20





  Insert the block from the clip board by selecting the Paste menu  item in
  the  Edit  menu  (short:  <right  Amiga>+<V>)  and move to the line after
  ENDLOOP and write:

    ENDPROC triangle

  Make similar changes for the other figures. The result should look like:

    // Program 5.1

    USE Turtle

    graphicscreen(0)
    ht

    PRINT AT 6,10: "Triangle ......... 3"
    PRINT AT 8,10: "Square ........... 4"
    PRINT AT 10,10: "Pentagon ......... 5"
    PRINT AT 12,10: "Hexagon .......... 6"
    INPUT AT 16,10: "Select figure: ": choice

    PAGE
    IF choice=3 THEN    // Triangle
      triangle
    ELIF choice=4 THEN  // Square
      square
    ELIF choice=5 THEN  // Pentagon
      pentagon
    ELIF choice=6 THEN  // Hexagon
      hexagon
    ELSE
      PRINT AT 12,10: "I don't know that figure!"
    ENDIF

    WAIT 4
    textscreen

    // ********* end of main program **************

    PROC triangle
      LOOP 3 TIMES
        forward(50)
        right(120)
      ENDLOOP
    ENDPROC triangle

    PROC square
      LOOP 4 TIMES
        forward(50)
        right(90)

                                         21





      ENDLOOP
    ENDPROC square

    PROC pentagon
      LOOP 5 TIMES
        forward(50)
        right(72)
      ENDLOOP
    ENDPROC pentagon

    PROC hexagon
      LOOP 6 TIMES
        forward(50)
        right(60)
      ENDLOOP
    ENDPROC hexagon


  The program is not shorter neither is it faster, but it is much more read-
  able, since  you only need to read the first 30 lines to see what it does
  (provided you have used descriptive names).

  The four procedures at  the end  of the  program contain  almost the same
  code. We could use this fact to make the program shorter. A general Poly-
  gon procedure could be made in this way:

    PROC Polygon(n)
      LOOP n TIMES
        forward(50)
        right(360/n)
      ENDLOOP
    ENDPROC Polygon


  By using this, the four procedures should be changed to (note that the new
  procedure must  be written  Polygon and  not polygon or you will conflict
  with one of the procedures in the Turtle module):

    PROC triangle
      Polygon(3)
    ENDPROC triangle

    PROC square
      Polygon(4)
    ENDPROC square

    PROC pentagon
      Polygon(5)
    ENDPROC pentagon


                                         22





    PROC hexagon
      Polygon(6)
    ENDPROC hexagon


  Try to make these changes as an exercise. The changed version is stored on
  disk under the name Prg5.2. Execute the program to see if it still does the
  same.

  One of the advantages  of using  procedures is  that the  program is more
  readable. But it is also much easier to make changes in a program that uses
  procedures. Try for instance to type in the following little procedure at the
  end of your program:

    PROC Fwrd(length)
      IF length<10 THEN
        forward(length)
      ELSE
        Fwrd(length/3)
        left(60)
        Fwrd(length/3)
        right(120)
        Fwrd(length/3)
        left(60)
        Fwrd(length/3)
      ENDIF
    ENDPROC Fwrd

  and replace the line forward(50) in the polygon procedure (line 51 in the
  program Prg5.2) with the line

    Fwrd(50)

  Execute the program to see the effect of the changes. It is very surprising!


  6 Making modules.

  As you saw in the last section it is a good idea to hide "complicated" code
  in procedures  and put these procedures in a separate section of the pro-
  gram. Another good reason for using procedures is that the  same piece of
  code may  be reused.  The Polygon procedure is a good example of this (it
  was used four times).

  The idea of hiding and reusing code may be developped  further by putting
  the procedures into a module and store this module on disk. This is in fact
  what has been done in the Turtle module. In this section it will be shown
  how such  a module is made. As an example we will put the four procedures
  triangle, square, pentagon and hexagon into a module called Figures so that
  they can be used in other programs.

                                         23






  First you  have to  load the program Prg5.2 into the editor (<rightAmiga>
  +<O>). Then go to the line  before the  first procedure  (line 32). Press
  <ENTER> to insert an extra line and write the following in this empty line:

    MODULE Figures


  Go to  the end  of the program (note that the lines are being indented as
  you are moving down), insert an extra line and write:

    ENDMODULE Figures


  Now you have made an internal  module which  can be  tested before  it is
  stored on disk. But the module is not ready for test. You have to USE the
  Turtle module inside this module and you have to tell  which ones  of the
  procedures should be made accessible from outside. This is done in special
  EXPORT statements. The final module should look like this:

    MODULE Figures

      USE Turtle

      EXPORT triangle,square,pentagon,hexagon

      PROC triangle
        Polygon(3)
      ENDPROC triangle

      PROC square
        Polygon(4)
      ENDPROC square

      PROC pentagon
        Polygon(5)
      ENDPROC pentagon

      PROC hexagon
        Polygon(6)
      ENDPROC hexagon

      PROC Polygon(n)
        LOOP n TIMES
          forward(50)
          right(360/n)
        ENDLOOP
      ENDPROC Polygon

    ENDMODULE Figures

                                         24







  Put the USE statement and the  EXPORT statement  into the  module so that
  it looks as above.

  Now let's try to execute the program. Press <right Amiga>+<E> and select 3
  (triangle) and press <ENTER>.

  What happened? An error occured! The cursor is placed after the word tri-
  angle and  an error message in the status line tells you that the name is
  unknown. This is because you have not told the main program to import the
  procedures from the module. You have to add the line

    USE Figures

  in the start of the program (for instance just after the line: USE Turtle).
  The start of the main program should look like:

    // Program 6.1

    USE Turtle
    USE Figures

    graphicscreen(0)


  Having added the USE line to your program, you are ready to  test the mo-
  dule once more. This time it should be okay. The program is stored on disk
  under the name Prg6.1

  The next step in making a module is to store it on disk.

  First you have cut the module off the program and  move it  into the clip
  board. Go  to the  MODULE line  (line 34  of Prg6.1) and enter block mode
  (<right Amiga>+<B>). Move  to  the  ENDMODULE  line  (line  63).  Now the
  whole module is marked as a block. Cut it off the program (<right Amiga>+
  <X>).

  As the next step  you have  to open  a new  project. Select  the New menu
  item in  the Project  menu (<right  Amiga>+<N>). As a result a new editor
  window will be opened. It is named Project 2. In fact you  have started a
  new process  in the  Amiga. In  this new project you can edit and execute
  programs independent of what you are doing in  the original  project. And
  you can even run programs in the two projects in parallel. But we are not
  going to do that now.

  The clip board we have used is the standard Amiga clip board  so that you
  can get  the content  from the  newly started project (and other programs
  that uses this clip board). Select the Paste menu item  in the  Edit menu
  (<right Amiga>+<V>) and the module will be copied into your editor.

                                         25






  And now we have to store it on disk. But in contradiction to what we have
  done earlier, where we have stored programs on disk as  simple text files
  (ready to be loaded by any text editor like for instance MEmacs or ED), we
  have to store it in a special code form.

  Select the Save... menu item in the Program menu (no short form - sorry!).
  As a  result the well known file requester will open. Activate the Drawer
  text field by clicking on it with your mouse (the left mouse  button) and
  write

    Comal:Modules


  Then write the following name into the File text field:

    Figures.mod

  Be careful to write it correct! Press <ENTER> (or click on the OK gadget)
  to store the module.

  That's all. Close Project 2 by clicking on the close gadget and start your
  program in Project 1. You will notice that it starts reading the module from
  disk. But otherwise the program works as before.

  Store your program on disk in the drawer  Tutorial under  the name Prg6.2
  (<right Amiga>+<A>).


  7 Looking into modules.

  If you  are using externale modules stored on disk it may happen that you
  want to see the content of a specific module. If there is a written docu-
  mentation for  that module,  you may find the answer to your questions in
  this. But if you do not have such a documentation there is another possibi-
  lity.

  Load the program Prg6.2 (if it is not already in the editor) and execute it
  so that modules are loaded. Select the Show  Modules... menu  item in the
  Program menu (short: <right Amiga>+<H>). As a result a file requester like
  window will pop up showing you all modules loaded so far.

  You may wonder! Five modules are  shown  but  you  have  only  loaded the
  Turtle module and the Figures module. The explanation is easy. The Turtle
  module itself uses the Graphics module and the System module which itself
  uses the SystemCode module.

  Select the Figures module either by pressing <ENTER> or by clicking on the
  Accept gadget. As a result your Figures module is shown in the editor win-
  dow. The  status line  tells you that you are in module mode and what the

                                         26





  name of the module is. In  module mode  you can  move around  in the text
  but you cannot edit the text and a lot of functions are disabled.

  To go  back to  the main  program select  the Edit Main Program menu item
  in the Edit menu (short: <right Amiga>+<M>).

  Let's see the loaded modules again (<right Amiga>+<H>).

  Modules may be written  in Comal  or they  may be  mashine coded modules.
  The Figures,  Turtle and  System modules are Comal modules while Graphics
  and SystemCode are mashine coded modules. Select the Graphics module (for
  instance by double clicking on the name).

  This time  a new window is opened. The source of the module is not shown,
  but an interface containing all the  procedures and  functions are shown.
  Move around  in the  text by using the cursor keys or by using the scroll
  bars. Click on the close gadget in the top left  corner of  the window to
  remove the window.

  Try to look into the other modules, for instance the Turtle module. It is a
  large and rather complicated module that will convince you of  the advan-
  tage of hiding code in modules.


  8 Making gadgets.

  The programs  in sections  4-6 used  the INPUT  statement to  get a users
  choice. This is somewhat old fashioned. A better and more  Amiga like me-
  thod is  to use gadgets so that the user can press on the relevant gadget
  by using the mouse.

  By using the CIT-modules (CIT is short for Comal Intuition Tool) it is rela-
  tively easy  to make  programs with  gadgets. NOTE!  CIT can only be used
  with WB2.04 or higher.

  Load the program Prg6.1 or Prg6.2 (if not already in the editor). First add
  two USE  lines specifying the CIT-modules we are going to use: CITWindows
  and CITGadgets. The start of the program should look like this:

    // Program 6.1

    USE CITWindow
    USE CITGadgets
    USE Turtle
    USE Figures

    graphicscreen(0)
    ht



                                         27





  First we'll make a gadget for the triangle. This is done by executing a DIM
  statement of the form:

    DIM TriangleGad OF ButtonGadget


  The gadget  is a simple ButtonGadget. By using CIT a lot of other gadgets
  can be made, but in this section only button gadgets will be used.

  We have to tell CIT what the size of the gadget should  be and  where the
  gadget is to be placed. This is done in the following statements:

    TriangleGad.Size(120,20)    // Width = 120 and height = 20 pixels
    TriangleGad.Position(0,0)   // The upper left corner of the window


  The point  between TriangleGad  and Size/Position indicates that Size and
  Position are in fact methods in a data structure. Read chapter IV.8 if you
  want to  know more  about methods  and objects.  But here you do not need
  think more about that (just write as shown).

  Inside the gadget we want the label Triangle. This is specified in this way:

    TriangleGad.Label("Triangle",INSIDE)


  Now we have specified the layout and the position of the gadget. It is in-
  serted in the standard Comal window in this way:

    ComalWindow.InsObject(TriangleGad,Error)


  As a result of executing this statement the gadget will be visible in the
  window. If an error occurs an error message  is returned  in the variable
  Error. We will not test for errors until all gadgets are made.

  The gadgets for the other figures are made in the same way:

    DIM SquareGad OF ButtonGadget
    SquareGad.Size(120,20)
    SquareGad.Position(120,0)
    SquareGad.Label("Square",INSIDE)
    ComalWindow.InsObject(SquareGad,Error)

    DIM PentagonGad OF ButtonGadget
    PentagonGad.Size(120,20)
    PentagonGad.Position(240,0)
    PentagonGad.Label("Pentagon",INSIDE)
    ComalWindow.InsObject(PentagonGad,Error)


                                         28





    DIM HexagonGad OF ButtonGadget
    HexagonGad.Size(120,20)
    HexagonGad.Position(360,0)
    HexagonGad.Label("Hexagon",INSIDE)
    ComalWindow.InsObject(HexagonGad,Error)


  To be able to stop the program we have to add a Close gadget. This is also
  a button gadget and it is placed to the right of all the other gadgets:

    DIM Close OF ButtonGadget
    Close.Size(138,20)
    Close.Position(480,0)
    Close.Label("STOP",INSIDE)
    ComalWindow.InsObject(Close,Error)


  Now it's time to test for errors:

    IF Error THEN
      STOP "Could not create one or more gadgets"
    ENDIF


  To prevent  the graphics  routines from  overwriting the  gadgets we will
  change the graphics viewport:

    viewport(0,width-1,0,height-20)


  Having made  all the gadgets we are entering a loop where we can wait for
  some buttons to be pressed. The loop terminates when the  Close button is
  pressed:


    REPEAT
      WAIT
      clearscreen  
      IF TriangleGad.Pressed THEN
        triangle
      ELIF SquareGad.Pressed THEN
        square
      ELIF PentagonGad.Pressed THEN
        pentagon
      ELIF HexagonGad.Pressed THEN
        hexagon
      ENDIF
    UNTIL Close.Pressed



                                         29





  The last statement in the main program closes the graphics system:

    textscreen


  The complete program looks:

    // Program 8.1

    USE CITWindow
    USE CITGadgets
    USE Turtle
    USE Figures

    graphicscreen(0)
    ht

    DIM TriangleGad OF ButtonGadget
    TriangleGad.Size(120,20)
    TriangleGad.Position(0,0)
    TriangleGad.Label("Triangle",INSIDE)
    ComalWindow.InsObject(TriangleGad,Error)

    DIM SquareGad OF ButtonGadget
    SquareGad.Size(120,20)
    SquareGad.Position(120,0)
    SquareGad.Label("Square",INSIDE)
    ComalWindow.InsObject(SquareGad,Error)

    DIM PentagonGad OF ButtonGadget
    PentagonGad.Size(120,20)
    PentagonGad.Position(240,0)
    PentagonGad.Label("Pentagon",INSIDE)
    ComalWindow.InsObject(PentagonGad,Error)

    DIM HexagonGad OF ButtonGadget
    HexagonGad.Size(120,20)
    HexagonGad.Position(360,0)
    HexagonGad.Label("Hexagon",INSIDE)
    ComalWindow.InsObject(HexagonGad,Error)

    DIM Close OF ButtonGadget
    Close.Size(138,20)
    Close.Position(480,0)
    Close.Label("STOP",INSIDE)
    ComalWindow.InsObject(Close,Error)

    IF Error THEN
      STOP "Could not create one or more gadgets"
    ENDIF

                                         30






    viewport(0,width-1,0,height-20)

    REPEAT
      WAIT
      clearscreen  
      IF TriangleGad.Pressed THEN
        triangle
      ELIF SquareGad.Pressed THEN
        square
      ELIF PentagonGad.Pressed THEN
        pentagon
      ELIF HexagonGad.Pressed THEN
        hexagon
      ENDIF
    UNTIL Close.Pressed

    textscreen


  The program  is stored on disk under the name Prg8.1. Execute the program
  to see how it works. Before the program starts you will notice a lot of disk
  reading. The CIT system is in fact a rather complicated system and a lot of
  modules are used.



  9 Event driven programs.

  The program in section 8 can be made better. The final loop is in fact un-
  necessary. Why not let the buttons themselves call the procedures automa-
  tically?

  To do this we have to add event procedures to  each button.  Add the fol-
  lowing lines after the the insertion of the TriangleGad (after line 15 in
  PRG8.1):

    TriangleGad.EventHandler(TriangleEvent())
    PROC TriangleEvent(Id OF USHORT)
      clearscreen
      triangle
    ENDPROC TriangleEvent


  By doing this you are telling CIT that the procedure TriangleEvent should
  be called automatically when the triangle button is pressed.

  Add similar lines after the insertion af the other buttons except the close
  button.


                                         31





  By using event procedures that are automatically called by CIT the loop can
  now be changed to this single line:

    WHILE NOT Close.Pressed DO WAIT


  The complete program now looks:

    // Program 9.1

    USE CITWindow
    USE CITGadgets
    USE Turtle
    USE Figures

    graphicscreen(0)
    ht

    DIM TriangleGad OF ButtonGadget
    TriangleGad.Size(120,20)
    TriangleGad.Position(0,0)
    TriangleGad.Label("Triangle",INSIDE)
    ComalWindow.InsObject(TriangleGad,Error)
    TriangleGad.EventHandler(TriangleEvent())
    PROC TriangleEvent(Id OF USHORT)
      clearscreen
      triangle
    ENDPROC TriangleEvent

    DIM SquareGad OF ButtonGadget
    SquareGad.Size(120,20)
    SquareGad.Position(120,0)
    SquareGad.Label("Square",INSIDE)
    SquareGad.EventHandler(SquareEvent())
    ComalWindow.InsObject(SquareGad,Error)
    PROC SquareEvent(Id OF USHORT)
      clearscreen
      square
    ENDPROC SquareEvent

    DIM PentagonGad OF ButtonGadget
    PentagonGad.Size(120,20)
    PentagonGad.Position(240,0)
    PentagonGad.Label("Pentagon",INSIDE)
    PentagonGad.EventHandler(PentagonEvent())
    ComalWindow.InsObject(PentagonGad,Error)
    PROC PentagonEvent(Id OF USHORT)
      clearscreen
      pentagon
    ENDPROC PentagonEvent

                                         32






    DIM HexagonGad OF ButtonGadget
    HexagonGad.Size(120,20)
    HexagonGad.Position(360,0)
    HexagonGad.Label("Hexagon",INSIDE)
    HexagonGad.EventHandler(HexagonEvent())
    ComalWindow.InsObject(HexagonGad,Error)
    PROC HexagonEvent(Id OF USHORT)
      clearscreen
      hexagon
    ENDPROC HexagonEvent

    DIM Close OF ButtonGadget
    Close.Size(138,20)
    Close.Position(480,0)
    Close.Label("STOP",INSIDE)
    ComalWindow.InsObject(Close,Error)

    IF Error THEN
      STOP "Could not create one or more gadgets"
    ENDIF

    viewport(0,width-1,0,height-21)

    WHILE NOT Close.Pressed DO WAIT

    textscreen


  Some programmers may find this program rather strange. It looks as if the
  program does not do anything but waiting for the close gadget to be pres-
  sed (in the WHILE statement).

  But it does! While you are waiting for the close button to be pressed all
  your event procedures are called automatically.

  This sort of a program is called an event driven program.

  The changed version is stored on disk under the name  Prg9.1. Execute the
  program to see if it still does the same.



  10 Debugging programs.

  The Comal system has an integrated debugger that allow you to examine the
  behavior of your programs. To show how it works enter this little program
  (that calculates the number 6!=720):



                                         33





    // Program 10.1

    n:=6

    fak:=1
    WHILE n>0 DO
      fak:=fak*n
      n:=n-1
    ENDWHILE

    PRINT fak

  or load it from disk (Prg10.1 in the Tutorial drawer).

  Select the  Trace Mode? menu item in the Program menu (no short form). As
  a result a little window (the watch window) is opned and the first execut-
  able line (here n:=6) is printed in the reverse pen color. The status line
  shows that you are now in trace mode. In trace  mode you  can move around
  in the text but you cannot edit the text and a lot of functions are disab-
  led.

  Select the Execute One Step menu item from the Trace  menu (short: <right
  Amiga>+<T>). As a result the current line will be executed and the next line
  (fak:=1) is printed in the reverse pen color. Continue to execute steps 4-5
  times by pressing <right Amiga>+<T>.

  As you see, you are able to follow the execution step by step. But to exa-
  mine the execution in detail it is necessary to watch the value of certain
  expressions. Select  the New  Watch Expression  menu item  from the Trace
  menu (no short form). A requester will pop up. Write n into the text field
  of the  requester and  press <ENTER>  to accept. Now the current value of
  the variable n is  shown  in  the  watch  window.  Select  the  New Watch
  Expression menu  item once more and enter the name fak. Also the value of
  this variable will be shown in the watch window as  soon as  you have ac-
  cepted it.

  Continue to  execute program  steps (<right  Amiga>+<T>) and  look at the
  watch window. The current value of the watch variables are  updated after
  each step.

  If you  have not stepped to the end of the program (the watch window will
  close) try to remove a watch expression by double clicking  on it  in the
  watch window. You will be asked to confirm.

  Step to the end of the program and start tracing again. The actual line is
  again n:=0. The watch window tells you that  the variable  is not defined
  (you has not yet executed the line). Set a break point at line 8 (the line
  n:=n-1) by double clicking on the line. The line will be marked as a break
  point line.


                                         34





  Start execution  by selecting  the Continue  Execution menu item from the
  Program menu (short: <right Amiga>+<G>). The execution starts and it stops
  the first time the break point line is met. Note the value of the watch ex-
  pression.

  Remove the break point by double clicking on it once more and execute the
  program to the end.

  Let's look at another example. Load the program Prg10.2 from the Tutorial
  drawer (<right Amiga>+<O>). This program  counts  the  occurences  of one
  (small) text in another (bigger) text.

  Enter the  trace mode  (Program menu) and add the following watches: pos,
  pattern$, text$ and Cnt.  Note that  no one  is defined  yet. Single step
  (<right Amiga>+<T>) 12 times.

  Note that each time the line :

    Cnt:=1+Count(pattern$,text$(pos+1..))

  is executed, the next line to be executed is the first line of the function
  Count, that is called in this line. This can be avoided by selecting the Exe-
  cute One Line menu item from the Trace menu (short: <right Amiga>+<L>).

  By using  the Execute  One Line command the whole function and all recur-
  sive calls to this function will be executed until execution returns to the
  same level as before. Try it to see how it works.
























                                         35





  III. THE PROGRAMMING ENVIRONMENT

  After start  Comal will  open its  own screen and a window in this screen
  (the editor window) and you may start typing  in your  program or  load a
  program from disk.

  From within  the editor  you may  edit programs, load programs from disk,
  start executing programs and much more.


  1 The keyboard.

  The editor works much like any other editor  except that  it makes syntax
  control of all the lines entered and organize the program lines with correct
  indention etc.

  All changes will be put into the program buffer immediately. What you see
  om the  screen is the same as is in the buffer. The only exception is the
  cursor line. It will not be put into the program buffer before you try to
  leave the line (by using the cursor keys, the ENTER key or the mouse). By
  pressing <Esc> the line will be restored to the content of the buffer.

  Certain keys have special functions. Here is a description of these keys and
  their functions:


    <shift>+<cur up>            move to top of window or show
                                previous page

    <Ctrl>+<cur up>             move to start of text


    <shift>+<cur down>          move to bottom of window or show
                                next page

    <Ctrl>+<cur down>           move to end of text


    <Alt>+<cur left>            scroll line right


    <Alt>+<cur right>           scroll line left


    <Alt>+<cur up>              scroll text down


    <Alt>+<cur down>            scroll text up



                                         36





    < <- >                      delete character before cursor


    <Del>                       delete character under cursor


    <shift>+<Del>               delete cursor line


    <shift>+<Ins>               toggles  insert/replace  mode - the current
                                state is shown in the status line


    <shift>+<Alt>+<Ins>         insert line


    <Esc>                       restore line



  On Amiga's without the  numeric keys  (Amiga 600)  the following replace-
  ments for the <Ins> key can be used:

    <shift>+<Ins>         ->    <shift>+<Alt>+<cur right>

                                    or

                                <<Alt>+<RETURN>


    <shift>+<Alt>+<Ins>   ->    <shift>+<Alt>+<cur down>

                                    or

                                <shift>+<Alt>+<RETURN>



  2 The mouse.

  The mouse  can be  used to move the cursor around in the the text. If you
  move to another line the previous cursor line will be syntax checked and if
  the syntax is not correct an error message will pop up and cursor will not
  be moved.

  On the right side of the editor window you will find a scroll bar. The po-
  sition of the scroller inside the container shows the approximate position of
  the window inside the whole text and the size of  the scroller  shows the
  size of the window compared to the whole text.


                                         37





  By moving  the scroller  the window is moved inside the text. Like before
  this will result in a syntax controll of the previos cursor line and if the
  syntax is  not correct an error message will pop up and the scroller (the
  window) will not be moved.


  3 The menus.

  Most of the commands  that can  be executed  from the  editor are reached
  through the menus.


  3.1 The Project menu.

  The Project menu is placed far to the left. It contains the following items:

    New
      By selecting this a new project with its own editor window and its own
      program buffer will be opened.

    Open
      A file requester will pop up and you may select the name of a program
      to load. The program file must be a text file.

      After the load the name of the file will be shown in the screen title
      line.

    Save
      Store program buffer on disk as a text file. If there is a current name
      this will be used. Otherwise a requester will pop up.

    Save As
      Store program buffer on disk as a text file. A requester will pop up so
      that you can select a name.

    File
      An extended form of the file requester will pop up.  From this  it is
      possible to rename files, delete files, copy files, move files from one
      directory to another, make a new directory and a new drawer (a direc-
      tory with a drawer info file).

      Delete, copy  and move  accepts the  selection of more than one file.
      Press the shift key while selecting the next file(s).

    New Shell
      A new Shell (CLI) will open.

    Print
      Output the program in the program buffer to a printer.


                                         38





    Open Command Window
      This will open a  command  window  where  you  may  type  in commands
      like you are used to in other Comal's.

      There are  no special  commands like  LIST, ENTER  etc. The only com-
      mands you can use is PRINT, DIR, CHDIR etc.

    About
      Useful information about the system will be shown

    Clear Program Buffer
      The program in the program buffer will be erased. You  will be warned
      if the program in the buffer has not been saved since last change.

    Quit Project
      The current project will be terminated and if it the only project the
      Comal system will terminate.

      You will be warned if the program in  the buffer  has not  been saved
      since last change.


  3.2 The Edit menu.

  The menu  to theright  of the  Project menu  is the  Edit menu. This menu
  contains the following items:

    Mark Block Start
      The cursor line will be one end of a block and it will be printed in the
      inverse pen  color. Select  the other  end of the block by moving the
      cursor (cursor up/down, PgUp/Dn) or by moving the scroll bar.

      The status line will change  from  Insert/Replace  to  Block  and you
      cannot edit the program.

      If you are already in block mode this mode will be terminated.

    Cut
      The marked  block will  be cut  off the  text and moved into the clip
      board.

    Copy
      A copy of the marked block block will be moved into the clip board

    Paste
      The content of the clip board will be inserted in the program.

    Erase
      The marked block will be erased. You will be prompted to confirm.


                                         39





    Insert file
      A program text file will be inserted at the cursor line.

    Save Block
      Store the marked block on disk as a text file.

    Print block
      Print out the marked block.

    Edit Main Program
      If the editor contains a module you may turn back to the main program
      by using this menu.



  Comal uses  the clipboard.device of the Amiga so that blocks may be moved
  between different projects (started by the New item in the  Project menu)
  or other text editors using this device.


  3.3 The Search menu.

  The third menu contains only these three items:

    Search
      A requester will pop up and you may enter a searh string.

    Search & Replace
      As before but also a replace string has to be entered.

      When a match is found you will be asked if the string should be repla-
      ced. After the answer the search will continue. Use Esc to abort.

    Search Again
      The previous search/search & replace will be resumed.


  Note that the search is case sensitive.



  3.4 The Macros menu.

  A macro is an ARexx script that can be used  to add  new commands  to the
  editor. The ARexx interface will be described in detail later. Here you will
  find only a short description of the menu items:

    Assign Macro
      This item contains a subitem for each function key (F1..F10).


                                         40





      By selecting one of these a requester will pop up an you  may enter a
      macro string to be assigned to that function key. The macro string may
      be the name of  an ARexx  script file  (which must  be placed  in the
      REXX: directory) or an ARexx command.

    Load
      Load a complete set of assignments for the function key.

    Save
      Save the current set of assignment for the function keys on disk.


  At the  start of  a project  a macro file is read and assignments for the
  function keys are made according to the definitions in this file. The name
  of this file is Comal.macro unless another name is specified by the MACRO
  tool type (se section I.9).

  If ARexx is not installed in the system this menu is disabled (goasted).



  3.5 The Settings menu.

  In the Settings menu various parameters may be changed. The content is:

    New Line At <ENTER>
      If selected a new line will be inserted after the current line when the
      enter key is pressed.

    Keywords In Capital
      Some people prefer small letters. They are more readable they say. With
      this item you may  choose to  print keywords  in upper  or lower case
      letters.

    Create Backup
      If selected a backup file will be created when a program is saved.

    Create Icon
      If  selected  an  icon  will  be  created when a program is saved. By
      clicking on this icon Comal will be started  and the  program will be
      loaded.

      All the  options selected in this menu wil be stored in the tool type
      array of the icon.

    Store Window Parameters
      The window position and size will be stored along with  other parame-
      ters in the icon (if icon is selected). This is useful if more than one
      project is loaded at a time.


                                         41





    ASCII File Format
      This item has four subitems:

        LF      -   line end is LF  (standard Amiga)

        CR      -   line end is CR

        CR+LF   -   line end is CR+LF

        PC      -   program is stored in PC format (line end  is CR+LF) and
                    characters are converted.


    Automatic variables?
      Selects if variables in a program should be created automatically or if
      they must be declared in a DIM or LOCAL statement before use.


    Execute IO Window?
      Selects if you want an execute window (used by PRINT,  INPUT etc.) to
      be opened. If this window is not open, Comal will send it the Command
      Window (if open).



  3.6 The Program menu.

  Now we come to the importent Program menu:


    Control
      The program is scanned and the structure is checked. Modules  USEd by
      the program are loaded and initialized and finally all functions/proce-
      dures are made known so that  they can  be executed  from the command
      window.

    Execute
      Program execution starts. During execution the status line is changed to
      Program execution. It is possible to move around in the program buffer
      during program execution but no editing can be performed.

      It is also possible to open another project and start editing or execu-
      ting a new program.

    Stop Execution
      Program execution is stopped.

    Continue Execution
      A stopped program can be restarted.


                                         42





    Parameters (only Workbench 2.04 or higher)
      A special  window is  opened where  you can  enter startup parameters
      send to  to the  program before start. The form of the parameters are
      free. The parameters are stored in the Comal structure (see 10.1.2) in
      the same way as ToolTypes in code files and combined files are stored.

    Load
      A program stored by the Save item can be loaded.

    Save
      Store program buffer in code form. This should be used to store modu-
      les.

    Combine
      Combines the interpreter, the program and all modules into an execut-
      able file. A file requester will pop up and you may select the name of
      the output file.

    Show Modules
      A requester like a file requester shows all modules loaded. By selecting
      one of these modules you may see the content.

    Remove All Modules
      All modules loaded is removed.

    Trace Mode
      Toggle the trace (debugging) mode.



  3.7 The Trace menu.

  This last menu is only active if Trace Mode is on. It contains the following
  items:

    Execute One Step
      One programming step is executed. Then  all watch  expressions in the
      watch window is updated and control is returned to the editor with the
      cursor placed on the next line to be  executed (shown  in reverse pen
      color).

    Execute One Line
      One program  line is  executed. Procedures, functions and single line
      loops are executed. Then all watch expressions in the watch window is
      updated and control is returned to the editor with the cursor placed on
      the next line to be executed (shown in reverse pen color).

    Open Watch Window
      If you have closed the watch window by clicking on its close gadget it
      is re-opened.

                                         43






    New Watch Expressing
      A requester  will pop  up and you may enter an expression (like a va-
      riable, a function name or a more complicated expression). The expres-
      sion and its current value will be shown in the watch window and will
      be updated after each program step.

      A watch expression can  be removed  by double  clicking on  it in the
      watch window. You will be prompted to accept.

    Clear All Watches
      All watch expressions will be removed. You will be prompted to accept.

    Clear All Break Points
      All break points in the program will be removed. You will be prompted
      to accept.


  A break point is set by double clicking on a  program line  in the editor
  while being in trace mode. It is possible to set break points in modules,
  too.

  Concerning the use of the debugger see the tutorial chapter (section 7).




























                                         44





  IV. DESCRIPTION OF THE Comal LANGUAGE


  1 Data types, variables and expressions.

  1.1 Identifiers.

  Identifiers are used as names of variables, functions, procedures etc.

  Examples of identifiers are:

    C   parity    Even    NewName    alfa1    get'key    prg_21


  An identifier consists of a letter eventually followed by a number of let-
  ters, digits, underscores (_) or single quotes ('). An identifier may have any
  length (in fact not longer than 255 characters, but who  would ever reach
  that limit?). Letters may be national letters such as the danish letters  , O
  and A.

  Identifiers may end with a number sign (#) or a dollar sign ($). Such iden-
  tifiers are used as names of integer or string variables or functions.

  Upper and lower case letters are destinct. These identifiers are different:

    newname     NewName   NEWNAME


  Some identifiers are reserved and are used either as names of predeclared
  functions or procedures or they are used as names of  Comal language key-
  words. Examples of reserved words are:

    sin    inkey$     IF     PROC     COS     ENDLOOP


  Reserved identifiers may be written in either upper or lower case letters.
  This means that both print and  PRINT are the  name  of  the  output key-
  word, but Print is not.


  1.2 Simple data types.

  Comal has  a few important data types built into the language. These data
  types are numbers and strings. Other data types may be defined by the pro-
  grammer (see the sections 1.3.2 and 1.5).

  Numbers may  be either integers or floats (reals). But normally you don't
  need to distinguish between integers and floats.



                                         45





  1.2.1 Constants.

  Integers

    The following numbers are examples of integers:

      123456    -852       0

    The integer data type is divided into different subtypes differing by the
    range of integers that can be the value of the type. These types are:

      type      integer range

      ULONG     0 ... 4294967295

      LONG      -2147483648 ... 214783647

      USHORT    0 ... 65535

      SHORT     -32768 ... 32767

      UBYTE     0 ... 255

      BYTE      -128 ... 127


    Integer constants may be written in either decimal, hexadecimal or binary
    format. Hexadecimal constants are preceded by a dollar sign ($) and bina-
    ry constants by a percent sign (%):

      $1234FED    $ABCD   $FFFFFFFF
      %010011     %111    %1000111101111110001


  Floats

    The float  number type is used to represent fractional numbers and num-
    bers of very large or very small magnitude.

    The float numbers are represented internally as floating  point (64 bit
    IEEE). This means that they have aproximately 16 digits of precision and
    a tens exponent in the range -308 to 308.

    Float constants may be written as normal numbers with a  single decimal
    point or in exponential notation:

      3.14159     123456.0     -852.3     0.00001
      3.52E-27    4756328E112   0.000123E-27



                                         46





  Strings

    A string is a sequence af characters (printable as well as non printable).

    A string  constant is  typed as  a sequence of characters surrounded by
    quotes ("):

      "This is a string containing 51 printable characters"
      "Comal"
      "123"

    A quotation mark is written using a double quote:

      "A ""double quote"" example"

    Non printable characters are written by  using its  ASCII number within
    quotation marks:

      "A bad error "7""


  1.2.2 Expressions.

  An expression consists of constants, variables, functions, parenthesis and
  operators that yield a new value.


  Expressions involving numbers

    Operators used in expressions involving numbers are:

                                      examples of use

        -     unary minus             -7  -3.14157

        ^     exponentiation          2^3 (=8)

        *     multiplication          5.2*7.1 (=36.92)

        /     division                4.8/3 (=1.6)

       MOD    modulo (remainder)      27 MOD 4 (=3)

       DIV    integer division        27 DIV 4 (=6)

        +     addition                4.56+5.77 (=10.33)

        -     subtraction             4.56-5.77 (=-1.21)

      BITAND  binary AND              %1101 BITAND %0110 (=%0100)

                                         47






      BITOR   binary OR               %1101 BITOR %0110 (=%1111)

      BITXOR  binary XOR              %1101 BITXOR %0110 (=%1011)

        <     less than               25<37 (=TRUE = 1)

       <=     less or equal           27<=27  (=TRUE)

        =     equal                   5=8 (=FALSE = 0)

       >=     greater or equal        -56>=56 (=FALSE)

        >     greater than            -33>-57 (=TRUE)

       <>     not equal to            44<>0 (=TRUE)

       NOT    logical NOT             NOT 44<>0 (=FALSE)

       AND    logical AND             44<>0 AND 5=8 (=FALSE)

       OR     logical OR              44<>0 AND 5=8 (=TRUE)


    The operators are listed in the order of their precedence. This precedence
    may be changed by using parenthesis:

       2+3*4    = 14
      (2+3)*4   = 20


    Before the  evaluation of an expression all integer types except ULONGs
    are changed to LONG. The type of the  result depends  on the  types and
    the operators in the expression according to the following table

     +------------------+------------------+------------------+
     |                  |                  |                  |
     |   left operand   |  right operand   |    result        |
     |                  |                  |                  |
     +------------------+------------------+------------------+
     |                  |                  |                  |
     |      FLOAT       |    any number    |    FLOAT         |
     |                  |                  |                  |
     |   any number     |      FLOAT       |    FLOAT         |
     |                  |                  |                  |
     |      LONG        |  LONG or ULONG   |     LONG         |
     |                  |                  |                  |
     |     ULONG        |  LONG or ULONG   |    ULONG         |
     |                  |                  |                  |
     +------------------+------------------+------------------+

                                 48







    The result of an expression involving division (/) is FLOAT.

    Normally all  parts of  an expression is evaluated. This means that the
    right parenthesis in

      (6-2*3)*(5+7)

    is evaluated even though the value of the expression is determined by the
    first parenthesis (which is zero).

    An important exception from this rule is expressions containing the logi-
    cal operators AND and OR. If the left operand of  an AND  expression is
    evaluated to  FALSE the  right operand is not evaluated and if the left
    operand of an OR expression is evaluated to TRUE, the  right operand is
    not evaluated. As an example the expression

      (25>37) AND (2=1/0)

    is evaluated to FALSE and it will not result in a run time error since the
    right expression is not evaluated.

    This evaluation rule for AND and OR simplifies a lot  of programming as
    the following example shows.

    Example: This  piece of  code can be used to find a given number n in a
      table of numbers Table()

        index:=MAXINDEX(Table())
        WHILE Index>0 AND n<>Table(index) DO
          index:=index-1
        ENDWHILE

      If the number is not found the value of index is zero.


    Note that there are no boolean type in Comal. Boolean expressions evalu-
    ates to 1 if they are true and they evaluates to 0 if they are false.

    Any number expression used as a boolean expression is considered false if
    it is zero and true otherwise.



  Expressions involving strings

    Operators in expressions involving strings are:



                                         49





                                    examples of use

      (..)  string selection        "strings"(3..6)  (= "ring")

       *    string repetition       3*"Comal" (="ComalComalComal")

       +    string concatenation    "some"+"times" (="sometimes")

       <    less than               "alf"<"alfa" (=TRUE)

      <=    less or equal           "abc"<="ABC"  (=FALSE)

       =    equal                   "some"="any" (=FALSE)

      >=    greater or equal        "2">="!"  (=TRUE)

       >    greater than            "Comal">"BASIC" (=TRUE)

      <>    not equal to            ""<>" " (=TRUE)

      IN    contained in            "me" IN "sometimes" (=3)


    Note that only the first three has string values. The remaining operators
    are used between strings but the value of the expression is a number.

    The selection operator (..) selects part of a string. The first character of
    the selected substring is the character with the position specified by the
    first number in the parenthesis and the last character in the substring is
    the character with the position specified by the last number.

    Example: The function DATE$ returns  the  current  date  in  the format
      yyyy-mm-dd. This  piece of code prints the current data in the format
      mm.dd.yy:

        PRINT DATE$(6..7),".",DATE$(9..10),"."DATE$(3..4)


    It is possible to omit the first or the last character position. In this case
    it is considered as 1 and the length of the string respectively:

        "sometimes"(..4)    = "some"
        "sometimes"(5..)    = "times"

    During evaluation of expressions involving the comparison oprators <, <=,
    =, >=, > and <>, the characters in the strings are compared  one by one
    using the ASCII value of the characters (or the value set in the sorting
    table - see section 10.1.2).



                                         50





    An IN expression evaluates to the first position of the left string operand
    in the right string operand (if it is part of this) and it evaluates to zero
    if the left operand is not part of the right operand.

    Note that since a non zero value is considered true if it is used  as a
    boolean value, an IN expression can be used as a boolean expression.


    Example: This  piece of  code can be used to get a "Yes/No"-answer from
      the user:

        PRINT "Yes or No (Y/N)? :",
        REPEAT
          Answ$:=INKEY$
        UNTIL Answ$ IN "YyNn"
        PRINT Answer$


  1.2.3 Variables.

  A variable is a location in the main memory used to hold a value of a cer-
  tain type. Such locations may be identified by a name (an identifier). Very
  often this name is identified as the variable itself.

  Since the variable is used to hold values of a certain type the size of the
  memory location depends on the type in question.

  Number variables

    Number variables  are declared  (memory is  reserved) in DIM statements
    like

      DIM Number OF FLOAT       // A floating point variable
      DIM n OF LONG, m OF LONG  // Two long integer variables
      DIM c OF UBYTE            // A variable of type UBYTE


    The general format of a number declaration DIM statement is

      DIM variablename [OF NumberType]

    where NumberType is one of  the  types  FLOAT,  ULONG,  ...  BYTE  or a
    user defined type (see 1.5). The default type is FLOAT.

    After the execution of a DIM statements the values of the declared vari-
    ables are zero. This value may be replaced by another value in assignment
    statements like:

      Number:=2.718281828       n:=-38
      m:=45+n                   c:=10

                                         51






    The general format of an assignment statement is:

      identifier:=expression


    The different number types may be mixed in expressions. Comal makes the
    necessary convertions between the types. If an integer variable is assig-
    ned the value of an expression with a floating point value, that value is
    rounded to the nearest integer value. If a value exceeds the range of the
    type, an error is reported by the Comal system.

    Floating point  and long  integer variables need not be declared before
    they are used. If the variable Alfa is not declared before its use, it is
    automatically created as a floating point variable (unless you have turned
    off automatic variable creation - see section III.3.6). If the variable i# is
    not declared before its use, it is automatically created as a long integer
    variable (the number sign # tells the system that it is a  long integer
    variable).

    The  value  of  number  variables  may be decremented or incremented in
    special assignment statements like:

      n:-7
      x:+3.2

    The effect of these statements are the same as the effect af the state-
    ments

      n:=n-7
      x:=x+3.2

    but they are executed a little bit faster.


  String variables

    String variables are used to hold string values. The name of a string va-
    riable always ends with a dollar sign ($). String variables are declared in
    DIM statements like:

      DIM Name$ OF 25   // A variable holding up to 25 characters
      DIM Answ$er OF 1  // A variable holding only one character


    In the declaration af a string variable the maximum length of the string
    value is specified. This maximum string length may be up to 32767. After
    the declaration the value of the string variable is set to the empty string
    (""). This value may be replaced by another value in  assignment state-
    ments like:

                                         52






      Name$:="Borge"
      Answer$:="y"


    If a string variable is assigned a string which is longer than the maximum
    length specified in the declaration statement, the string is truncated. For
    example:

      Answer$:="No" // The string "No" is truncated to "N"

    A string variable need not be declared before its use. If a string variable
    is not declared before it is used, the system will automatically reserve
    space for 80 characters.

    The value  of string  variables may be "incremented" by using a special
    assignment statement like

      Name$:+" Christensen"


    Part of a string variable may be assigned a new value by using the selec-
    tion operator:

      s$:="Comal is a programming language"
      s$(5..9):=": the"


    If the value of the new substring is longer than the selected string, the
    value is truncated. If it is shorter, spaces are added.



  1.3 Structured data types.

  A structured type is a single type built out of simple types in certain ways
  and given a single name. This section discuss indexed variables (arrays) and
  records (strucs). In a later section the class type, which is an extension of
  the record type, will be discussed.


  1.3.1 Indexed variables.

  An indexed variable is a sort of table of values all of the same type. The
  table may have one ore more dimensions.

  Indexed variables are declared in DIM statements where the number  of in-
  dicies (dimensions), the number of index values of each dimension and the
  type of the elements are specified:


                                         53





    DIM a(4,5) OF FLOAT
    DIM b(100) OF BYTE
    DIM t$(20) OF 30

  The first table a has two indicies. The first index may have the values 1,
  2, 3  and 4 and the second index may have the values 1,2,3,4,5. The total
  number of elements, which are all of type FLOAT, is 20.

  The second table b has only one index taking on values from 1 to 100. The
  elements are of type BYTE. The third array t$ has one index that can take
  on values from 1 to 20. Each element is a string with  the maximum length
  30.

  The number of indicies and the range of each index is limited only by the
  available memory. The specified type may be any type (including user defi-
  ned types) except pointer type and array type.

  The indicies may take on the values from 1 (lower bound) up to the speci-
  fied upper bound. It is possible to specify other index ranges:

    DIM a(0..3,0..4) OF FLOAT
    DIM b(-50..49) OF BYTE
    DIM t$(10..29) OF 30

  Here is both the upper and lower bound of the indicies specified. The num-
  ber of index values are the same as in the previous example.

  The upper  and lower  bounds of the index values can be examined by using
  the functions MAXINDEX and MININDEX:

    MININDEX(a())     returns 0
    MININDEX(a(),1)   returns 0   (same as MININDEX(a())
    MAXINDEX(a(),2)   returns 4
    MAXINDEX(t$())    returns 29


  Note that the second argument is optional. If not present the bounds of the
  first dimensions is returned.

  The individual elements of the table is referenced by using the name of the
  table followed by the indicies of the element in parenthesis:

    a(2,3):=11.27
    INPUT "Enter a number: ":b(35)
    t$(15):="blue"
    PRINT a(1,1)

  Each of the elements referenced in this way is treated like a  simple va-
  riable of the elements type, and they may replace simple variables at any
  time.

                                         54






  The whole table can be set equal to the same value in one statement:

    a(,):=111.11
    b():=27
    t$():="yellow"


  1.3.2 Strucs (records).

  A struc is a user defined type whose value is a collection of values that
  are (possibly) of different types (in contradiction to indexed variables).

  The struc type is defined in a STRUC statement which has the form:

    STRUC identifier
        :

      DIM statements

        :
    ENDSTRUC identifier



  The DIM  statements in  the struc definition has the same format as other
  DIM statements except that all size specifications (like the size of a string
  and index bounds) must be constants.

  Later an extension of the struc concept (classes) will be discussed.

  Example:

    STRUC Person
      DIM FirstName$ OF 20, LastName$ OF 20
      DIM Address$ OF 30, City$ OF 20
      DIM Age of UBYTE
    ENDSTRUC Person

  Note that a STRUC statement defines a new type. No variables are defined.
  This is done in the normal way using DIM statements.

  Example:

    DIM Person OF Person
    DIM Member OF Person
    DIM Club(100) OF Person

    Note that it is possible to use the same name for the variable as is used
    for the type.

                                         55






  The elements of the struc are called fields and are accessed using the name
  of the struc variable, a dot and the name of the field (the dot notation).

  Example:

    Person.FirstName$:="Len"
    INPUT "Last name: ": Member.LastName$
    PRINT Club(35).Age


  A struc may be assigned the value of another struc in an assignment state-
  ment.

  Example:
    Member:=Club(35)
    Club():=Person


  1.4 Dynamic variables. Pointer variables.

  Any variable  that is  to be used in a program needs two things. It needs
  storage in the main memory to store the value of the variable and it needs
  some means of identification. A declaration like

    DIM Alfa OF UBYTE

  meets both this needs. It creates a variable (reserves storage in main me-
  mory) and it identifies this variable by the name Alfa.

  There is another more indirect way of creating and identifying a variable.
  In the declaration

    DIM p OF POINTER TO UBYTE

  a variable p is created. The value of this variable is not itself of type
  UBYTE. In stead the value is the identifier of a variable of type UBYTE.

  A variable is in fact a memory location in the main memory.  Such a loca-
  tion has an address. This address can be identified by a name (that's what
  we have used until now) or it can be identified by a number (in our daily
  life addresses  are mixtures  of names and numbers). In our newly created
  variable the address of the UBYTE variable is identified by  a number. If
  you execute the statement

    PRINT p

  you will get this number. It turns out to be zero which is synonymous for
  no address. There is no address since the UBYTE variable has not yet been
  created.

                                         56






  This can be done in a statement like

    ALLOCATE(p)


  Now the statement

    PRINT p

  will give a non zero address.

  The value of the UBYTE variable is accessed by using the indirection opra-
  tor @ as shown in the following statements

    p@:=5
    p@:+7
    PRINT p@+27


  As you see p@ is a normal variable (of type UBYTE).

  In the statement

    ALLOCATE(p)

  the procedure ALLOCATE reserves space for the variable  p@ among  all the
  other variables  used in  the program. The ALLOCATE procedure may also be
  called with two parameters:

    ALLOCATE(p,MemType)


  If this form is used the memory is allocated in the storage administrated by
  the Amiga operating system. The value of MemType can be:

      MEMF_PUBLIC (=1)
      MEMF_CHIP   (=2)
      MEMF_FAST   (=4)


  Normally  you  will  use  MEMF_PUBLIC.  This  name  as  well as the names
  MEMF_CHIP and MEMF_FAST are defined in the System module.

  The memory allocated by the  procedure  ALLOCATE  can  be  deallocated by
  the statement

    DEALLOCATE(p)



                                         57





  After the execution of this statement the value of p is again zero (which
  means no address).

  A variable created by the procedure  ALLOCATE is  created during  the ex-
  ecution of the program and is therefore called a dynamic variable. The va-
  riable p itself is called a pointer variable since the value of p points to a
  variable.

  The general format of the declaration af a pointer variable is

    DIM varname OF POINTER TO typename
    DIM varname@ OF typename            // Short form

  where typename can be any type except a pointer type (see next section).

  Normally the  type pointed  at (typename above) has to be known, i.e. the
  type must be defined somewhere else in the program or it must be imported
  from a module. Exception from that rule are pointers that are fields in a
  structure. In this case the type pointed at is set to unknown pointer if it
  is not defined.

  If a  structure with  unknown pointer types is imported from a module the
  system looks for the  type in  the program  environment that  imports the
  structure and changes the unknown pointer type to the correct type if it is
  found. Otherwise the type will contiue to be unknown pointer.

  It is not possible to use the indirection operator on a pointer with unknown
  pointer type.


  Example: To write a text directly into an intuition window a structure of
    the following is used

      STRUC IntuiText
        DIM FrontPen OF UBYTE
        DIM BackPen OF UBYTE
        DIM DrawMode OF UBYTE
        DIM LeftEdge OF SHORT
        DIM TopEdge OF SHORT
        DIM ITextFont OF POINTER TO TextAttr
        DIM IText OF POINTER TO UBYTE
        DIM NextText OF POINTER TO IntuiText
      ENDSTRUC IntuiText


    If the type TextAttr is not defined elsewhere the pointer type is set to
    unknown pointer.




                                         58





  Example: In the module System the ComalStruc structure is defined (se sec-
    tion 10.1.2). One of the fields of this structure is:

      DIM CommPort OF POINTER TO MsgPort

    The type MsgPort is not known in the  module and  the type  of CommPort
    is unknown pointer.

    If you want to use this field you have to import the module PortObjects
    along with the module System:

      USE PortObjects
      USE System


  A pointer variable acts in many cases like a variable of type ULONG. It has
  a value (an address) of type ULONG, that may be used in any expression. It
  is also possible to assign a value to a pointer variable using an assignment
  statement:

    p:=$0004


  Such an  assignment can be dangerous since you are now able to change any
  portion of the memory in your Amiga. To avoid mistakes  you can  only as-
  sign an  expression of type ULONG to a pointer. In this way most mistakes
  will be caught by the system because ULONG  values are  difficult to make
  (see description of expressions).

  Example:  The  function  in  this  example  implements  the PEEK function
    known from many BASIC dialects


        FUNC peek(MemPtr OF UbytePtr) OF UBYTE
          RETURN MemPtr@
        ENDFUNC peek

        TYPE UbytePtr=POINTER TO UBYTE


  Example: The following program fraction shows the use of pointers  in the
    creation of a linked list:


          :
          :

        STRUC Person
          DIM Next OF POINTER TO Person
          DIM Name$ OF 30

                                         59





          DIM Address$ OF 30
          DIM City$ OF 20
          DIM Age of UBYTE
        ENDSTRUC Person

        DIM Start OF POINTER TO Person
        DIM Node OF POINTER TO Person

          :
          :

        // Insert the variable Person@ in the list

        IF Start=0 THEN
          Start:=Person
        ELSE
          Node:=Start
          IF Start@.Name$<Person@.Name$ THEN
            WHILE Node@.Next AND Node@.Next@.Name$<Person@.Name$
              Node:=Node@.Next
            ENDWHILE
          ENDIF
          Person@.Next:=Node@.Next
          Node@.Next:=Person
        ENDIF

          :
          :


  A pointer  variable can  be used as a one dimensional indexed variable by
  placing an index value after the  variable name.  The range  of the index
  values is all non negative integers.


  Example: An  array of  100 elements  of type Person can be made by execu-
    ting the statements

      DIM Pers OF POINTER TO Person

      Pers:=malloc(100*SIZE(Person),MEMF_PUBLIC)

    and you can get one of the elements in this way:

      Pers(23).Name$="Donald E. Knut"


  This way of indexing into a dynamic variable can be very useful in connec-
  tion with some of the system routines.


                                         60






  1.5 Type definition.

  New types  are defined  in a  STRUC statement.  But new types may also be
  defined in a TYPE  statement. The  TYPE statement  can take  on different
  forms.


  TYPE NewType=OldType

    By using  this form  of the TYPE statement no new type is created. It's
    only new names for existing types.

    Examples:

      TYPE UWORD=USHORT
      TYPE WORD=SHORT
      TYPE BOOL=BYTE


  TYPE PtrType=POINTER TO Type

    This form of the TYPE statement is used to create a pointer type.

    Examples:
      TYPE UbytePtr=POINTER TO UBYTE
      TYPE WordPtr=POINTER TO WORD
      TYPE PersonPtr=POINTER TO Person
      TYPE WdPtr=POINTER TO Window

  TYPE AryType=ARRAY(dimension list) OF Type

    This form of the TYPE statement is used  to create  an array  type. The
    dimension list has the same form as discussed in the description of the
    DIM statement in section 1.3.1 (Indexed variables) except that only con-
    stant may be used.

    Examples:
      TYPE IntArray=ARRAY(0..5,-2..10) OF LONG
      TYPE Data=ARRAY(0..150000) OF UBYTE
      TYPE String10=ARRAY(0..10) OF UBYTE


    Having made these type definitions a pointer to (for example) a variable
    of type Data can be declared and a large array of bytes can be created:

      DIM DataPtr OF POINTER TO Data
      ALLOCATE(DataPtr,MEMF_PUBLIC)



                                         61





  TYPE FncType=FUNC[(parameter type list)] [OF NumberType]
  TYPE PrcType=PROC[(parameter type list)]

    These forms of the TYPE statement is used to create function and proce-
    dure types. This is necessary if you want to make functions or procedures
    with such parameter types. The terms inside the square brackets are op-
    tional.

    Examples:
      TYPE FltFunc=FUNC(FLOAT) OF FLOAT
      TYPE ByteFnc=FUNC OF BYTE
      TYPE ExceptProc=PROC(ULONG)


    Example: In  this program an integral function is made and the integral
      of the function sin from 0 to pi is printed out:

        PRINT integral(SIN(),0,PI)

        FUNC integral(f OF FltFunc,a,b)
          LOCAL n, dx, xi, s
          n:=16; dx:=(b-a)/n;
          xi:=a; s:=(f(a)-f(b))*dx/6
          LOOP n TIMES xi:=xi+dx; s:=s+(f(xi)+2*f(xi-dx/2))*dx/3
          RETURN s
        ENDFUNC integral

        TYPE FltFunc=FUNC(FLOAT) OF FLOAT


  TYPE statements may be  placed in  the main  program, in  closed procedu-
  res/functions or in modules. The program execution speed is not affected by
  the use of new type names. All type references are resolved at  the scan-
  ning time.

  The search for a matching type is done in this way:

    1.  The scanner  searches for  the name  in the same program level (the
        same procedure/function, module or the main program).

    2.  Then the name is searched for in the  modules used  in this program
        level.

    3.  Then the  name is  searched for  in lower  program levels (the main
        program or a procedure if the current procedure is local) or in modu-
        les used in these lower levels.

  If the  name cannot  be found the scanner will stop with an error message
  and the cursor will be placed on the unknown type name.


                                         62






  2 Program flow control statements.

  This section discuss the program statements that can be used to direct the
  program's sequence of execution ("flow control statements").


  2.1 Selection.

  Comal provides several forms of decision-making statements: the IF state-
  ments and the CASE statement.


  2.1.1 The IF statements.

  More than five variants of the IF statement are available, depending upon
  the use of ELSE and ELIF.


  Single line IF - THEN

    The single line IF statement has the form:

      IF condition THEN simple_statement

    where condition  is a number expression whose value is interpreted as a
    boolean value and simple_statement is any  non declaration  single line
    statement (except single line IF). If the condition is fulfilled the state-
    ment after THEN is executed.

    Examples:

      IF Printer$="Y" THEN SELECT OUTPUT "lp:"

      IF x>Max THEN Max:=x


  IF - THEN - ENDIF

    This is the simplest form of the multy  line IF  statement. The general
    format is:

      IF condition THEN
        statements
      ENDIF

    If the condition is fulfilled the statement(s) between the IF-THEN line
    and the ENDIF line is executed. If the condition is not fulfilled, then
    these lines  are skipped, and the program execution continues after the
    ENDIF line.

                                         63






    Example: In the following example an Intuition window is opened. If the
      operation fails (Window pointer is zero) an error message is printed and
      the program stops:

        Window=OpenWindow(ADR(NewWindow))
        IF Window=0 THEN
          PRINT "Window could not be opened"
          STOP
        ENDIF



  IF - THEN - ELSE - ENDIF

    The general format of this variation of the IF statement is:

      IF condition THEN
        statements
      ELSE
        statements
      ENDIF

    The statement(s) between the IF-THEN line and the ELSE line is executed
    if the condition is fulfilled, while the statement(s) between the ELSE line
    and the ENDIF line is executed if the condition is not satisfied. continues
    after the  ENDIF line. One of the statement segments will always be ex-
    ecuted, but never both. When the selected segment has been executed the
    program execution continues after the ENDIF line.


    Example:

      IF Printer$="Y" THEN
        SELECT OUTPUT "lp:"
      ELSE
        PAGE  // Clear screen
      ENDIF


  IF - THEN - ELIF - ENDIF

    The simplest form of this variation of the IF statement is:

      IF condition THEN
        statements
      ELIF condition THEN
        statements
      ENDIF


                                         64






    The ELIF (else if) condition is examined if and only if the IF condition is
    not satisfied. An arbitrary number of ELIF statements may be  placed in
    the IF-statement, for instance:

      IF condition THEN
        statements
      ELIF condition THEN
        statements
      ELIF condition THEN
        statements
      ELIF condition THEN
        statements
      ENDIF


    The conditions are examined one by one until one is fulfilled or there is
    no more ELIF lines. If a condition is fulfilled the statement(s) between
    the corresponding  IF/ELIF and  the next  ELIF/ENDIF is executed. After
    that the remainig conditions and statements are skipped, and the program
    execution continues after the ENDIF line. If none of conditions are ful-
    filed, then no statements are executed, and the  program execution con-
    tinues after the ENDIF line.


    Example:

      IF x>Max THEN
        Max:=x
      ELIF x<Min THEN
        Min:=x
      ENDIF



  IF - THEN - ELIF - ELSE - ENDIF

    The simplest form of this variation of the IF statement is:

      IF condition THEN
        statements
      ELIF condition THEN
        statements
      ELSE
        statements
      ENDIF


    An arbitrary  number of  ELIF statements may be placed in the IF-state-
    ment, for instance:

                                         65






      IF condition THEN
        statements
      ELIF condition THEN
        statements
      ELIF condition THEN
        statements
      ELIF condition THEN
        statements
      ELSE
        statements
      ENDIF

    The conditions are examined one by one until one is fulfilled or there is
    no more ELIF lines. If a condition is fulfilled the statement(s) between
    the corresponding IF/ELIF and the  next  ELIF/ELSE  is  executed. After
    that the  remainig part of the IF statement is skipped, and the program
    execution continues after the ENDIF line. If none of conditions are ful-
    filed, then the statements between ELSE and ENDIF will be executed.

    Example: This  program section  prints the  solution of a second degree
      equation (with coefficients A, B and C)

        D:=B^2-4*A*C    // The discriminant is calculated
        IF D<0 THEN
          PRINT "No solution"
        ELIF D=0 THEN
          PRINT "There is one solution:";-B/(2*A)
        ELSE
          x1:=(-B-SQR(D))/(2*A)
          x2:=(-B+SQR(D))/(2*A)
          PRINT "There are two solutions:";x1;"and";x2
        ENDIF



  2.1.2 The CASE statement.

    With a CASE statement it is possible to provide multiple branching. The
    general form of the statement is:

      CASE expression OF
      WHEN expression_list
        statements
      WHEN expression_list
        statements
      WHEN expression_list
        statements
      WHEN expression_list
        statements

                                         66






        :

      OTHERWISE
        statements
      ENDCASE


    An arbitrary  number of  WHEN options  can be  used. The OTHERWISE part
    is optional.

    When the CASE expression has been evaluated, it  will be  compared with
    each of the values specified in the first WHEN branch. If one of the val-
    ues agrees with the CASE expression, then the following statement (until
    next  WHEN/OTHERWISE/ENDCASE)   will  be   executed,  and  the  program
    execution will continue after ENDCASE.

    Each WHEN branch expression in the CASE statement  will be  examined in
    the order  in which  they occur.  The first  WHEN segment  for which an
    agreement is found will  be executed.  The remaining  part of  the CASE
    statement will be skipped, and the program execution will continue after
    ENDCASE.

    If none of the WHEN branch values agree  with the  CASE expression, the
    statement(s)  after  OTHERWISE  (if  present)  will  be executed. If no
    OTHERWISE part is present and none  of  the  WHEN  branch  values agree
    with the CASE expression, and error message will be generated.


    Example:

      CASE month$ OF
      WHEN "jan","mar","may","jul","aug","oct","dec"
        days:=31
      WHEN "apr","jun","sep","nov"
        days:=30
      WHEN "feb"
        days:=28    // No leap year support
      ENDCASE


    The type  of the  WHEN branch  values must  be the  same as the type of
    the CASE expression (string or number).



  2.2 Repetition.

  In Comal there are several forms  of repetive  statements: REPEAT, WHILE,
  FOR and LOOP.

                                         67







  REPEAT - UNTIL

    This repetive statement allows groups of statements to be executed again
    and again, until some terminating UNTIL condition is fulfilled.

    The general format of the statement is

      REPEAT
        statements
      UNTIL condition

    Since the the statements are executed before the condition is evaluated,
    the statements will always be executed at least once.

    Example:

      REPEAT
        CURSOR 5,10
        PRINT ""155"K",   // Erase to end of line
        INPUT "Enter number of disks (between 1 and 10): ": Num
      UNTIL Num>=1 AND Num<=10



  WHILE - ENDWHILE

    The  WHILE  statement  is  similar  to the REPEAT statement. It is used
    when a block of statements is to be  executed repeatedly  as long  as a
    particular condition is fulfilled. The major difference is that the condition
    is evaluated and tested before any statements inside the WHILE-block is
    executed.

    The general format of the WHILE statement is:

      WHILE condition DO
        statements
      ENDWHILE

    If the  condition is fulfilled, then the statement(s) between the WHILE
    line and ENDWHILE line is executed, and the condition will then be eval-
    uated again.

    If the condition is not satisfied, then the statement block is skipped and
    the program execution will continue after the ENDWHILE line.


    Example: This little program will print the content of a text file on the
      screen

                                         68







        OPEN FILE 1,"TextFile",READ
        WHILE NOT EOF(1) DO
          INPUT FILE 1: Line$
          PRINT Line$
        ENDWHILE
        CLOSE FILE 1


  Single line WHILE

    If only a single statement follows the WHILE line, then the statement can
    be placed in a single line WHILE.

    The format of a single line WHILE is:

      WHILE condition DO statement

    where simple_statement is any non declaration single line statement.

    Example: This line waits for a key to be pressed without using CPU time:

        WHILE KEY$<>"" DO WAIT



  FOR - ENDFOR

    The FOR statement is used if a group of statements are to be executed a
    certain number  of times with given counter values. The number of times
    the statement block is to be executed, is determined by means of a con-
    trol variable (the counter).

    The general format is

      FOR counter:=start TO end [STEP interval] DO
        statements
      ENDFOR counter

    The FOR statement contains an indication of the start and end values of
    the control variable. The statement block is executed until the control
    variable exceeds the the end value.

    When the group of statements has been executed, the control variable is
    changed. How much it is changed can be specified by  means of  the STEP
    option. If this option is omitted, then the control variable will be incre-
    mented by 1. After the change of the control variable the value is com-
    pared with the end value.


                                         69





    The first  time the value is surpassed, the FOR loop is completed , and
    the program execution continues after the ENDFOR line.


    Example:

      FOR i:=1 TO MaxMember DO
        PRINT Club(i).LastName$,", ",Club(i).FirstName$
        PRINT Club(i).Address$
        PRINT Club(i).City$
        PRINT
      ENDFOR i


  Single line FOR

    If only a single statement follows the FOR line, then the statement can
    be placed in a single line FOR.

    The format of a single line FOR is:

      FOR counter:=start TO end [STEP interval] DO statement

    where statement is any non declaration single line statement.

    Example:

      FOR x:=0 TO 6.3 STEP 0.1 DO PRINT x;COS(x);SIN(x)



  LOOP - TIMES - ENDLOOP

    The LOOP  statement is used if a group of statements are to be executed
    a certain number of times and you do not need a counter variable.

    The general format is:

      LOOP number TIMES
        statements
      ENDLOOP


    The statements are executed number times  before the  program execution
    continues after the ENDLOOP line.

    This loop  statement is  executed considerably faster than the FOR loop
    because of the lack of the counter variable.



                                         70





    Example:

      LOOP 4 TIMES
        forward(80)
        right(90)
      ENDLOOP


  Single line LOOP

    If only a single statement follows the LOOP-TIMES line, then the state-
    ment can be placed in a single line LOOP.

    The format of a single line LOOP is:

      LOOP number TIMES simple_statement

    where simple_statement is any non declaration single line statement.

    Example:

      t$:=""
      LOOP 30 TIMES t$:+"*"


  Endless LOOP

    This is  the simplest of all the repetive statements. It is used when a
    block of statements is to be executed an infinite number of times.

    The format of the statemet is

      LOOP
        statements
      ENDLOOP

    Example: The following piece of code is a typical main  part of  a menu
      driven program

        LOOP
          PAGE
          PRINT AT 7,10: "Create"
          PRINT AT 9,10: "Delete"
          PRINT AT 11,10: "Write"
          PRINT AT 15,10: "Select job: ",
          job$:=PressKey$("CcDdWw")
          CASE job$ OF
          WHEN "C","c"
            Create
          WHEN "D","d"

                                         71





            Delete
          WHEN "W","p"
            Write
          ENDCASE
        ENDLOOP


    The loop can be terminated by executing one of the branching statements
    (GOTO, IF .. GOTO, EXIT, EXIT WHEN ..).



  2.3 Branching.

  Branching means changing a program's flow of control by some  means other
  than the  statements described in the sections 2.1 and 2.2. There are two
  such branching statements: the GOTO statement and the EXIT statement.


  2.3.1 The GOTO statement.

  The GOTO statement is used to redirect execution  to another  part of the
  program. The new location is specified by a label.

  The general format of a GOTO statement is:

    GOTO label


  The format of a label line is:

    label:

  where label is an identifier. Note the colon (:) after the label name.

  The GOTO  statement is  not often  used, because of the presence of other
  flexible structures like the other program flow statements discussed in sec-
  tion 2.1 and 2.2. But there are times (usually error conditions) when it is
  the least painful way to break out of a number  of nested  program struc-
  tures.


    Example:

      Window=OpenWindow(ADR(NewWindow))
      IF Window=0 THEN
        PRINT "Window could not be opened"
        GOTO CleanUp
      ENDIF


                                         72







  2.3.1 The EXIT statement.

  The EXIT statement is used in any of the repetive statements to cause con-
  ditional or unconditional interrupt of the loop. When EXIT has been execu-
  ted, the program execution continues after the end of the loop.

  The format of an EXIT statement is:

    EXIT [WHEN condition]

  If the  optional WHEN part is present the exit of the loop will only take
  place if the condition is fulfilled. Otherwise the exit will take place uncon-
  ditionally.

    Example:

      OPEN 1,"SER:",READ        // Open serial port
      LOOP
        ch$=GET$(1,1)
        EXIT WHEN ch$=""26""    // ^Z (ASCII 26) is end of file
        PRINT ch$
      ENDLOOP
      CLOSE FILE 1


  3 Procedures and functions.

  One way  of designing  modular programs  in Comal  is by using procedures
  and functions.

  Procedures and functions are some sort of named  subprograms that  can be
  activated in any part of the program.


  3.1 Procedures.

  The general format of a procedure is

    PROC procedure_name[(formal_parameter_list)] [CLOSED]
      program_statements
    ENDPROC procedure_name


  The program_statements  inside the  procedure are activated by an execute
  statement. The format of this statement is

    procedure_name[(actual_parameter_list)]


                                         73






  The statements inside the procedure are executed as normal program state-
  ments. The  execution of  these statements  stops when the ENDPROC state-
  ment is reached or when a RETURN statement is executed.

  The format of a procedure RETURN statement is

    RETURN


  When the execution of the procedure statements stops,  the program execu-
  tion continues after the execute statement that activated the procedure.

  The name of a procedure is an identifier. Normally one chooses a name that
  reflects the action of the procedure. Procedures can be placed averywhere
  in the  program except  that it  may not  appear within IF, CASE, REPEAT,
  WHILE, FOR, LOOP and TRAP statements.

  It's a good idea to place procedures at the end of the program. In this way
  it reflects the top-down programming strategi.


  3.1.1 Procedures without parameters.

  The simplest form of procedures are the procedures without parameters. The
  general format of such procedures is

    PROC procedure_name
      program_statements
    ENDPROC procedure_name

  and they are activated by an execute statement of the form

    procedure_name

  Example: The procedure EraseToEndOfLine in this example will erase to the
    end of the line

        :
      REPEAT
        CURSOR 5,10
        EraseToEndOfLine
        INPUT "Enter number of disks (between 1 and 10): ": Num
      UNTIL Num>=1 AND Num<=10
        :

      PROC EraseToEndOfLine
        PRINT ""155"K",
      ENDPROC EraseToEndOfLine


                                         74







  3.1.2 Value parameters.

  Data can  be transferred  to procedures through parameters. There are two
  kinds of parameters: Value parameters and reference parameters. Value pa-
  rameters are exclusively used to transfer data into the procedure. Reference
  parameters are used both to transfere data into the procedure  and to get
  data back  from the  procedure. Value parameters and reference parameters
  can be mixed in procedures, but they are discussed in  separate sections.
  Reference parameters will be discussed in the next section.

  Value parameters are special kinds of local variables that are initialized
  when the procedure is called.

  A procedure with value parameters have the format

    PROC procedure_name(formal_parameter_list) [CLOSED]
      program_statements
    ENDPROC procedure_name


  The formal_parameter_list consists of one or more formal parameters sepa-
  rated by commas. The format of a formal value parameter is

    ParameterName [OF Type]
    IntegerName#
    StringName$


  If the type in the first form is not specified, the type of the parameter is
  set to FLOAT (the default type).


  Example: The procedure polygon in this  example draws  a polygon.  It re-
    quires that the Turtle module is loaded.

      PROC polygon(Corners OF UBYTE, SideLen OF FLOAT)
        LOOP Corners TIMES
          forward(SideLen)
          right(360/Corners)
        ENDLOOP
      ENDPROC polygon


  A procedure with parameter(s) is called by executing a line of the format

    procedure_name(actual_parameter_list)



                                         75





  The actual  parameters consists  of one  or more expressions separated by
  commas. The types of the expressions  must be  assignment compatible with
  the corresponding formal parameters, i.e. numbers must correspond to num-
  bers and strings to strings.


  Example:  The following procedure calls are all legal calls to the procedure
    polygon:

      DIM s OF BYTE

      polygon(3,s)
      polygon(4,3.5)
      polygon(6,25/3+s)


  A value  parameter can  be of  any type  except a  STRUC. An array can be
  transferred either by defining an array type and then use this type in the
  formal parameter description, or by using array indicators.


  Example:

    PROC ArrParProc1(Alfa OF ArrType)   // Array type used
    PROC ArrParProc2(Beta(,) OF LONG)   // Array indicator used
    TYPE ArrType=ARRAY(5,8) OF LONG


  In both  cases the  actual parameter must be an array name with array in-
  dicators.


  Example: The procedure PrintArray in this example prints an array as a two
    dimensional table.

    PROC PrintArray(Beta(,) OF BYTE)
      ZONE 5
      FOR i=1 to MAXINDEX(Beta(,),1) DO
        FOR j=1 TO MAXINDEX(Beta(,),2) DO
          PRINT Beta(i,j),
        ENDFOR j
        PRINT
      ENDFOR i
    ENDPROC PrintArray

    PrintArray(A(,))


  Note that  if arrays are used as value parameters, a copy of the array is
  made each time the procedure is called. This can result in a  lot of time

                                         76





  consuming copying. Therefor it is recommended to use reference parameters
  when possible (as for instance in the example above).

  Procedures (and functions) can be used as parameters. To do this a procu-
  dure (function) type has to be defined (see section 1.5).


  3.1.3 Reference parameters.

  Reference parameters can be used to return values back from a procedure.

  Procedures with reference parameters have the format

    PROC procedure_name(formal_parameter_list) [CLOSED]
      program_statements
    ENDPROC procedure_name


  The formal_parameter_list consists of one or more formal parameters sepa-
  rated by commas. The format of a formal referencee parameter is

    REF ParameterName [OF Type]
    REF IntegerName#
    REF StringName$


  If the type in the first form is not specified, the type of the parameter is
  set to FLOAT (the default type).

  Example: The  procedure swap  in this example interchanges the content of
    two string variables (with maximal length 50)

      text1$:="Text number 1"
      text2$:="Text number 2"
      swap(text1$,text2$)

      PROC swap(REF t1$,REF t2$)
        LOCAL Temp$ OF 50

        Temp$:=t1$
        t1$:=t2$
        t2$:=Temp$
      ENDPROC swap


  A procedure with reference parameter(s) is called by executing a line of the
  format

    procedure_name(actual_parameter_list)


                                         77






  The actual parameters consists of one or more variables separated by com-
  mas. The type of the variables must be the same as the type of the corre-
  sponding formal  parameters, i.e. string must correspond to string, FLOAT
  to FLOAT, ULONG to ULONG etc.

  At the call to a procedure with reference parameters a new local variable
  is not created (as was the case with value parameters). Only a new name is
  introduced to identify an existing variable. Every change made in the para-
  meter is therefore made on the variable used as the actual parameter in the
  calling line.

  To be specific. The changes made on the strings t1$ and t2$ in the proce-
  dure swap  in the  example above are in fact made on the string variables
  text1$ and text2$.


  3.1.4 Local procedures.

  It is possible to place procedures and/or functions inside a procedure (or
  function). Such a procedure is called a local procedure and it is only known
  inside the procedure where it is placed.

  Example:

    PROC QuickSort(REF t$(),Start OF LONG,End OF LONG)
      LOCAL temp$ OF 10, x$ OF 10
      LOCAL a OF LONG, z OF LONG

      a:=Start; z:=End; x$:=t$((Start+End)/2)
      REPEAT
        WHILE t$(a)<x$ DO a:+1
        WHILE x$<t$(z) DO z:-1
        IF a<=z THEN
          swap(t$(a),t$(z))
          a:+1; z:-1
        ENDIF
      UNTIL a>z
      IF Start<z THEN QuickSort(t$(),Start,z)
      IF a<End THEN QuickSort(t$(),a,End)

      PROC swap(REF t1$,REF t2$)
        LOCAL Temp$ OF 50

        Temp$:=t1$
        t1$:=t2$
        t2$:=Temp$
      ENDPROC swap

    ENDPROC QuickSort

                                         78








  3.1.5 Local and global variables. CLOSED procedures.

  Variables, procedures, functions and type definitions outside a procedure are
  accessible from a procedure.

  Further more, a variable created inside a procedure (either declared in a
  DIM statement or declared implicitely in an assignment statement) is in fact
  created out side the procedure an is still there after the procedure is left.


  Example: The output from the following little program

      x:=-5
      y:=-8
      MyProc
      PRINT x;y

      PROC MyProc
        x:=11
        y:=7
      ENDPROC MyProc

    is

      11 7


  Sometimes these effects of creating and/or using variables inside a proce-
  dure are intended. But in other cases it is the source of unpredictable and
  undiserable results.

  If a variable used inside a procedure is strictly local to that procedure, it
  should be declared as local. This is done in a LOCAL statement,  that has
  the same form as a DIM statement:

    LOCAL name[(dimension_specification)] [OF Type]
    LOCAL pntr_name OF POINTER TO Type
    LOCAL integer_name#[(dimension_specification)]
    LOCAL string_name$[(dimension_specification)] OF string_len


  Example: The output from the following little program

      x:=-5
      y:=-8
      MyProc
      PRINT x;y

                                         79






      PROC MyProc
        LOCAL x OF SHORT
        x:=11
        y:=7
      ENDPROC MyProc

    is

      -5 7


  Example:

    PROC QuickSort(REF t$(),Start OF LONG,End OF LONG)
      LOCAL temp$ OF 10, x$ OF 10
      LOCAL a OF LONG, z OF LONG
      a:=Start; z:=End; x$:=t$((Start+End)/2)
      REPEAT
        WHILE t$(a)<x$ DO a:+1
        WHILE x$<t$(z) DO z:-1
        IF a<=z THEN
          temp$:=t$(a); t$(a):=t$(z); t$(z):=temp$
          a:+1; z:-1
        ENDIF
      UNTIL a>z
      IF Start<z THEN QuickSort(t$(),Start,z)
      IF a<End THEN QuickSort(t$(),a,End)
    ENDPROC QuickSort


  The use of local variables prevents undiserable name coincidence. Another
  way to avoid this is to make the procedure closed. This is accomplished by
  adding the word CLOSED at the end of the PROC line.

  All variables in a closed procedure are local, and variables, procedures and
  functions found outside the procedure is unknown inside the procedure.

  Example: The output from the following little program is -5 -8


      x:=-5
      y:=-8
      MyProc
      PRINT x;y

      PROC MyProc CLOSED
        x:=11
        y:=7
      ENDPROC MyProc

                                         80







  In order to make identifiers outside a closed procedure accessible it is pos-
  sible to  import them. This is done by using the IPMPORT statement or the
  GLOBAL statement.

  The IMPORT statement is used to import identifiers from the program level
  just below  the procedures  level (for  instance another procedure if the
  IMPORT statement is placed  in a  local procedure).  The GLOBAL statement
  imports identifiers from the main part of the current program environment
  (from the main program, from the initialization part of a  module or from
  the fields in a STRUC if the GLOBAL statement is placed in a method).

  The general format of an IMPORT statement and a GLOBAL statement is

    IMPORT name_list
    GLOBAL name_list

  where name_list is one or more names separated by commas.

  Example:
    IMPORT a,Beta,Str$
    GLOBAL p#,Alfa$,x


  Example:  The QuickSort procedure could have been written (although it is
    not recommendable):

    PROC QuickSort(Start OF LONG,End OF LONG) CLOSED
      IMPORT t$
      DIM temp$ OF 10, x$ OF 10
      DIM a OF LONG, z OF LONG
      a:=Start; z:=End; x$:=t$((Start+End)/2)
      REPEAT
        WHILE t$(a)<x$ DO a:+1
        WHILE x$<t$(z) DO z:-1
        IF a<=z THEN
          temp$:=t$(a); t$(a):=t$(z); t$(z):=temp$
          a:+1; z:-1
        ENDIF
      UNTIL a>z
      IF Start<z THEN QuickSort(Start,z)
      IF a<End THEN QuickSort(a,End)
    ENDPROC QuickSort


  It is possible to place USE statements and TYPE definition statements in-
  side a closed procedure.



                                         81





  3.2 Functions.

  The FUNC  statement is  used to  define funtions. The general format of a
  FUNC statement is

    FUNC func_name[(formal_parameter_list)] [OF NumType] [CLOSED]
      program_statements
    ENDFUNC func_name

  or

    FUNC int_func_name#[(formal_parameter_list)] [CLOSED]
      program_statements
    ENDFUNC int_func_name#

  or

    FUNC str_func_name$[(formal_parameter_list)] [CLOSED]
      program_statements
    ENDFUNC str_func_name$


  The default type in the first form is FLOAT. The value returned by a func-
  tion is calculated in a function RETURN statement of the form:

    RETURN expression


  The expression  must match with the function type and there have to be at
  least one function RETURN line in a function.

  The program statements inside the functions are activated  by placing the
  function name in an expression.

  Examples:

    PRINT 1-errf(1.5)
    y:=sinh(x)/cosh(x)


  Except for the returned value, there is no difference between functions and
  procedures. The sections 3.1.1 to 3.1.5 holds for functions, too.

  Example: The following function gcd calculates the greatest common divisor
    of two integer numbers

      FUNC gcd(m OF LONG,n OF LONG)
        IF (m MOD n)=0 THEN
          RETURN n
        ELSE

                                         82





          RETURN gcd(n,m MOD n)
        ENDIF
      ENDFUNC gcd


  Example: The following function WaitKey$ waits for special keys to be en-
    tered

        :
      PRINT "Yes or No (Y/N)? ",
      IF WaitKey$("YyNn") IN "Yy" THEN
        :
      ELSE
        :
      ENDIF
        :

      FUNC WaitKey$(WaitChars$)
        LOCAL ch$ OF 1
        REPEAT
          ch$:=INKEY$
        UNTIL ch$ IN WaitChars$
        PRINT ch$,
        RETURN ch$
      ENDFUNC WaitKey$


  Example: The following function  PenColor$ returns  a string  that can be
    used to set the drawing color of the console

        :
      PRINT PenColor$(3),"A nice color",PenColor$(1)
        :

      FUNC PenColor$(col OF UBYTE)
        RETURN CHR$($9B)+STR$(30+(col MOD 7))+"m"
      ENDFUNC PenColor$


  4 Exception handling.

  This section describes how exceptions such as user break and run time er-
  rors may be handled.

  4.1 The TRAP statement.

  Normally a run time error such as Division by zero or Out  of memory will
  result in  a program  stop. This  can be prevented by the TRAP statement.
  The format of a TRAP statement is


                                         83





      TRAP
        statements_1
      HANDLER
        statements_2
      ENDTRAP


  If an error is detected during the execution af statements_1 then the seg-
  ment statements_2  will be executed. Otherwise the program continues with
  the first statement after ENDTRAP.


  Example: In this example two files are used. If an error is detected during
    the opening of the second file the first one has to be closed before the
    the program stops

      OPEN FILE 1,File1$,WRITE
      TRAP
        OPEN FILE 2,File2$,READ
      HANDLER
        CLOSE FILE 1
        REPORT
      ENDTRAP

        :



  In connection with the handling of errors a number of error handling state-
  ments an convenient system functions are available:

      REPORT [ErrNum,[ErrText$]]

        This statement can be used to generate an error.

        If used  alone the error reported will be the error that caused the
        HANDLER section to be executed.

        If ErrNum is present the error text connected to this number will be
        reported.

        If ErrText$ is present this test will be reported.


      RETRY

        This statement directs the execution back to the first statement in
        statemens_1.



                                         84





      ERR

        A function that returns the error number of the error detected.


      ERRFILE

        A function that returns the number of the  file in  question if the
        error occurs in connection with a file operation.


      ERRTEXT$

        A function that returns the string containing the error message that
        the system would have generated.


  Example:  This piece of code reads  a  number  using  the  standard input
    statement INPUT. If the input is not a legal number the screen is flashed
    and the input statement is reexecuted:

        TRAP
          INPUT AT 5,10: "Enter a number: ": Num
        HANDLER
          PRINT CHR$(7),
          RETRY
        ENDTRAP



  4.2 The TRAP ESC statement.

  If the user presses the Esc-key (while the execute window  is active) the
  program will normally be breaked.

  The TRAP  ESC statement  is used  to control the operation of user breaks
  from within a Comal program.

  By executing TRAP ESC+ a user  break will  be handled  in the  normal way
  (the execution is interrupted).

  By executing TRAP ESC- the following will occur:

    - Ppressing the Esc-key will no longer interrupt a running program.

    - The system  function ESC  equals FALSE  (=0) until  the user tries to
      break the program; then it equals TRUE (=1).

  Thus a Comal program can use the value of ESC to check for user break.


                                         85





  Note, that the selection of the Stop Execution  item in  the Program menu
  cannot be masked, i.e. the program will always be stopped by doing this.



  5 IO statements.

  In this section the different forms of IO statements will be described. The
  most frequent used IO statements are PRINt and INPUT.


  5.1 The PRINT statement.

  The general format of the output statement PRINT is

    PRINT [AT Row,Col:] [USING Format_text:] [print_list] [mark]

   
  Simple PRINT statement

    The simplest for af the PRINT statement has the form

      PRINT print_list [mark]

    where print_list is one ore more of the following separated by either a
    comma (,) or a semicolon (;)

      TAB(position)

        where position is a numeric expression with value from 1 to 255

      string expression

      number expression

    and mark is either a comma (,) or a semicolon (;).

    The use  of TAB will result in the output of spaces up to the specified
    position if this position is not already passed.

    The separator comma (,) has no influence on the output (it acts solely as
    a separator). The separator semicolon (;) outputs spaces up to the next
    ZONE tabulation (the default ZONE tabulation is one resulting always in
    the output one extra space).

    If mark is not used a terminating line feed is output. If present it acts
    like the separator.




                                         86





    Examples:

      PRINT 2;-3
      PRINT "x=",x;"y=",y
      PRINT x,TAB(8),
      PRINT y,TAB(16)
      PRINT SIN(x);
      PRINT             // Output an empty line


  Formatted output with PRINT USING

    The PRINT USING statement allows formatted  output. This  statement has
    the format:

      PRINT USING Format_text: [print_list] [mark]

    where Format_text is a normal text expression containing the format cha-
    racters '#' '.' and '-' and print_list is one ore more number expressions
    separated by comma (,).

    Examples:

      PRINT USING "Pi = #.####": PI
      FOR x=0 TO 2*PI STEP 0.2 DO
        PRINT USING "sin(#.#)=-#.####":x,SIN(x)
      ENDFOR x

    All the characters in the the format except the special formmat charac-
    ters are printed as is. The format fields in the text are replaced one by
    one by  the values of the number expressions in the Print_list. To make
    room for a sign a minus sign (-) has to precede the format characters.


  PRINT AT specified position in window

    The beginning of the output can be set to any position of the screen by
    means of the PRINT AT statement with the form

      PRINT AT Row,Col: [USING Format_text:] [print_list] [mark]


    Examples:

      PRINT AT 10,20: "Hello world!"
      PRINT AT 0,20: USING "Pi = #.####": PI


    If Row is zero the current line is used, and if Col is zero the current
    column is used.

                                         87







  5.2 The INPUT statement.

  The general format of the input statement INPUT is

    INPUT [AT Row,Col[,lenght]:] [Promt-string:] variable_list [mark]


  Simple INPUT statement

    The simple form of the INPUT statement is

      INPUT [Promt_string:] variable_list [mark]

    where variable_list is one ore more text or number variables separated by
    comma (,) and mark has the same effect as in the PRINT statement.


    Example:

      INPUT x,y,t$
      INPUT "Enter a text: ": t$


    If the Promt_string is present this will be printed. Otherwise a question
    mark (?) wil be printed. Then the cursor is turned on and  the user may
    type in the values for the variables. The input is terminated by pressing
    the <ENTER>  key. The  values entered  are assigned  one by  one to the
    variables in the variable list. A string variable will be assigned the rest
    of the line entered.


  INPUT AT specified position in window

    The beginning of the promt string can be set to any position of the win-
    dow by means of the INPUT AT statement with the form

      INPUT AT Row,Col: [Promt-string:] variable_list [mark]


    Example:

      INPUT AT 5,10: "Enter a number: ": Num


    If Row is zero the current line is used, and if Col is zero the current
    column is used (like PRINT AT).



                                         88





  Specifying the length of the input field

    By adding a lengthfield to the  AT part  of an  INPUT AT  statement the
    length of  the input field can be specified. The general format of this
    statement is

      INPUT AT Row,Col,Lenght: [Promt-string:] variable_list [mark]


    Example:

      INPUT AT 0,0,1: "Accept (Y/N)? ": Answer$


  5.3 Redirection of IO.

  Normally all IO goes through the console attached to the program. But it is
  possible to redirect input and/or output to another device or a file. This
  done by using the SELECT statement that has the form:

    SELECT Direction File$

  where Direction is INPUT, OUTPUT or  INOUT and  File$ is  either the name
  of a file or the name of one of the standard devices

    ds:   the console window (only output)
    kb:   the keyboard (only input)
    lp:   a printer (only output)


    Example:

      INPUT "Output on printer (Y/N)? ": Answer$

      IF "Yy" IN Answer$ THEN
        SELECT OUTPUT "lp:"     // Select printer
        MakeOutput
        SELECT OUTPUT "ds:"     // Output back to window
      ELSE
        MakeOutput
      ENDIF


  It is  possible to  add other devices than the three above (or replace an
  existing one). To do this you have to write device drivers for that device
  and then add the device to the device list. Normally these drivers is writ-
  ten in C, but it is possible to write them in Comal, too. See chapter VII.




                                         89





  5.4 System functions performing IO.

  A number of system function can be used to perform IO. These are


    FUNC KEY$

      By calling this string function the keyboard buffer is scanned. If there
      is no  key in  the buffer the empty string is returned. Otherwise the
      first key in the buffer is returned. Note that some  keys return more
      than a single character.

      The key returned (if any) is not echoed on the window.

      Example:

        PRINT "Press any key to continue"
        WHILE KEY$="" DO WAIT


    FUNC INKEY$

      This string  function does the same as KEY$ but if there is no key in
      the buffer the empty string is not returned. In stead the process will
      go to sleep until a key is pressed.

      The key returned is not echoed on the window.


    FUNC INKEY$(time_out)

      This string function does the same as INKEY$ but if there is no key in
      the buffer it will wait maximal time_out seconds for a keypress before
      continuing program  execution. If  no keypress is detected within the
      time limit the empty strin is returned.

      The key returned (if any) is not echoed on the window.


  5.5 Other IO related statements and functions.


    CURSOR Row,Col

      Statement used to place the cursor on the window.

      If Row is zero the current line is used, and if Col is zero the current
      column is used (like PRINT AT and INPUT AT).



                                         90





      Example:

        CURSOR 15,5



    CURCOL

      Function returning the current column position of the cursor.

      Example:

        CurX:=CURCOL



    CURROW

      Function returning the current row position of the cursor.

      Example:

        CurY:=CURROW



    PAGE

      Statement used to clear the screen or make a form feed (page eject) if
      the current output direction is the printer.

      Example:

        PAGE


    ZONE tab_interval

      Statement used to set the width of the  tab zone  on the  window (see
      description of the separator semicolon (;) in section 5.1)

      Example:

        ZONE 8

      At startup the zone value is 1.





                                         91





    ZONE

      Function used to return the current zone value.

      Example:

        OldZone:=ZONE



    DIGITS number_of_digits

      Statement used to specify the number of significant digits wich will be
      shown in any PRINT statement.

      Example:

        DIGITS 12

      At startup the number of digits is 10.


    DIGITS

      Function used to return the current  number of  digits used  in PRINT
      statements.

      Example:

        OldDigits:=DIGITS




  6 File statements.


  Comal saves  data on  external starage media using two different types of
  data files:

    - Sequential files

    - Random access files (direct files)

  Further more data can be saved in two different formats:

    - Binary format

    - ASCII format (text files)


                                         92






  6.1 Sequential binary files.

  In a sequential file one item is saved after another without regard for the
  space that each item occupies.

  A sequential  data file  has to  be opened before you can read from it or
  write to it. This is done by executing an OPEN statement of the format

    OPEN FILE FileNo,FileName$,OpenMode

  where FileNo is a number in the range 1..32767 used to identify  the file
  and OpenMode is one of the following:

    READ        file is opened for reading only
    WRITE       write only (create new file or overwrite existing file)
    READWRITE   both read and write access possible
    APPEND      write access (append to existing or create a new file)


  FileName$ is  the name  of an  AmigaDOS file  or the  name of  one of the
  standard devices introduced in section 5.3.  Normally the  READWRITE open
  mode is used only in connection with special devices such as a serial devi-
  ce.


  Example:

    OPEN FILE 2,"MyFile",APPEND
    OPEN FILE 1,"Data",READ


  Data is written to a sequential binary file using a WRITE statement of the
  form:

    WRITE FILE FileNo: write_list

  where write_list is one or more expressions or variables separated by com-
  mas (,) and FileNo is the number used in the OPEN statement.


  Example:

    WRITE FILE 1: Alfa,Text$,Beta
    WRITE FILE 9: 2+4,3.14159,"Amiga"+"DOS"


  It is possible to write a whole record or a whole array to a  file in one
  write statement.


                                         93






  Example:
    DIM Member OF Person
    DIM Club(100) OF Person

      :

    WRITE FILE 2: Member
    WRITE FILE 2: Club()      // Note the array indicator


  Data is  read from a sequential binary file using a READ statement of the
  form:

    READ FILE FileNo: read_list

  where read_list is one or more variables separated by commas (,) and File-
  No is the number used in the OPEN statement.


  Example:
    READ FILE 1: Alfa,Text$,Beta
    READ FILE 9: n,x,t$
    READ FILE 2: Member,Club()


  The type  of the variables used to read in data from a file must have the
  same type as the corresponding data written to the file. It is very impor-
  tant that you observe this rule. Comal has almost no way to test the type
  of the data saved on a file.

  It is recommended that you always use variables (and not  expressions) in
  the  WRITE  statements.  Then  you  can pair WRITE and READ statements by
  using the same variables:

    WRITE FILE 1: Alfa,Text$,Beta

      :

    READ FILE 1: Alfa,Text$,Beta


  When file operations have been completed the file must be closed by using
  a CLOSE statement of the form:

    CLOSE [FILE FileNo]


  If FileNo is specified only this file will be closed. Otherwise all currently
  opened files will be closed.

                                         94






  Example:

    CLOSE FILE 2
    CLOSE


  Example: This little program will print out the whole content of a file con-
    taining short integer numbers:

      DIM n OF SHORT

      OPEN FILE 1,"Numbers",READ
      WHILE NOT EOF(1) DO
        READ FILE 1: n
        PRINT n
      ENDWHILE
      CLOSE FILE 1


    The function EOF used in the program is discussed in section 6.4.


  6.2 Random access binary files.

  In a random access file data items is stored on fixed positions in the file.
  A random access file is some times refered to as a direct file, because a
  specific number of bytes is reserved for each record (a group of data items)
  allowing direct access to a record when its number is known.

  A random access file must exist before you can open it (in contradiction to
  sequential files opened in WRITE or APPEND mode). A random access file is
  created by using a CREATE statement:

    CREATE FileName$,NoOfRecords,RecordLength


  where FileName$ is the name of the file you are going to create and NoOf-
  Records is the total number of records each one having the length Record-
  Length.

  The statement creates a file containing NoOfRecords records numbered from
  1 to NoOfRecords). The file is filled with CHR$(0).


  Example:

    CREATE "Numbers",100,12   // 100 records each holding one long integer
                              // .. and one float
    CREATE "Addresses",50,27  // 50 records each holding a text of max. 25

                                         95







  When you  are going  to create  a data  file you  have to decide how many
  records you need and to caclulate the necessary  size of  the records. To
  make this  calculation you  have to  use this  information about how many
  bytes each data item occupies in the record:

    - a text item occupies the actual length of the text (returned by the LEN
      function) plus two. The last two bytes is used to hold the actual length
      of the text.

    - all other data items occupies the size returned by the function SIZE.


  For the simple data types the SIZE function will return:

    UBYTE and BYTE:   1
    USHORT and SHORT: 2
    ULONG and LONG:   4
    FLOAT:            8


  Example: To create a file that is to hold 200 records each holding the con-
    tent of the structure

        STRUC Person
          DIM Name$ OF 30
          DIM Address$ OF 30
          DIM City$ OF 20
          DIM Age of UBYTE
        ENDSTRUC Person

    you could do the following:

        DIM Person of Person
        CREATE "PersonFile",200,SIZE(Person)



  A random  access binary  file is opened by executing an OPEN statement of
  the form

    OPEN FILE FileNo,FileName$,RANDOM RecordLength


  where FileNo and FileName$ is the  same as  discussed in  connection with
  sequential files and RecordLength is the size of the records in the file (the
  same size as was used to create the file).



                                         96





  Example:
    OPEN FILE 1,"Numbers",RANDOM 4
    OPEN FILE 2,"Addresses",RANDOM 27
    OPEN FILE 3,"PersonFile",RANDOM SIZE(Person)


  Data is written to a random access binary file using a WRITE statement of
  the form:

    WRITE FILE FileNo,RecordNo[,Offset]: write_list

  where FileNo  and write_list  is the same as discussed in connection with
  sequential files and RecordNo is the number of the record (a positive inte-
  ger) you want to update.

  If Offset is present the writing starts Offset bytes from the start of the
  record (default is zero). The Offset option is rarely used.

  Examples:
    WRITE FILE 1,51: n,x
    WRITE FILE 2,1: Address$

  Data is read from a random access binary file using  a READ  statement of
  the form:

    READ FILE FileNo,RecordNo[,Offset]: read_list

  where FileNo and read_list is the same as discussed in connection with se-
  quential files and RecordNo is the number of the record (a positive integer)
  you want to read.

  If Offset is present the reading starts Offset bytes from the start of the
  record (default is zero). The Offset option is rarely used.

  Examples:

    READ FILE 1,51: n,x
    READ FILE 2,1: Address$


  When file operations have been completed the file must be closed by using
  a CLOSE statement of the same form as discussed in the preceding section.



  6.3 ASCII files.

  ASCII files (text files) are always sequential. An ASCII file is opened in the
  same way as a sequential binary file, e.i. executing a statement of the form:


                                         97





    OPEN FILE FileNo,FileName$,OpenMode

  where FileNo is a number in the range 1..32767 used to identify  the file
  and OpenMode is one of the following:

    READ        file is opened for reading only
    WRITE       write only (create new file or overwrite existing file)
    READWRITE   both read and write access possible
    APPEND      write access (append to existing or create a new file)


  FileName$ is  the name  of an  AmigaDOS file  or the  name of  one of the
  standard devices introduced in section 5.3.  Normally the  READWRITE open
  mode is used only in connection with special devices such as a serial devi-
  ce.


  Example:

    OPEN FILE 2,"MyFile",APPEND
    OPEN FILE 1,"Data",READ
    OPEN FILE 7,"SER:",READWRITE    // "SER:" is the AmigaDOS serial device
    OPEN FILE 5,"lp:",WRITE         // "lp:" is the printer device


  Data is written to an ASCII file using a PRINT statement of the form:

    PRINT FILE FileNo: [USING Format_text:] [print_list] [mark]

  where FileNo is the number used in the  OPEN statement.  The rest  of the
  statement has the same form as a PRINT statement discussed in section 5.1.


  Example:

    PRINT FILE 1: Alfa,Text$
    PRINT FILE 5: USING "n = #### x = ##.####": n,x


  Data is written from an ASCII file using an INPUT statement of the form:

    INPUT FILE FileNo: variable_list [mark]

  where FileNo  is the  number used  in the OPEN statement. The rest of the
  statement has the same  form as  a INPUT  statement discussed  in section
  5.2.





                                         98





  Example:

    INPUT FILE 1: Alfa,Text$
    INPUT FILE 5: n,x


  When file operations have been completed the file must be closed by using
  a CLOSE statement of the same form as discussed in the preceding sections.


  Example: This little program will print out the whole content of a text file:

      DIM Line$ OF 150

      OPEN FILE 1,"Text",READ
      WHILE NOT EOF(1) DO
        INPUT FILE 1: Line$
        PRINT Line$
      ENDWHILE
      CLOSE FILE 1

    The function EOF used in the program is discussed in next section.



  6.4 File system related functions.

  A number of system functions are used in connection with files. These are


      EOF(FileNo)

        This function returns TRUE if the internal file pointer of the file
        opened with file number FileNo is positioned at the end of the file
        (EOF = End Of File). Otherwise it returns FALSE

        EOF returns TRUE as soon as the last  data item  are read  from the
        file. If an empty file is opened a call to EOF will return TRUE be-
        fore any reading at all.

        The following code fraction shows a safe way to read the whole con-
        tent of a (possibly empty) file

            OPEN FILE 1,"Name",READ
            WHILE NOT EOF(1) DO
                :
              <do your reading>
                :
            ENDWHILE
            CLOSE FILE 1

                                         99







      FREEFILE

        Returns the next available file number. This function is useful in li-
        brary procedures/functions which are to be used in various programs.

        Example:

          DIM FileNo OF SHORT

          FileNo:=FREEFILE
          OPEN FILE FileNo,FileName$,APPEND
            :


      GET$(FileNo,NumberOfCharaters)

        This string function fetches NumberOfCharaters characters  from the
        file opened with file number FileNo. The length of the string retur-
        ned will be NumberOfCharaters unless the end of file is reached first.
        In this case the string will only be those characters retrieved prior
        to the end of file.

        Example: This little program can be used to make a copy of any file
          (even a binary file which is not a Comal file)

            DIM Block$ OF 1000

            OPEN FILE 1,Source$,READ
            OPEN FILE 2,Destination$,WRITE
            WHILE NOT EOF(1)
              Block$=GET$(1,1000)
              PRINT FILE 2: Block$,
            ENDWHILE
            CLOSE

          Note the comma at the end of the PRINT FILE statement!


  6.5 READ and DATA statements.

  The READ  statement is  used to  read internal data stored in one or more
  data statements.

  The format of the READ statement is:

    READ read_list

  where read_list is one or more variables separated by commas (,).

                                         100






  When a READ statemet is executed the variables in the  READ statement are
  assigned values from the internal data queue formed by DATA statements in
  the program.

  The format of a DATA statement is:

    DATA data_list

  where data_list is one ore more number or string expressions separated by
  commas (,).


  Example: After  the execution of the DATA statement in the following pro-
    gram fraction the variable a will have the value 5, b the value 7, c the
    value 3 and t$ the value "A string" (without the quotes).

      READ a,t$,b,c

        :

      DATA 5
      DATA "A string",7,3


  It is possible to fill a whole array in one read.


  Example: The following two program fractions are equivalent

      DIM a(3,4)

      FOR i:=1 TO 3 DO
        FOR j:=1 TO 4 DO
          READ a(,)
        ENDFOR j
      ENDFOR i

        :
        :

      DATA 1,2,3,4
      DATA 5,6,7,8
      DATA 9,10,11,12

    and

      DIM a(3,4)

      READ a(,)

                                         101






        :
        :

      DATA 1,2,3,4
      DATA 5,6,7,8
      DATA 9,10,11,12


  The values are read one by one in the order the DATA constants are placed
  in the program. It is possible to change  this order  by using  a RESTORE
  statement of the form:

      RESTORE label

  where label is a label is defined in a label line (see section 2.3.1).


  Example:

      READ a,b
      RESTORE d
      READ c

      DATA 1
      d:
      DATA 2


  If you  are trying to read beyond the last data constant in the queue the
  program will stop with an error message. It is possible to test for the end
  of the  data queue  by using the function EOD (End Of Data). The function
  returns true (1) if there is no more data in the queue. Otherwise it returns
  false (0).

  The READ  and DATA statements are useful to initialize arrays or structu-
  res.


  Example: The  NewWindow structure  used to  open an  Intuition window can
    be initialized in this way:

      DIM NewWindow OF NewWindow

      READ NewWindow

        :

      DATA 10,20,400,100,-1,-1  // Potition, size and pen colors
      DATA 0                    // No IDCMP flags

                                         102





      DATA WINDOWSIZING BITOR WINDOWDEPTH BITOR WINDOWDRAG
      DATA 0,0                  // Gadget and image
      DATA ADR("My window")     // Window title
      DATA 0,0                  // Screen and bitmap
      DATA 100,40,640,200       // Max and min size
      DATA WBENCHSCREEN         // Type

        :



  7 Miscellaneous statements, procedures and functions.
  

  7.1 DOS statements and functions.

  A number  of statements  can be  used to execute AmigaDOS commands. These
  statements are:

    CAT

      CAT is synonymous for DIR. CAT is relisted as DIR.


    CD

      CD is synonymous for CHDIR. CD is relisted as CHDIR.


    CHDIR

      Change the current directory. The format of the statement is:

        CHDIR path

      where path is a string expression.

      The statement works as the shell command CHDIR.

      Example:

        CHDIR "Demos"
        CHDIR "/"
        CHDIR Path$+"/Example"

    COPY

      Make a copy of a file. The format of the statement is:

        COPY OldFile,NewFile

                                         103






      where OldFile and NewFile are string expressions.

      The statement works as  the shell  command COPY  except that  the new
      name must be specified.

      Example:

        COPY "Demos/Hanoi","Ram:Hanoi"


    DELETE

      Remove a file from disk. The format of the statement is:

        DELETE FileName

      where FileName is a string expression.

      Example:

        DELETE "ram:Temp"


    DIR

      Output a  directory list  of a volume or directory. The format of the
      statement is

        DIR path

      where path is a string expression.

      The statement works as the shell command DIR.

      Example:
        DIR                 // The current directory is listed
        DIR "Modules"       // The directory Modules is listed
        DIR "df0:"+Drawer$
        DIR DIR$            // Same as just DIR


    DIR$

      String function returning the complete path of the current directory.
      The path string will always end with a colon (:) or a slash (/).

      Example:
        PRINT DIR$
        Path$:=DIR$

                                         104







    MAKEDIR

      Synonymous for MKDIR. MAKEDIR is relisted as MKDIR.


    MKDIR

      Statement used  to create  a new subdirectory (drawer). The format of
      the statement is

        MKDIR name

      where name is a string expression.

      The statement works as the shell command MAKEDIR.

      Example:

        MKDIR "Comal:AsciiFiles"


    PASS

      Statement used to execute shell commands directly. The format is

        PASS command

      where command is a string expression.  As a  result of  executing the
      command a  window will  open in  the Workbench screen and the command
      will be executed with this window as the output window. After the exe-
      cution of  the command  you will  be requested to press enter to con-
      tinue.

      If the  command string  commadn is  the empty  string a  new Shell is
      opened. Type  ENDCLI (or  press the close gadget in WB 2.x) to return
      to your Comal program.

      Example:

        PASS "RELABEL df0: NewName"
        PASS ""


    RENAME

      Statement used to give  a disk  file a  new name.  The format  of the
      statement is:


                                         105





        COPY OldName,NewName

      where OldName and NewName are string expressions.

      The statement works as the shell command RENAME.

      Example:

        RENAME "Demos/Hanoi","Demos/Towers"


    UNIT

      Synonymous for CHDIR. UNIT is relisted as CHDIR.


    UNIT$

      String function  returning the name of the volume containing the cur-
      rent directory.

      Example:

        PRINT UNIT$
        Volume$:=UNIT$


  7.2 Time related statements and functions.


    DATE$

      String function returning the current date. The string returned has the
      format yyyy-mm-dd.


    TIME$

      String function returning the current time. The string returned has the
      format hh:mm:ss.


    TIMER

      Statement used to set the value of an internal constant running timer.

      Example:

        TIMER 0         // Reset the timer


                                         106






    TIMER

      Number function used the return the current value of the internal ti-
      mer.

      Example:

        PRINT TIMER


    WAIT

      Statement used  to make  your process sleep. The format of the state-
      ment is:

        WAIT [delaylength]

      where delaylength is a number expression.

      If delaylength is specified the process will sleep for the specified num-
      ber of seconds. If no delaylength is specified the process will sleep
      until an event occurs (for instance a key is pressed).

      Example:

        WAIT 5                    // Sleep in 5 seconds
        WHILE KEY$="" DO WAIT



  7.3 Random numbers.

  The function RND is used to generate pseudo  random numbers.  The are two
  forms of the function RND:

    RND

      Used without  arguments the  function RND  returns a (pseudo) randaom
      number in the interval [0;1[ (1 not included).


    RND(min,max)

      Used with arguments an  integer random  number greater  than or equal
      min and less than or equal max is returned.


  The random  numbers are read from a random number table (the table values
  are calculated one by one). To make Comal start a new place in this table

                                         107





  the  RANDOMIZE  statement  may  be  used.  The  format  of  the RANDOMIZE
  statement is

    RANDOMIZE [seed]


  If seed (a numeric expression) is not present the Amiga timer is  used to
  generate the seed.


  Example: The  following program  will return  the same  random number se-
    quence each time it is run:

      RANDOMIZE 100

      LOOP 10 TIMES
        PRINT RND
      ENDLOOP



  7.4 STOP and END statements.

  A Comal program will stop after the last statement of the program  is ex-
  ecuted. To  make the  program stop before the last statement the STOP and
  END statements may be used. The format of these statements is:

    STOP [message]
    END [message]

  where message is a string expression whose value is printed in the status
  line of the editor.

  The  most  importent  difference  between  the STOP and END statements is
  that the program execution cannot be continued after execution of the END
  statements.

  If the STOP statement is executed the cursor of the editor is placed after
  the STOP and the  message STOP  statement is  executed is  printed in the
  status line (unless message is present).


  Example:

    STOP "Error while opening the file"
    END





                                         108





  7.5 Interrupt procedures.

  It is possible to make interrupt procedures in Comal. An interrupt procedure
  is a procedure with the format:

    PROC Interrupt(SigMask OF ULONG) CLOSED


  To activate the interrupt procedure a call must be made to the Comal pro-
  cedure ADDINTERRUPT:

    ADDINTERRUPT(Interrupt(),SigMask)

  or

    ADDINTERRUPT(Interrupt(),SigMask,Priority)


  where the  parameter SigMask is a mask containing the signals that should
  cause interrupt (more about signals in the Amiga system manual). Interrupt
  are prioritized. In the second version the parameter Priority the priority
  sets this priority (-128 .. 127). The default priority used in the first version
  is 0.

  To deactivate  the interrupt  procedure a  call must be made to the Comal
  procedure REMINTERRUPT:

    REMINTERRUPT(Interrupt())



  7.6 Mathematical functions.


    ABS         absolute value

      ABS(x) returns the absolute value (numerical value) of the argument x
      which may be any real number.


    ACS         arccosine

      ACS(x) returns  the arccosine in radians of the argument x which is a
      real number in the range from -1 to 1.


    ASN         arcsine

      ASN(x) returns the arcsine in radians of the  argument x   is  a real
      number in the range from -1 to 1.

                                         109






    ATN         arctangent

      ATN(x) returns  the arccosine  in radians of the argument x which may
      be any real number.


    COS         cosine

      COS(x) returns the cosine of the argument x measured in radians.


    EXP(x)      natural exponential function

      EXP(x) returns the number e (=2.718281828..) raised to the power of the
      argument x.


    FLOAT

      FLOAT(i) returns a floating point number equalto the argument i which
      is normally an integer (but may be any number).


    INT

      INT(x) returns the largest integer integer which is less than or equal
      to the argument x.


    LOG         natural logarithm

      LOG(x) returns the natural logarithm of the argument x which is a po-
      sitive real number.


    PI

      PI is a constant function returning the number pi (=3.1415592655...).


    ROUND

      ROUND(x) returns the integer which is closest to the argument x.


    SGN         sign

      SGN(x) returns -1 if the argument x is negative, 0 if it is zero and +1
      if x is positive.

                                         110







    SIN         sine

      SIN(x) returns the sine of the argument x measured in radians.


    SQR         square root

      SQR(x) returns the square root of a non negative argument x.


    TAN         tangent

      TAN(x) returns the tangent of the argument x measured in radians.



  7.7 Functions involving strings.


    CHR$

      CHR$(x) returns a string containing the character with ASCII value x.


    LEN         string length

      LEN(t$) returns the length (number of character) of the string expres-
      sion used as argument.


    ORD         ordinal value

      ORD(t$) returns an integer representing the ASCII value  of the first
      character of the string expression used as argument. An error will re-
      sult if the empty string is used as argument.


    SPC$

      SPC$(n) returns the number of blanks (spaces) specified  by the argu-
      ment n.


    STR$

      STR$(num) returns  the string equivalent of the numeric argument num.
      The number of significant digits are set by the DIGITS statement.


                                         111





      STR$(format$,num) returns  the string  equivalent of  the numeric ar-
      gument num.  The string  argument format$ specifies the format of the
      string equivalent. See description of PRINT USING in  section 5.1 for
      further details.

      Example: The output from

          PRINT STR$("#.###",pi)

        will be

          3.142


    VAL

      VAL(t$) returns the numeric equivalent of a string representing a num-
      ber. VAL and STR$ can be concidered as inverse of each other.



  7.8 Other functions.


    ADR

      Function returning the address of the data field of a variable, the code
      of a procedure/function or the address of the text in a text constant.

      Example:

        ADR(NewWindow)
        ADR("My window")

    FREE

      FREE returns the number of free bytes in the work space.


    SIZE

      SIZE(var) returns the number of bytes occupied byt the variable var.









                                         112





  8 Objects.

  Very often  a computer  program can  be viewed  of as a model of the real
  world. In these cases a very powerful program design strategi is to base the
  structures of the program on the structures of the part of the world being
  modeled. For each object in the real world we should  try to  construct a
  corresponding computational object. Such a design strategi is called Object
  Oriented Programming design strategi (or short OOP).

  OOP is still rather unknown and section 8.1 is an introduction to this pro-
  gramming strategi. The sections 8.2-8.4 contains a more formal description
  of objects.


  8.1 Introduction to OOP.

  As an example to OOP we are going to make  a simple  model of  a bank ac-
  count. A  bank account is identified by a number (an ID code), there is a
  balance and an interest. All together these quantities are characterizing a
  specific bank account and it would be natural to put them into a structure:

    STRUC BankAccount
      Id OF ULONG
      Balance OF FLOAT
      Interrest OF FLOAT
    ENDSTRUC BankAccount


  Operations can be made on a bank account. In our simple model this is de-
  positing and withdrawal of money which can be implemented by  a procedure
  and a function:


    PROC Deposit(REF Account OF BankAccount,Amount)
      Account.Balance:+Amount
    ENDPROC Deposit

    FUNC Withdraw(REF Account OF BankAccount,Amount) OF SHORT
      IF Account.Balance-Amount>=0 THEN
        Account.Balance:-Amount
        RETURN TRUE
      ELSE
        RETURN FALSE
      ENDIF
    UNDFUNC Withdraw


  The function  Withdraw returns  true if  there is enough money on the ac-
  count to accomodate the withdrawal. Otherwise it returns false.


                                         113





  With these definitions bank accounts  can  be  created  by  executing DIM
  statements:

    DIM MyAccount OF BankAccount
    DIM YourAccount OF BankAccount

  and operations can be made through the procedure Deposit and the function
  Withdraw:

    Deposit(MyAccount,1000)

    IF Withdraw(YourAccount,1750) THEN
      PRINT "New balance: ",YourAccount.Balance
    ELSE
      PRINT "Insufficient founds"
      PRINT "Balance is: ",YourAccount.Balance
    ENDIF


  The operations Deposit and  Withdraw are  closely bound  to the structure
  BankAccount and it would be natural to put them into the structure defini-
  tion itself. This is in fact possible and can be done by defining BankAc-
  count like:

    STRUC BankAccount
      Id OF ULONG
      Balance OF FLOAT
      Interrest OF FLOAT

      PROC Deposit(Amount)
        Balance:+Amount
      ENDPROC Deposit

      FUNC Withdraw(Amount) OF SHORT
        IF Balance-Amount>=0 THEN
          Balance:-Amount
          RETURN TRUE
        ELSE
          RETURN FALSE
        ENDIF
      UNDFUNC Withdraw

    ENDSTRUC BankAccount



  Procedures and  functions defined inside a structure are sometimes called
  methods. Methods cannot be closed but are otherwise normal procedures and
  functions.


                                         114





  With this new definition bank accounts are still created by executing DIM
  statements:

    DIM MyAccount OF BankAccount
    DIM YourAccount OF BankAccount

  and operations can be made through the metods using the dot notation:

    MyAccount.Deposit(1000)

    IF YourAccount.Withdraw(1750) THEN
      PRINT "New balance: ",YourAccount.Balance
    ELSE
      PRINT "Insufficient founds"
      PRINT "Balance is: ",YourAccount.Balance
    ENDIF


  By using methods everything that has to do with the  bank account  is put
  into the structure definition. This is callded encapsulation and is one of the
  central elements in OOP. Another important element is the use  of the in-
  heritance mechanism to conveniently derive new types from existing types.

  A cash credit is a bank account where the balance is allowed to be negati-
  ve (until some predefined negative value). Apart from this it shares all the
  proporties of a normal bank account. In making a model of a cash credit it
  would be natural to try to  reuse BankAccount.  This may  be done  in the
  following way where BankAccount is inherited:

    STRUC CashCredit
      INHERIT BankAccount

      DIM MinBalance OF FLOAT

      FUNC Withdraw(Amount) OF SHORT
        IF Balance-Amount>=0 THEN
          Balance:-Amount
          RETURN TRUE
        ELSE
          RETURN FALSE
        ENDIF
      UNDFUNC Withdraw

    ENDSTRUC CashCredit



  This new definition of the bank account type can be used like the old one
  and at the same time:


                                         115





    DIM MyAccount OF BankAccount
    DIM YourAccount OF BankAccount
    DIM Cash OF CashCredit

    MyAccount.Deposit(1000)
    Cash.Deposit(2000)

    IF Cash.Withdraw(1750) THEN
      PRINT "New balance: ",Cash.Balance
    ELSE
      PRINT "Insufficient founds or illegal password"
      PRINT "Balance is: ",Cash.Balance
    ENDIF


  Note that the new function Withdraw in CashCredit  overloads the original
  Withdraw function  in BankAccount. All other fields or methods of BankAc-
  count are inherited and can be used as if they were defined inside the new
  type CashCredit.

  There is another way to reuse BankAccount in the new CashCredit. First we
  have to redefine the structure BankAccount:

    STRUC BankAccount
      Id OF ULONG
      Balance OF FLOAT
      Interrest OF FLOAT

      PROC Deposit(Amount)
        Balance:+Amount
      ENDPROC Deposit

      FUNC Withdraw(Amount) OF SHORT
        Balance:-Amount
        IF Proceed THEN
          RETURN TRUE
        ELSE
          Balance:+Amount
          RETURN FALSE
        ENDIF
      UNDFUNC Withdraw

      FUNC Proceed OF SHORT VIRTUAL
        RETURN Balance>=0
      ENDFUNC Proceed

    ENDSTRUC BankAccount




                                         116





  The legitimacy of the withdraw transaction is tested by the function Pro-
  ceed which is declared as VIRTUAL. The meaning of this will be clear soon.
  Note that allthough BankAccount has been redefined all the code  that has
  used BankAccount until now need not be rewritten.

  Now the CashCredit structure can be defined in this way:

    STRUC CashCredit
      INHERIT BankAccount

      DIM MinBalance OF FLOAT

      FUNC Proceed OF SHORT
        RETURN Balance>=MinBalance
      ENDFUNC Proceed

    ENDSTRUC CashCredit


  To se  how this  works let  us make  one normal bank account and one cash
  credit by executing the statements:

    DIM Account OF BankAccount
    DIM Cash OF CashCredit


  If the statement

    IF Cash.Withdraw(750) THEN

  is executed the method Withdraw of  the structure  BankAccount is called.
  Inside this method another method Proceed is activated. But since Proceed
  in BankAccount is declared as VIRTUAL it is not this one that is called. In
  stead the method Proceed defined in CashCredit is called and this version
  of Proceed compares Balance with MinBalance in stead of comparing it with
  zero.

  On the other hand, if the statement

    IF Account.Withdraw(750) THEN

  is executed the method Withdraw of the structure Account is again called.
  But this time the object BankAccount has no descendants and it is the ori-
  ginal method Proceed inside BankAccount that is activated.

  As a final example let us define a special password-protected bank account
  where a password has  to be  supplied to  withdraw money.  This new pass-
  word-protected bank  account shares all the properties of the simple bank
  account. These properties are inherited from BankAccount in the following
  definition of ProtectAccount:

                                         117






    STRUC ProtectAccount
      INHERIT BankAccount

      DIM Password$ OF 10

      FUNC Proceed OF SHORT
        LOCAL pw$ OF 10

        INPUT "Enter password: ":pw$
        IF pw$=Password$ THEN
          RETURN BankAccount.Proceed
        ELSE
          PRINT "Illegal password"
          RETURN FALSE
        ENDIF
      UNDFUNC Proceed

    ENDSTRUC BankAccount



  8.2 Methods.

  An object type is a STRUC type (discussed in section 1.3.2) extended with
  one or more procedures or functions. The general format of an object type
  definition is

    STRUC Identifier

      [USE statements]

      DeclarationStatements

    ENDSTRUC Identifier

  where DeclarationStatements is one or more of the following

    - structure DIM statement (see section 1.3.2)

    - open procedure

    - open function



  Procedures and  functions in  an object  type are called methods. Methods
  must be open but are otherwise normal procedures or functions.



                                         118





  Example:

    STRUC Person
      DIM FirstName$ OF 20
      DIM LastName$ OF 20
      DIM Address$ OF 30
      DIM City$ OF 20
      DIM Age of UBYTE

      PROC Input
        INPUT "First name: ": FirstName$
        INPUT "Last name:  ": LastName$
        INPUT "Address:    ": Address$
        INPUT "City:       ": City$
        INPUT "Age:        ": Age
      ENDPROC Input

      PROC Print
        PRINT "First name: ", FirstName$
        PRINT "Last name:  ", LastName$
        PRINT "Address:    ", Address$
        PRINT "City:       ", City$
        PRINT "Age:        ", Age
      ENDPROC Print

    ENDSTRUC Person


  A object variable or a pointer  to an  object must  dimensioned in  a DIM
  statement as normal data structures discussed in section 1.3.


  Example:

    DIM Member OF Person
    DIM Club(100) OF Person
    DIM PersonPtr OF POINTER TO Person

    ALLOCATE(PersonPtr)


  Methods are activated by using the dot notation on the variable.


  Example:

    FOR i:=1 TO 100
      Club(i).Print
    ENDFOR i
    PersonPtr@.Input

                                         119







  The scope of a method is the fields and the other methods inside the actual
  object.


  Example: The scope of Club(23).Print is the method  Input and  the fields
    FrstName$, LastName$, Address$, City$ and Age of Club(23).


  An object  type is  a true  extension of the data structure STRUC so that
  everything that can be done with  a data  structure can  be done  with an
  object type.



  8.3 Inheritance.

  In the  definition of an object type it is possible to inherit fields and
  methods from a previous defined object types. This is done  by placing an
  INHERIT line in the start of the structure definition.


  Example:  In a  school you have pupils and teachers. They are all persons
    but have different additional properties. Pupils are members of a class
    and have  parents, while the teachers teaches in special subjects (like
    mathematics, computer sience, physics etc.) and they have their monthly
    salary.

    Two object types Pupil and Teacher can be defined in this way:

      STRUC Pupil
        INHERIT Person

        DIM Class OF BYTE
        DIM Mother$ OF 30
        DIM Father$ OF 30

        PROC Print
          Person.Print
          PRINT
          PRINT "Class:  ",Class
          PRINT "Mother: ",Mother$
          PRINT "Father: ",Father$
        ENDPROC Print

      ENDSTRUC Pupil

      STRUC Teacher
        INHERIT Person

                                         120






        DIM Subject OF UBYTE
        DIM Salary OF FLOAT

        PROC Print
          Person.Print
          PRINT
          PRINT "Subject: ",Subject
          PRINT "Salary:  ",Salary
        ENDPROC Print

      ENDSTRUC Teacher


  All the fields and all the methods of the inherited are accessible from the
  descendant object type and from the outside world as if they were directly
  defined in this object type. If a method in the descendant object has the
  same name as a method in the ancestor object, the child method will over-
  load the  method in  the ancestor object. This is the case with the Print
  method in the example.  The overloaded  method may  be accessed  by using
  the dot notation.


  Example: A general stack type can be made in this way:

      STRUC Stack
        DIM StackTop OF NodePtr

        PROC Push(REF Element OF NodePtr)
          Element@.Next:=StackTop
          StackTop:=Element
        ENDPROC Push

        PROC Pop(REF Element OF NodePtr)
          Element:=StackTop
          IF StackTop<>0 THEN
            StackTop:=StackTop@.Next
        ENDPROC
      ENDSTRUC Stack

      STRUC StackNode
        DIM Next OF POINTER TO StackNode
      ENDSTRUC StackNode

      TYPE NodePtr=POINTER TO StackNode


    To store a someting on the stack, for example a long integer, we can de-
    fine a NumberNode:


                                         121





      STRUC NumberNode
        INHERIT StackNode
        DIM Num OF LONG
      ENDSTRUC NumberNode

    and then execute statements like:

      DIM Stack OF Stack
      DIM NumPtr OF POINTER TO NumberNode

      ALLOCATE(NumPtr)
      Stack.Pusk(NumPtr)

    The elements of the stack need not all be of  the same  type. They only
    need to be objects that are descendants of the StackNode object.


  A structure A is assignment compatible to a structure B, i.e. an assignment
  of the form A:=B can be made, if A and B have the same type or if A's ty-
  pe is  the ancestor  of B's  type. That's  why the variable NumPtr in the
  example can be used as the actual parameter to the  formal parameter Ele-
  ment in the method Push.


  8.4 Virtual methods.

  If the  Print method was called from the Input method in the Pupil object
  in the section 9.3, it would have been the ancestors Print method, i.e. the
  fields of the descendant (Class, Mother$ and Father$) would not be printed.

  It is possible to force the system to call the method of the youngest des-
  cendant (if any) by specifying the method as virtual. A virtual method is
  defined by using a procedure/function head of the form:

    PROC func_name[(formal_parameter_list)] VIRTUAL

  or 

    FUNC func_name[(formal_parameter_list)] [OF NumType] VIRTUAL

  Example: This example shows a definition of different figures constituing a
    class hierarchy. The point is the base figure. The circle and the ractangle
    shares proporties with this but each one has its own characteristics:

    STRUC Point
      USE Graphics

      DIM PosX OF FLOAT, PosY OF FLOAT
      DIM FigureColor OF BYTE


                                         122





      PROC Init(x,y,Color OF BYTE)
        PosX:=x; PosY:=y
        FigureColor:=Color
        pencolor(FigureColor)
        moveto(x,y)
        Draw
      ENDPROC Init

      PROC Move(dx,dy)
        pencolor(0)
        moveto(PosX,PosY)
        Draw
        pencolor(FigureColor)
        PosX:+dx; PosY:+dy
        moveto(PosX,PosY)
        Draw
      ENDPROC Move

      PROC Draw VIRTUAL   // Virtual method
        plot(PosX,PosY)   // Defines the form of the figure
      ENDPROC Draw

    ENDSTRUC Point

    STRUC Circle
      INHERIT Point

      USE Graphics

      DIM Radius OF FLOAT

      PROC Init(x,y,Color OF BYTE,R)
        Radius:=R
        Point.Init(x,y,Color)
      ENDPROC Init

      PROC Draw
        circle(PosX,PosY,Radius)
      ENDPROC Draw
    ENDSTRUC Circle

    STRUC Rectangle
      INHERIT Point

      USE Graphics

      DIM Height OF FLOAT, Width OF FLOAT

      PROC Init(x,y,Color OF BYTE,h,w)
        Height:=h

                                         123





        Width:=w
        Point.Init(x,y,Color)
      ENDPROC Init

      PROC Draw
        draw(Width,0)
        draw(0,Height)
        draw(-Width,0)
        draw(0,-Height)
      ENDPROC Draw

    ENDSTRUC Rectangle


    STRUC Square
      INHERIT Rectangle

      PROC Init(x,y,Color OF BYTE,s)
        Rectangle.Init(x,y,Color,s,s)
      ENDPROC Init

    ENDSTRUC Square


  If you are using pointers to structures containing virtual methods it may be
  necessary to store a method table in the data field of the structure. You
  have to tell Comal that you want this table by using the line

    METHODTABLE

  in the start of the structure.


  Example: This structure is the ScreenClass defined in the module CITScreen:


    STRUC ScreenClass
      METHODTABLE

      FUNC CreateObject(REF Screen OF Screen) OF SHORT VIRTUAL
        RETURN TRUE
      ENDFUNC CreateObject

      PROC DeleteObject(REF Screen OF Screen) VIRTUAL
      ENDPROC DeleteObject

    ENDSTRUC ScreenClass




                                         124





  NOTE: If a  method table  is stored  in the data field you cannot use the
  structure in a call to an external (C programmed) routine (for instance a
  system routine).


  8.5 Constructors.

  Sometimes an object needs to be initialized before it can be used.


  Example:  One of the basic structures of the Amiga operating system is the
    List structure:

      STRUC List
        DIM lh_Head OF POINTER TO Node
        DIM lh_Tail OF POINTER TO Node
        DIM lh_TailPred OF POINTER TO Node
        DIM lh_Type OF UBYTE
        DIM lh_pad OF UBYTE
      ENDSTRUC List

    which is a double ended list used to store nodes of the form:

      STRUC Node
        DIM ln_Succ OF POINTER TO Node
        DIM ln_Pred OF POINTER TO Node
        DIM ln_Type OF BYTE
        DIM ln_Pri OF BYTE
        DIM ln_Name OF ULONG
      ENDSTRUC Node

    Before the list can be used it has to be initialized to an empty list by
    executing statements like:

      lh_Head:=ADR(lh_Tail)
      lh_TailPred:=ADR(lh_Head)
      lh_Tail:=0


  If the initiatization of an object does not require parameters (like the ini-
  tialization of List in the example) it can be put into a method that is de-
  clared as  a constructor  method. A constructor method is a method with a
  head of the form:

    FUNC func_name CONSTRUCTOR


  Such a method is called automatically when the object is created as a static
  variable in  a DIM or LOCAL statement or is created as a dynamic variable
  by executing the ALLOCATE procedure.

                                         125






  A constructor returns a SHORT integer value to be considered as a boolean
  value. TRUE (=1) if the initialization succeded. FALSE (=0) otherwise.

  Example:  A smarter definition of the List object is the following:

      STRUC List
        DIM lh_Head OF POINTER TO Node
        DIM lh_Tail OF POINTER TO Node
        DIM lh_TailPred OF POINTER TO Node
        DIM lh_Type OF UBYTE
        DIM lh_pad OF UBYTE

        FUNC New CONSTRUCTOR
          lh_Head:=ADR(lh_Tail)
          lh_TailPred:=ADR(lh_Head)
          lh_Tail:=0
          RETURN TRUE
        ENDFUNC New

      ENDSTRUC List

    The List  in the  module ExecLists found in the directory SystemModules
    is defined in this way.


  If a field is an object containing a constructor this constructor is called,
  too. If  an object  inherits other  objects all the ancestors constructor
  methods are called when an object of that type is created. But you should
  note that  the virtual  method calling mechanism does not function at the
  initialization time (the descendants are not initialized until after all the
  ancestors have been initialized).

  If a constructor returns FALSE, all the destructors (see next section) of
  the sub objects (fields or inherited objects) that has been successfully ini-
  tialized, will be called.



  8.6 Destructors.

  If an object needs to do some cleaning up before its data fields are remo-
  ved you can declare a method as a  destructor. A  destructor method  is a
  method of the form:

    PROC proc_name DESTRUCTOR


  Such a  method is  called whenever  the memory  used by  the objects data
  field is removed. This will be the case if DEALLOCATE  is called  or at a

                                         126





  return from a procedure or function (if the object is a local variable). But
  a constructor may also be called when all the variables are cleared, i.e af-
  ter the program has stopped!

  Because destructors are called in many different sitiuations a destructor
  method should be as short as possible, it should do no IO and it should be
  free of errors!

  The best  way to  develop constructors is to first activate them manually
  (leave out the word CONSTRUCTOR). Then,  when  you  think  it  works, add
  the word CONSTRUCTOR.


  Example: One  of the  basic elements of the Amiga multy tasking system is
    the message port. A message port consists of a list node (such  that it
    can be put into the list of public ports if desired) and some fields.

    One of the fields contains the number of a signal bit that has to be allo-
    cated when the port is created and it  has to  be deallocated  when the
    port is  removed. Another field is a list where messages can be placed.
    Also this has to be initialized.

    The alocation of a signal bit can be done in a constructor and the deal-
    location in a destructor. A message port can be defined in this way:

      STRUC MsgPort
        INHERIT Node

        USE ExecLibrary
        USE ExecLists

        DIM mp_Flags OF UBYTE
        DIM mp_SigBit OF BYTE
        DIM mp_SigTask OF ULONG
        DIM mp_MsgList OF List

        FUNC Init CONSTRUCTOR
          ln_Type:=NT_MSGPORT
          mp_SigTask:=FindTask($0)
          mp_SigBit:=AllocSignal(-1)
          RETURN mp_SigBit>=0         // No signals if mp_SigBit<0
        ENDFUNC Init

        PROC Delete DESTRUCTOR
          IF ln_Name<>0 THEN
            RemPort(ADR(Node))
            ln_Name:=0
          ENDIF
          IF mp_SigBit>=0 THEN
            FreeSignal(mp_SigBit)

                                         127





          ENDIF
        ENDPROC Delete

      ENDSTRUC MsgPort

    Note that the list mp_MsgList is not initialized in the constructor shown.
    But this list is imported from the module ExecLists and the list in this
    moule contains a constructor that will do the initialization.



  9 Modules.

  A module  is a  program structure  used to  devide a program into smaller
  parts. A module contains variables, procedures, functions and type defini-
  tions that can be use outside the module.


  9.1 Making modules.

  A module has the form:

    MODULE ModuleName

      [USE statements]

      [EXPORT statements]

      <normal program lines>

    ENDMODULE ModuleName


  Example:

      MODULE Hyperbolic

        EXPORT sinh,cosh

        FUNC sinh(x)
          RETURN (EXP(x)-EXP(-x))/2
        ENDFUNC sinh

        FUNC cosh(x)
          RETURN (EXP(x)+EXP(-x))/2
        ENDFUNC cosh

      ENDMODULE Hyperbolic



                                         128





  All procedures, functions and variables are strictly local to the module. On-
  ly those names that are exported  in EXPORT  statements can  be used out-
  side the module. An EXPORT statement has the form:

    EXPORT ExportList

  where ExportList is one or more of the following separated by commas (,):

    Identifier [AS ExportName]


  If the AS part is present the ExportName is used in references to the ex-
  ported element. Otherwise it is referenced by the name used inside the mo-
  dule itself.


  Example: If the EXPORT statement in the previous example is changed to

      EXPORT sinh,sinh AS SINH,cosh,cosh AS COSH

    both sinh and SINH can be used to refer to the sinh function (and similar
    for cosh).


  It is possible to export names that are imported from other modules. This is
  called re-export.

  A module can be placed in the program that uses the module (local module)
  or it can be placed in a separate disk file (external modules). There is no
  limit to  the number  of modules  that can be placed in a program or in a
  disk file.

  An external module is best made by first make it as a local module. Then,
  when you  think it  works, store it on disk in code form (open a new pro-
  ject, use the clipboard to move the module  to the  new project  and then
  save the module using the Save item in the Program menu).



  9.2 Using modules.

  The exported names in a module (including type names) are made accessible
  by a USE statement. A USE statement has form:

    USE ModuleName [FROM FileName]


  Example:
    USE System
    USE Hyperbolic FROM MathModule

                                         129







  All USE statements are executed at the scanning  time. If  the USE state-
  ment is  used without the FROM part the system searches for the module in
  the following way:

    1. among the local modules
    2. in a file Comal:Modules/ModuleName.mod (if it exists)
    3. in a file Comal:SystemModules/ModuleName.mod (if it exists)
    4. in a file Comal:Modules/ModuleName (mashine coded module)
    5. in a file Comal:SystemModules/ModuleName (mashine coded module)


  If the file in 2. or 3. is found there must be one module by name Module-
  Name in this file.

  If the second form of the USE statement is used, the specified file is loa-
  ded and if it is a Comal module (.mod file) there must  be one  module by
  name ModuleName in this file.

  A USE statement can be placed in a program, in an object, in a closed pro-
  cedure or function or in a module, and all the type names and all the ex-
  ported names  in the  module used can be accessed in the scope of the USE
  statement.


  9.3 Signal procedures.

  It is possible to make the module recieve  messages about  changes in the
  system. These messages are send as signal numbers to a procedure declared
  as a signal procedure.

  The head of a signal procedure has the form:

    PROC ProcName(Sig OF LONG) SIGNAL


  Example:

      PROC Signal(s OF LONG) SIGNAL
        CASE s OF
        WHEN SIG_DISCARD
          CleanUp
        OTHERWISE
          // No action
        ENDCASE
      ENDPROC Signal


  The signal numbers send and their meanings are:

                                         130






    SIG_CLOSE (1)       send if interpreter is going to close
    SIG_DISCARD (2)     send if the module is being removed
    SIG_CLEAR (3)       send if all variables in main program are cleared
    SIG_RUN (4)         send before execution starts
    SIG_STOP (5)        send at program stop that can be continued
    SIG_END (6)         send at program stop that cannot be continued
    SIG_STARTTRACE (7)  send at change to trace mode
    SIG_STOPTRACE (8)   send if trace mode ends
    SIG_STEP (9)        send if a step is executed in trace mode
    SIG_CONTINUE (10)   send if program execution continues after stop


  The symbolic names used are defined in the System module.

  NOTE! Signal numbers send to a module should not be  confused with signal
  numbers used in the Amiga operating system.



  10 Description of the standard modules.

  In the  directory SystemModules a number of useful modules are found. The
  content of some of these modules will be described in this section.


  10.1 The System modules.

  As is the case with other implementations of Comal the system module con-
  tains implementation  dependent stuff.  In this implementation the system
  module is devided into two parts: A module written in Comal by  name Sys-
  tem, and a machine coded module by name SystemCode.


  10.1.1 SystemCode.

  This module contains the following procedures and functions:


    FUNC ComalStrucAdr OF ULONG

      Returns the  address of the ComalStruc (see description of the System
      module in next section).


    FUNC CharArrayToString$(Array OF ULONG)

      Converts a null terminated array of characters into a Comal string.



                                         131





    FUNC ComalWait(SignalMask OF ULONG) OF ULONG

      This function is used like the function Wait in the Exec library of the
      Amiga operating system.

      The function OR's all the Comal signals to the actual parameter Signal-
      Mask and then calls Wait. Among  other things  this means  that it is
      possible to break a Comal program that has called ComalWait (this can-
      not be done if Wait is called!)

      The return value is a mask containing the signals that caused the re-
      turn (and this may be one of the Comal signals).


    FUNC SysLibVersion OF USHORT

      Returns the current version of your system.


    FUNC LockComalWindow OF ULONG

      Get a  shared lock  for the standard IO window. The return value is a
      pointer to the standard IO window.


    PROC UnlockComalWindow

      Call this procedure when the pointer  returned by  LockComalWindow is
      not needed any longer.


    PROC AddSignal(SigMask OF ULONG)

      Call this routine if you want one or more signals to be a Comal signal.
      The arrival of a Comal signal will cause ComalWait to  return and the
      sleep state started by the WAIT statement (see sectio 7.2) will be ter-
      minated. The actual parameter is a mask of signals.


    PROC RemSignal(SigMask OF ULONG)

      Removes a Comal signal added by AddSignal.


    PROC AddComalDevice(IoDevice OF ULONG)

      Call this procedure if you want to add a new IO device to the list of
      standard devices (like "ds:", "kb:" and "lp:"). The actual parameter must
      be a pointer to an initialized device structure.


                                         132






    PROC RemComalDevice(IoDevice OF ULONG)

      Call this with a pointer to the IO device you want to remove from the
      list of IO devices.


    PROC AddExcept(ExceptStruc OF ULONG)

      Call this procedure if you want to add an exception routine to the list
      of exception routines. The actual parameter must be  a pointer  to an
      initialized exception structure.


    PROC RemExcept(ExceptStruc OF ULONG)

      Call this with a pointer to the exception structure you want to remove
      from the list of exception routines.


  Although it is possible to use a Comal procedure as  an exception routine
  the last two routines are normally not used from Comal. Use the procedures
  ADDINTERRUPT and REMINTERRUPT in stead (see section 7.5).



  10.1.2 System.

  The System  module uses  the SystemCode  module from  where the functions
  CharArrayToString$ and  ComalWait is re-exported. Two routines are expor-
  ted:

    FUNC ComalPath$

      Returns a string containing the complete path of your comal system.


    FUNC StartupArg$(n OF USHORT)

      Returns the n'the startup argument (if started from the Shell) or the
      n'the tool type (if started from Workbench). If n is out of range (zero
      or too large) the empty string is returned.

      For the time being this function is of no interest if the Comal program
      is started from the editor.



  In this module the Comal structure is defined:


                                         133





    STRUC ComalStruc
      DIM BreakFlags OF UBYTE
      DIM SecBreakFlags OF UBYTE
      DIM Flags OF USHORT
      DIM CurrWorkBottom OF POINTER TO UBYTE
      DIM CurrWorkTop OF POINTER TO UBYTE
      DIM MinStack OF POINTER TO UBYTE
      DIM IO_Screen OF POINTER TO Screen
      DIM CommPort OF POINTER TO MsgPort
      DIM AsciiId OF POINTER TO UBYTE
      DIM MainPrgBuf OF POINTER TO PrgBuf
      DIM PrgEnv OF POINTER TO PrgEnv
      DIM WorkStart OF ULONG
      DIM WorkEnd OF ULONG
      DIM WorkLength OF ULONG
      DIM SortTable OF POINTER TO UBYTE
      DIM ComalPath OF POINTER TO UBYTE
      DIM Parameters OF POINTER TO ULONG
    ENDSTRUC ComalStruc


  Only a few fields are of interest for Comal programmers:

    IO_Screen

      A pointer to the screen where all windows should be placed. This poin-
      ter is non zero even if the screen is the Workbench screen.


    CommPort

      If non zero this is a pointer to a message port allocated for you.


    AsciiId

      This is a pointer to a null terminated array of characters that identi-
      fies this interpreter process.


    SortTable

      This is a pointer to a table of sort values used when strings are com-
      pared. Normally it is filled with the ASCII values.

      It is possible to change the content of the table (do not  change the
      pointer itself!) with other sorting values.

      Example: By  using this little code upper and lower case letters will
        have the same sorting values:

                                         134






          FOR i:=ORD("a") TO ORD("z") DO
            ComalStruc@.SortTable(i):=i-32
          ENDFOR i


    ComalPath

      This is a pointer to a null terminated string containing the complete
      path of your comal system.


    Parameters

      This is a pointer to an array of pointers to null terninated strings (the
      startup arguments). The array is terminated by a null pointer.


  A pointer ComalStruc to the Comal structure is exported.

  The following useful types are defined in the module:

    TYPE BOOL=BYTE
    TYPE bool=BYTE
    TYPE WORD=SHORT
    TYPE word=SHORT
    TYPE UWORD=USHORT
    TYPE uword=USHORT


  The signal numbers SIG_CLOSE,  SIG_DISCARD  etc.  (see  section  9.3) and
  the memory  types MEMF_PUBLIC  etc. (see section 1.4) are defined and ex-
  ported from the module.



  10.2 The graphics modules.

  Two graphics modules Graphics and Turtle can be used.



  10.2.1 Graphics.

  This module is compatibal with graphics modules (packages) found in other
  implementations  of  Comal  (UniComal  for  the  C64  and PC'c and Amiga-
  COMAL v. 2.x).

  The Graphics module contains:


                                         135





    PROC arc(c1,c2,r,start_angle,arc)

      The procedure causes an arc to be drawn with its center at (c1,c2) and
      with a radius r. The arc has the length arc degrees starting with the
      angle start_angle.

      Exampel:

        arc(100,100,50,45,90)
        arc(x0,y0,r,v,2*v)


    PROC background(color)

      This procedure sets the back ground color to the value of the parame-
      ter color.

      Exampel:

        background(3) // Set back ground color to blue (red in WB 1.3)


    PROC circle(c1,c2,r)

      Draws a circle with its center at (c1,c2) and with radius r. The drawn
      figure will not be a round circle unless a suitable relation between the
      vertical and horizontal units has been chosen (window procedure).
    
      The current pen position is unchanged.

      Exampel:

        window(-160,160,-100,100)
        circle(0,0,50)              // Draw a "circular" circle.


    PROC clear

      Clears the window inside the current drawing region set by the proce-
      dure clip.


    PROC clearscreen

      Clears the window inside the current viewport frame set by the proce-
      dure viewport.





                                         136





    PROC clip(xmin,xmax,ymin,ymax)

      The procedure clip defines a working region expressed in window coor-
      dinates (se window procedure). All drawings (clear, fill, draw, ....) can-
      not be seen outside this region.

      Exampel:

        clip(2.5,4.7,-2,0)
        clip(0,cx,cy,cy+10)


    FUNC depth

      Function that returns the depth of the graphics window (the number of
      bitmaps in the window).

      If the depth is 2, there are four colors with numbers 0-3. The number
      of colors can always be calculated as

        colornumber:=2^depth


    PROC draw(x,y)

      Draws a  line from the current pen position (x0,y0) to the point with
      coordinates (x0+x,y0+y).
    
      The new pen position is the point with coordinates (x0+x,y0+y).

      Exampel:

        draw(0,100)  // Draws a vertical line of length 101


    PROC drawto(x,y)

      Draws a line from the current pen position (x0,y0) to  the point with
      coordinates (x,y).
    
      The new pen position is the point with coordinates (x,y).


    PROC fill(x,y)

      Fills an area with the current pencolor. The area to be filled must con-
      tain the point (x,y) and be bounded by either a curve in a color diffe-
      rent from the background color or by the frame set by the clip.
    
      The current pen position is unchanged.

                                         137






      Exampel:
        pencolor(1)
        circle(300,100,50)  // Draw a circle with pencolor 1
        pencolor(2)
        fill(300,100)       // .. and fill with pencolor 2


    FUNC getcolor(x,y)

      Return color of the point with coordinates (x,y).
    
      The current pen position is unchanged.


    FUNC getrgb(color_register)

      Returns the color of the specified color register. Written as a hexade-
      cimal number the content is of the form

        $0rgb

      where r, g and b are  the content  of the  red, green  and blue color
      (four bits for each color).

      If the graphics system is not opned the value -1 is returned.
             

    PROC graphicscreen(Window)

      Opens the graphics system. If the actual parameter is zero the standard
      IO-window will be used as the graphics window. If the parameter is non
      zero it  must be the address of a window and this window will be used
      as the graphics window.

      This procedure must be called before any of the graphics routines are
      called. When  the graphics  system is opned the command window or the
      graphics window may be brought in front by pressing <Shift>+<F1>.
      

    FUNC height

      Returns the height of the graphics window in pixels
    

    FUNC inq(no)

      The function inq returns certain of the internal variable of the gra-
      phics system. The parameter no must be an integral number in the ran-
      ge 0-33. The meaning of the returned value can be found in the follo-

                                         138





      wing table:

      number    information about     range     Affected by
        0       graphics mode         0/1       graphicscreen
        1
        2       text background       0-        textbackground
        3       text color            0-        pencolor
        4
        5       graphics backgruond   0-        background
        6       pen color             0-        pencolor
        7       graphics text width   8/10      textstyle
        8       graphics text height  8/10      textstyle
        9       gr. text direction    0         (dir. cannot be changed)
       10       graphics text mode    0/1       textstyle
       11
       12       pen inside dr. frame  0/1       most drawing procs
       13                             1
       14                             0
       15       wrapping active       0         (wrap not implemented)
       16       pen down              1         only changed in Turtle
       17       x-position            0-width   most drawing procs
       18       y-position            0-height  most drawing procs
       19       viewport xmin         0-width   viewport
       20       viewport xmax         0-width   viewport
       21       viewport ymin         0-height  viewport
       22       viewport ymax         0-height  viewport
       23       vindow xmin           real no.  window
       24       vindow xmax           real no.  window
       25       vindow ymin           real no.  window
       26       vindow ymax           real no.  window
       27                             1
       28                             0
       29                             0
       30       x-ratio               (rem. 1)  window and viewport
       31       y-ratio               (rem. 2)  window and viewport
       32                             0
       33                             0

      remark 1: (wdxmax-wdxmin)/(vpxmax-vpxmin)
      remark 2: (wdymax-wdymin)/(vpymax-vpymin)


    PROC interiorcolor(color)

      Subsequent calls to the procedure polygon uses the parameter color to
      fill out the interior of the polygon.

      Exampel:
        interiorcolor(2)    // Fill with black
        interiorcolor(-1)   // No filling

                                         139







    PROC loadscreen(name$)

      The procedure causes an ILBM-picture previous stored by  Comal (save-
      screen and  SaveWindow procedures) or by another program (like DeLuxe
      Paint) to be fetched from disk and placed in the graphics window.

      Exampel:

        loadscreen("MandelBrot.ilbm")


    PROC move(x,y)

      Moves witout drawing the pen from the current position (x0,y0) to the
      point with coordinates (x0+x,y0+y).


    PROC moveto(x,y)

      Moves witout drawing the pen to the point with coordinates (x,y).


    PROC noclip

      The procedure cancels any previously defined clip working region (set
      by the procedure clip) and restores the working region to the current
      viewport (set by the procedure viewport).


    PROC outlinecolor(color)

      Subsequent calls to the procedure polygon uses the parameter color as
      the color of the edges of the polygon.

      Exampel:

        outlinecolor(3)   // Draw polygon with edges in pencolor 3


    PROC paint(x,y)

      The procedure causes a region to be filled in with the current pencolor.
      The region  to be  filled must contain the point (x,y) and it must be
      limited by the pencolor or the viewport drawing area.
    
      The current pen position is unchanged.
    


                                         140





      Exampel:

        pencolor(1)
        moveto(0,0)
        drawto(500,150)     // Draw a black (white in WB 1.3) line
        pencolor(3)
        circle(300,100,50)  // .. a red circle
        paint(300,100)      // .. and fill the circle with blue color


    PROC palette(no)

      The procedure is used to set  the first  four colors  of the graphics
      screen. The colors are selected from one of three palettes:
     
        palette   color 0   color 1   color 2   color 3
        
           0       black     green      red      yellow
           1       black     magenta    cyan     white

      palette(-1) restores the colors to the original values.
     
      Note that  if the graphics window is placed in the IO-window (this is
      the case if the procedure graphicscreen is called with a zero as para-
      meter) all the Comal windows will change colors. The proceudre is im-
      plemented to insure compatibility with UniComals graphics module. Nor-
      mally the more general procedure setrgb should be used.


    FUNC pencolor(color)

      Procedure used to set the color of the pen.


    FUNC pixelx

      The function returns the width of a picture element (a pixel) expressed
      in the current window units.

      Exampel:
        draw(2*pixelx,0)


    FUNC pixely

      The function returns the height of a picture element (a pixel) expressed
      in the current window units.

      Exampel:
        draw(0,pixely)

                                         141







    PROCEDURE plot(x,y)

      The procedure places a dot at the point with coordinates (x,y).

      The current pen position is unchanged.


    PROC plottext(x,y,text$)

      The procedure causes text$ to be written in the graphics window star-
      ting at the point with coordinates (x,y).

      The current pen position is unchanged.

      Exampel:

        plottext(100,150,"Comal")


    PROC polygon(corners, REF x(), REF y())

      The procedure causes a polygon with corners vertices to be drawn. The
      coordinates of the vertices are stored in the vectors x() and y().
    
      The number of elements must be at least the value of corners.
    
      The edges (the perimeter) of the polygon is drawn in the color set by
      the procedure outlinecolor and the color of the inner region is set by
      the procedure interiorcolor (unless the interiorcolor is set to the value
      -1 in which case a transparent polygon will be drawn).

      Exampel:

        DIM x(3), y(3)
        READ x(),y()
        interiorcolor(3)
        outlinecolor(2)
        polygon(3,x(),y()) // Draw a triangle in color 3 with edges in color 2
        DATA 150,300,450
        DATA 20,150,20

      NOTE: The procedure uses a routine in the Amiga graphics  library. It
      seems as  if this  procedure has a bug. If the corners of the polygon
      falls outside the graphics window the system may crash (the well known
      GURU will visit you!).




                                         142





    PROC printscreen

      The procedure  transfers the  content of  the graphics  window to the
      printer.


    PROC savescreen(name$)

      The procedure causes the content of the graphics  window to  be saved
      on disk in ILBM format.
    
      Exampel:

        savescreen("MandelBrot.ilbm")


    PROC SaveWindow(name$,mode)

      The procedure  works as  savescreen above  if the  parameter mode has
      the value 0. If mode has the value 1 the window is saved in compressed
      form (using  the ByteRun1 compressing algorithm). This may reduce the
      size significantly.

      Exampel:

        SaveWindow("Mandelbrot.ilbm",1)


    PROC setrgb(color_register,red,green,blue)

      This procedure is used to set the amount of red, green and blue in the
      specified color register. The color_register is an integral number in the
      range 0-31 (this number is directly asociated to the color number used
      by the procedures pencolor, backcolor etc.) and the amount of color is
      an integral number in the range 0-15.

      Exampel: After the call
       
          setrgb(1,15,0,15)
        
        color number 1 (set by pencolor procedure) has the color magenta.
       
      The colors of all the color registers may be restore to their original
      value by palette(-1).


    PROC TextAttr(attr)

      The procedure  defines the manner in which the printout from the pro-
      cedure TextAttr will appear on the graphics window.

                                         143






      The following values may be used for the parameter attr:

         0  normal text
         1  bold
         2  italic
         4  underlined
         8  inverse
        16  only text (no background)

      More values can be set by adding the values.

      Exampel:
        TextAttr(1+2) // Print in bold and italic


    PROC textbackground(color)

      The procedure is used to set the background color used by the plottext
      procedure (see also textstyle).


    PROC textscreen

      Closes the graphics system.


    PROC textstyle(width#,height#,direction#,mode#)

      The proceudre  is implemented  to insure compatibility with UniComals
      graphics module and only the last parameter is significant. The meaning
      of this is:

         0  print without background
         1  print with background (as attr=16 in TextAttr above)


    PROC viewport(xmin,xmax,ymin,ymax)

      Defines a  region of  the graphics  window in which all drawings take
      place. The parameters refer to the physical window (pixel coordinates).


    PROC viewport_to_window(vpx, vpy, REF wdx, REF wdy)

      The procedure maps the viewport coordinates (the physical  window co-
      ordinates) to the window coordinates (set by the window procedure).




                                         144





    FUNC width

      The function returns the width of the graphics window in pixels.


    PROC window(xmin,xmax,ymin,ymax)

      The procedure defines a coordinate system in the current viewport. The
      parameter defines new coordinates for the border of the viewport.


    PROC window_to_viewport(wdx, wdy, REF vpx#, REF vpy#)

      The procedure converts window coordinates to  physical window coordi-
      nates (pixel coordinates).


    FUNC xcor

      The function returns the current x-coordinate of the pen.


    FUNC ycor

      The function returns the current y-coordinate of the pen.



  10.2.2 Turtle.

  This module  extends the  Graphics module with relative graphics routines
  (Turtle graphics). The modulee is written in Comal and uses all the routines
  in the Graphics module.

  The command  graphicscreen executes an implicit window(-160,160,-100,100)
  and draws a turtle (a small triangle) in the mittle of the window.

  All procedures and functions in the Graphics module are accessible from the
  Turtle module and besides the following routines are supplied:


    PROC arcl(radius,arc_angle)

      The procedure  causes an arc to be drawn to the left with the parame-
      ter radius as the radius of curvature and subtending an angle arc_angle
      degrees. The starting point is the current position and the starting di-
      rection is the current direction.

      Both the current pen position and the current direction is changed.


                                         145





      Exampel:

        arcl(10,90)


    PROC arcr(radius,right_arc)

      The procedure causes an arc to be drawn to the  right with  the para-
      meter  radius  as  the  radius  of  curvature and subtending an angle
      arc_angle degrees. The starting point is the current position and the
      starting direction is the current direction.

      Both the current pen position and the current direction is changed.


    PROC back(x)

      The procedure  moves the pen x units backwards in the current drawing
      direction.

      The current pen position is changed.


    PROC forward(x)

      The procedure moves the pen x  units forward  in the  current drawing
      direction.

      The current pen position is changed.


    FUNC heading

      The function  returns the value of the current drawing direction. The
      direction is indicated in degrees with 0 vertically upward and positive
      to the left.
    

    PROC hideturtle

      The procedure  causes the  drawing pen (the turtle) to disappear from
      view in the window.


    PROC home

      The procedure causes the drawing pen (the turtle) to return to position
      (0,0) and with drawing direction upward.



                                         146





    FUNC inq(no)

      The function inq returns certain of the internal variable of the gra-
      phics system. The parameter no must be an integral number in the ran-
      ge 0-33. The meaning of the returned value can be found in the follo-
      wing table:

      number    information about     range     Affected by
        0       graphics mode         0/1       graphicscreen
        1
        2       text background       0-        textbackground
        3       text color            0-        pencolor
        4
        5       graphics backgruond   0-        background
        6       pen color             0-        pencolor
        7       graphics text width   8/10      textstyle
        8       graphics text height  8/10      textstyle
        9       gr. text direction    0         (dir. cannot be changed)
       10       graphics text mode    0/1       textstyle
       11       dr. pen visible       0/1       hideturtle/showturtle
       12       pen inside dr. frame  0/1       most drawing procs
       13                             1
       14                             0
       15       wrapping active       0         (wrap not implemented)
       16       pen down              0/1       penup/pendown
       17       x-position            0-width   most drawing procs
       18       y-position            0-height  most drawing procs
       19       viewport xmin         0-width   viewport
       20       viewport xmax         0-width   viewport
       21       viewport ymin         0-height  viewport
       22       viewport ymax         0-height  viewport
       23       vindow xmin           real no.  window
       24       vindow xmax           real no.  window
       25       vindow ymin           real no.  window
       26       vindow ymax           real no.  window
       27                             1
       28                             0
       29       size of pen           0-        turtlesize
       30       x-ratio               (rem. 1)  window and viewport
       31       y-ratio               (rem. 2)  window and viewport
       32                             0
       33                             0

      remark 1: (wdxmax-wdxmin)/(vpxmax-vpxmin)
      remark 2: (wdymax-wdymin)/(vpymax-vpymin)






                                         147





    PROC left(angle)

      The procedure causes the drawing pen  to be  turned angle  degrees to
      the left in relation to the current drawing direction.


    PROC pendown

      The procedure causes the pen to be lowered. This means that the turtle
      will draw as it is moved, except for the procedures move and moveto.


    PROC penup

      The procedure causes the pen to be lifted. After this the pen will not
      draw when it is moved, except for the procedures draw and drawto.


    PROC right(angle)

      The procedure  causes the  drawing pen  to be turned angle degrees to
      the right in relation to the current drawing direction.


    PROC setheading(angle)

      The procedure causes the drawing direction to be set to angle degrees
      from the home direction (positive to the left).


    PROC setxy(x,y)

      The procedure  causes the pen to be moved to the point with coordina-
      tes (x,y). If the pen is down it acts like drawto, i.e. a line will be
      drawn. If pen is up it acts as moveto, i.e. no line will be drawn.


    PROC showturtle

      The procedure  causes the  drawing pen  (the turtle) to appear in the
      window.


    PROC turtlesize(x)

      The procedure defines the size of the turtle.


  In the Turtle module the following abbrivations may be used:


                                         148






      bk      =   back
      bg      =   background
      cs      =   clearscreen
      fd      =   forward
      ht      =   hideturtle
      lt      =   left
      pc      =   pencolor
      pd      =   pendown
      pu      =   penup
      rt      =   right
      seth    =   setheading
      st      =   showturtle
      textbg  =   textbackground


  Most programs developped to  be used  with the  Graphics module  will run
  without changes  with the  Turtle module. In some cases it is necesary to
  set the coordinate system back to the physical pixel coordinate system by
  using the line:

        window(0,width-1,0,height-1)


  10.3 Catalog.

  This module  treats a  directory mush like a file. The module exports the
  following procedures and functions:


    PROC OpenCatalog(Name$, REF CatId OF ULONG)

      Opens a volume or directory  for  subsequent  readings.  The returned
      value in CatId is used as an identifier for the catalog.


    PROC ReadCatalog(CatId OF ULONG,REF name$,REF filesize OF LONG)

      Read the next file/subdirectory from the catalog. If filesize=-1 this is a
      subdirectory otherwise it is the size of the file.


    PROC CloseCatalog(number OF ULONG)

      Close a previosly opened catalog.


    FUNC EOC(number OF ULONG) OF BOOL

      Returns TRUE (=1) if no more directories. Otherwise FALSE (=0).

                                         149







    PROC CloseAllCatalogs

      Close all catalogs opened by OpenCatalog.



  10.4 Bob and sprite modules.

  The three modules SpriteRoutines, SpriteStrucs and GelObject are  used in
  connection with gels (bobs and sprites). For normal use it is only the last
  one that has interest.

  The module  GelObject contains  two object  definitions VSpriteObject and
  BobObject. The use of these object are best shown by an example.

  Let first make a (virtual) sprite. This is done by defining an object:

    STRUC VirtualSprite
      INHERIT VSpriteObject

      PROC Create
        Init
      ENDPROC Create

      // Colors
      DATA $0F00,$00F0,$000F

      // Sprite data
      DATA 12                                   // number of lines
      //   plane 0           plane 1
      DATA %1111111111111111,%1111111111111111  // 1. line
      DATA %1111111111111111,%1100000000000011
      DATA %1111111111111111,%1100000000000011
      DATA %1111000000001111,%1100111111110011
      DATA %1111000000001111,%1100111111110011
      DATA %1111000000001111,%1100110011110011
      DATA %1111000000001111,%1100110011110011
      DATA %1111000000001111,%1100111111110011
      DATA %1111000000001111,%1100111111110011
      DATA %1111111111111111,%1100000000000011
      DATA %1111111111111111,%1100000000000011
      DATA %1111111111111111,%1111111111111111  // 12. line
    ENDSTRUC VirtualSprite


  This object inherits the VSpriteObject and it contains a method Create and
  DATA statements defining the object.


                                         150





  To create a sprite with a shape defined by the  DATA statements  you have
  to execute a DIM statement:

    DIM MySprite OF VirtualSprite

  and then call the methods Create and Draw:

    MySprite.Create
    MySprite.Draw(50,50)

  Now you  can see  the sprite  on the  screen. To move the sprite call the
  method Move (inherited from VSpriteObject), for instance

    MySprite.Move(10,10)


  At the end of your program the sprite has to be deleted:

    MySprite.Delete


  A bob is made in almost  the same  way. First  you have  to define  a Bob
  structure:


    STRUC Bob
      INHERIT BobObject

      PROC Create
        Init
      ENDPROC Create

      // Bob data
      DATA 1,9                 // Words per line and number of lines

      // Plane 0
      DATA %0000111111000011   // 1. line
      DATA %0011111111110011
      DATA %0011000011000011
      DATA %0000000000000000
      DATA %0000000000000000
      DATA %0000000000000000
      DATA %1100000000110011
      DATA %1111111111000000
      DATA %0011111100000011   // 9. line

      // Plane 1
      DATA %0000000000000000   // 1. line
      DATA %0000000000000000
      DATA %0000000000000000

                                         151





      DATA %0011110000000011
      DATA %0011111111000011
      DATA %0000001111000011
      DATA %1100000000110011
      DATA %1111111111000000
      DATA %0011111100000011   // 9. line
    ENDSTRUC Bob


  and execute a DIM statement:

    DIM MyBob OF Bob

  The rest of the program is made in the same way as for virtual sprites.

  You can  have as  many virtual sprite and bobs as you want on the screen.
  They are all made in the same way.  Only the  DATA statement  may be dif-
  ferent.


  10.5 Memory.

  This module is used to allocate memory from the Amiga's free memory pool.

  The following routines are exported:


    FUNC malloc(BlockSize OF ULONG,MemType OF ULONG) OF ULONG

      Allocate  a  memory  block  of  size  BlockSize  and  of type MemType
      (MEMF_PUBLIC, MEMF_CLEAR, MEMF_CHIP or MEMF_FAST).

      The function returns the address of  the block  or zero  if the block
      could not be allocated.


    PROC mfree(MemAdr OF ULONG)

      Free a memory block previosly allocated by malloc


    PROC mfreeall

      Free all memory blocks previosly allocated by malloc


  The module contains a signal routine that calls mfreeall if the variables are
  cleared.



                                         152






  10.6 CodeManInclude and InterpreterInclude.

  These modules contains definitions to be used to access Comal.CodeMan and
  Comal.Interpreter (see section V.2).



  10.7 StartProgram.

  This module exports one procedure:


    PROC StartProgram(ProgramName$)

      Start a Comal program as a seperate process that will terminate itself.
      The parameter must be the full path of the program that must  be sto-
      red as a code file.


  10.9 Timer.

  This module exports the following procedures:


    PROC TimerEvent(TimeInterval,EventProc)

      Start the timer. The timer runs for the specified time interval (in se-
      conds) and then you procedure EventProc is called. The event procedure
      is a procedure without parameters.


    PROC AbortTimer(EventProc)

      Abort a time event before timeout.


  The timer  can be used to make periodic interrupts (see section 7.5). The
  timer interrupts the program at priority 20.


  10.10 SerialComm.

  This module is a serial communication module. The following procedures and
  functions are exported:

    FUNC chars_in_buff(direction OF BYTE) OF SHORT

      This function  returns TRUE  if there are chracters in the IO buffer.
      Otherwise it returns FALSE.

                                         153






      If direction is 0 the input buffer is tested. Otherwise it is the output
      buffer (for  the time  being there is no output buffer and the return
      value with direction<>0 will always be FALSE).


    PROC clear_buff(direction OF BYTE)

      Clear the IO buffer. If direction is 0 the input buffer is tested. Other-
      wise it is the output buffer.


    PROC ClearTermArray

      Remove all break characters set by SetTermArray.


    PROC CloseSerial

      Close the serial device and make it available for other tasks.


    FUNC OpenSerial OF BOOL

      Open serial  device. If success the value TRUE is returned. Otherwise
      FALSE is returned.


    PROC receive(REF Data$,MaxChars OF LONG,MaxWait)

      Read MaxChars characters into th string Data$. The procedure will re-
      turn when all characters are received or MaxWait secs has elapsed.


    PROC send(Data$)

      Send the string Data$.


    PROC set_mode(baud OF LONG,parity$,Len OF UBYTE,StopBits OF BYTE)

      Set communication parameters. The parameters are:

        baud:     The baud rate.
        parity$:  "N" = no parity
                  "E" = even parity
                  "O" = odd parity
        Len:      The number of bits in a word (6,7 or 8).
        StopBits: The number of stop bits (1 or 2).


                                         154






    PROC SetTermArray(Arr OF TermArray)

      Set termination characters. The type TermArray is defined in the modu-
      le as

        TYPE TermArray=ARRAY(0..7) OF UBYTE

      The actual parameter Arr should be filled with characters that  is to
      break the reading. All 8 characters must be set in non increasing order.
      If less than 8 break characters is to be used (this is normal) fill the
      end of the array with the lowest value.

      Example:

        DIM Array OF TermArray

          :

        Array():=$03    // ^C
        Array(0):=$1A   // ^Z
        Array(1):=$04   // ^D

        SetTermArray(Array())

          :


  10.11 Operating system modules.

  A number  of modules are to be used to access the operating system routi-
  nes. These routines are devided into two groups:


    - Library interfaces for the system routines themself. These modules are
      mashine coded modules.

    - Definitions necessary to call the system routines.


  10.11.1 Library interface routines.

  These modules are:

        AslLibrary
        DosLibrary
        ExecLibrary
        GadToolsLibrary
        GraphicsLibrary
        IntuitionLibrary

                                         155







  The modules contains interfaces for all the routines in the corresponding
  libraries. The names and the calling conventions are exactly the same as is
  used in a C program.

  Example: This program opens a window and closes it again

    USE System    
    USE IntuitionWindow    
    USE IntuitionLibrary    

    DIM NewWindow OF NewWindow
    DIM Window OF POINTER TO Window    

    NewWindow.LeftEdge:=50
    NewWindow.TopEdge:=30
    NewWindow.Width:=300
    NewWindow.Height:=100
    NewWindow.BlockPen:=1
    NewWindow.Title:=ADR("Window demo")
    NewWindow.Screen:=ComalStruc@.IO_Screen
    NewWindow.Flags:=WINDOWDEPTH BITOR WINDOWDRAG
    NewWindow.Type:=CUSTOMSCREEN

    Window:=OpenWindow(ADR(NewWindow))      
    WAIT 3
    CloseWindow(Window)        


  The library interface modules will not be described in detail here. Consult a
  description of the Amiga OS system routines.



  10.11.2 Definitions for use in the library modules.

  The definitions to be used in connection with calls to library routines are
  found in the following modules:


    AslInclude

      Structures:
        STRUC FileRequester
        STRUC FontRequester

      Types:
        TYPE FileReqPtr=POINTER TO FileRequester


                                         156





      Constants:
        ASL_FuncFlag file requester tag values (FILF_DOWILDFUNC ..)
        ASL_ExtFlags1 file requester tag values (FILF1_NOFILES ..)
        ASL_FuncFlag font requester tag values  (FONF_FRONTCOLOR ...)
        requester types (ASL_FileRequest and ASL_FontRequest)
        tags for AllocAslRequestA and AslRequestA (ASL_Hail ...)


    DosAslInclude

      Structures:
        STRUC DateStamp
        STRUC FileInfoBlock
        STRUC AChain
        STRUC AnchorPath

      Types:
        TYPE FileInfoPtr=POINTER TO FileInfoBlock
        TYPE AnchorPathPtr=POINTER TO AnchorPath


    ExecLists

      Structures:
        STRUC Node
        STRUC List
          FUNC New CONSTRUCTOR
            Initializes the list.

      Constants:
        Node types (NT_TASK, NT_MESSAGE ...)


    GadToolsInclude

      Structures:
        STRUC StringInfo
        STRUC Gadget
        STRUC NewGadget
        STRUC NewMenu

      Constants:
        Gadget types (GENERIC_KIND, BUTTON_KIND ...)
        IDCMP flags (ARROWIDCMP, BUTTONIDCMP ...)
        spacing constants (INTERWIDTH and INTERHEIGHT)
        NewGadget flags (NG_HIGHLABEL, PLACETEXT_LEFT ...)
        NewMenu types (NM_TITLE, NMITEM ...)
        return codes (GTMENU_TRIMMED ...)
        tags for tool kit functions (GTVI_NewWindow, GTVI_NWTags ...)


                                         157






    Gfx

      Functions and procedures:
        FUNC RASSIZE(w,h)     (in fact a macro in C include files)

      Structures:
        STRUC RectAngle
        STRUC BitMap


    IDCMP

      Structures:
        STRUC IntuiMessage    (inherits Message from PortObjects)

      Constants:
        IDCMP classes (SIZEVERYFY, NEWSIZE ...)
        Mouse codes and key qualifiers (SELECTUP, ALTLEFT, ...)


    IntuiText

      Structures:
        STRUC IntuiText
        STRUC TextAttr


      Constants:
        draw modes (JAM1, JAM2, XOR)


    IoObjects

      Structures:

        STRUC IoRequest
          INHERIT Message
          PROC Init Constructor
            Initializes the length of the message.
          PROC Do
            Calls DoIO  in the  ExecLibrary with a pointer to the object as
            parameter.
          PROC Send
            Calls SendIO in the ExecLibrary with a pointer to the object as
            parameter.
          PROC Abort
            Calls AbortIO in the ExecLibrary with a pointer to the object as
            parameter.


                                         158





        STRUC IoStdReq
          INHERIT IoRequest
          PROC Init Constructor
            Initializes the length of the message.


    Layers

      Structures:
        STRUC Layer_Info

      Constants:
        Layer types etc. (LAYERSIMPLE, LAYERSMART ...)


    MenuStrucs

      Structures:
        STRUC Menu
        STRUC MenuItem

      Constans:
        menu and item flags (MENUENABLED, CHECKIT, ITEMTEXT ...)
        MENUNULL


    PortObjects

      Structures:
        STRUC MsgPort
          FUNC Init CONSTRUCTOR
            Makes the necessary initialization for a private port (allocates a
            signal bit etc.).
          PROC Delete DESTRUCTOR
            Removes the  port from  the list of public ports (if the method
            Add has been called) and deallocates the signal.
          PROC Add(REF Name$,Priority OF BYTE)
            Adds the port to the list of public ports.
          PROC Wait
            Wait for a message to arrive. It is possible to break the program
            during the wait.
          FUNC Get
            The ExecLibrary function GetMsg is called with a pointer to the
            port as parameter.
          PROC Put(REF Msg OF Message)
            The ExecLibrary procedure PutMsg  is called  with a  pointer to
            the port and the message as parameter.

        STRUC Message
          FUNC Init CONSTRUCTOR

                                         159





            Initializes the message (but no reply port is created!!).
          PROC Wait
            Waits for the message to be replied and remove the message from
            the reply port.
          PROC Reply
            Replies the message.


        Before you can use  the message  you will  normally have  to make a
        reply port  and put  an address  of this port into the mn_ReplyPort
        field.


    RastPort

      Structures:
        STRUC AreaInfo
        STRUC TmpRas
        STRUC RastPort


      Constants:
        drawing modes (JAM1, JAM2, COMPLEMENT, INVERSEVID)
        bits for RastPort flags (FRST_DOT, ONE_DOT, DBUFFER)


    TagItem

      Structures:
        STRUC TagItem


      Constants:
        system tags (TAG_DONE, TAG_END, .. , TAG_USER)
        TAGFILTER_AND, TAGFILTER_NOT


    View

      Structures:
        STRUC ColorMap
        STRUC ViewPort
        STRUC View
        STRUC RasInfo

      Constants:
        view modes (PFBA, DUALPF, HIRES, ...)




                                         160





    IntuitionWindow

      Structures:
        STRUC NewWindow
        STRUC Window

      Constants:
        flag bits set by Intuition (SCREENTYPE, WBENCHSCREEN, ...)
        window flags (WINDOWSIZING, WINDOWDRAG, ...)


    IntuitionScreen

      Structures:
        STRUC NewScreen
        STRUC Screen



  11 The Comal Intuition Tools (CIT).


    NOTE! CIT requires Workbench 2.04 or higher.


  The Comal Intuition Tool (short CIT) is a set of modules that are used to
  build various Intuition items: screens, windows, gadgets etc.

  Normally the creation of Intuition items involves filling out special struc-
  tures with special values. If the item is used to act on  response from a
  user (such as gadgets or menus) you also have to work with message ports,
  signals and messages. And you must evaluate each message to find out what
  to do.

  By using CIT it is much easier. All the necessary structures are filled auto-
  matically with standard values that  can  be  changed  by  calling object
  methods. You  don't have  to know  anything about ports, signals and mes-
  sages and the evaluation of a user action is done by the objects.



  11.1 General description of CIT.

  In CIT all Intuition items are treated as Objects. For  example, a button
  gadget is  an object  that can  be placed  in a window and a window is an
  object that can be placed in a screen.

  Certain objects have similar characteristics and  can be  classified into
  groups called classes. For instance, menus and gadgets are similiar in that
  they are placed in a window. They are classified as WindowClass objects. In

                                         161





  the same  way a  check box, a button gadget and a string input gadget are
  similar. They are classified as members of the class CITGadget (a subclass
  of the WindowClass).

  All the  objects are  implemented as  structures. You should think of the
  objects as physical items. First you have to create it. This is done by exe-
  cuting a  DIM or  LOCAL statement. Then you will proporly add some attri-
  butes like a position, a size or even a colour.  This is  done by calling
  methods in the structure. Finally you will place it somewhere. This is done
  by calling a method InsObject in the containing object.

  As can be seen, all objects are placed in other objects. In this way objects
  form an object hierachy as shown below (the boxes containing '??' are not
  defined for the time being):

                            +---------------+
                            |               |
                            | CITWorkbench  |
                            |               |
                            +---------------+
                              ^     ^     ^
                              |     |     |
          +-------------------+     |     +------------------+
          |                         |                        |
    +------------+           +-------------+          +-------------+
    |            |           |             |          |             |
    |   ??       |  ......   |  CITScreen  |  ......  |    ??       |
    |            |           |             |          |             |
    +------------+           +-------------+          +-------------+
                              ^     ^     ^
                              |     |     |
          +-------------------+     |     +------------------+
          |                         |                        |
    +------------+           +-------------+          +-------------+
    |            |           |             |          |             |
    |   ??       |  ......   |  CITWindow  |  ......  |    ??       |
    |            |           |             |          |             |
    +------------+           +-------------+          +-------------+
                              ^  ^  ^  ^  ^
                              |  |  |  |  |
                              |  |  |  |  |
          +-------------------+  |  |  |  +------------------+
          |                      |  |  |                     |
          |           +----------+  |  +-------+             |
          |           |             |          |             |
    +-----------+     |    +-------------+     |      +-------------+
    |           |     |    |             |     |      |             |
    |  CITText  |     |    |  CITGadgets |     |      |   CITMenus  |
    |           |     |    |             |     |      |             |
    +-----------+     |    +-------------+     |      +-------------+

                                         162





                      |                        |
                +-----------+           +-------------+
                |           |           |             |
          ....  |  CITMouse | ........  | CITRequester| ......
                |           |           |             |
                +-----------+           +-------------+



  The use of all these different sorts of objects follow the same scheme:

  To create an object you have to execute a DIM (or LOCAL) statement like:

      DIM MyObject OF CITObject


  Most of the visible objects have a Size method and  a Position  method to
  set its size and its position:

      MyObject.Size(Width,Height)
      MyObject.Position(LeftEdge,TopEdge)

  and some have a Label method to connect a text to the object:

      MyObject.Label(SomeText$)


  These methods  are always  named Size,  Position and Label, but sometimes
  there are additional parameters. In some of the gadgets, for instance, you
  have to specify if the text is to be placed inside the object itself or to the
  left, to the right etc.

  If the object is to respond to a user action  and you  want immediate re-
  sponse, you  have to call the method EventHandler with the name of a pro-
  cedure to be called as the result of the users action:

      MyObject.EventHandler(MyObjectEvent())


  The number and types of the parameters in the event  procedure are diffe-
  rent from object type to object type (in the example we have indicated one
  parameter).

  Having set all the attributes you have to insert it in another object. This is
  done by calling the method InsObject in the container object:

      Container.InsObject(MyObject,Error)


  When you are finished using the object you should remove it by calling the

                                         163





  method RemObject in the container object:

      Container.RemObject(MyObject)


  NOTE: Not all objects can be removed individually.

  By removing the object MyObject all objects contained in this object will
  be removed, too.

  Between the  insertion and  the removal  you will normally be waiting for
  some termination code to take on a non zero value. This is typically done
  in a loop like:

      WHILE NOT Terminate DO WAIT


  While you are waiting all your event procedures will be called automatically.

  In the following sections you will find a complete description of all the CIT
  objects.


  11.2 Simple CIT examples.

  As a simple example of the use of CIT let's make a  window with  a string
  input gadget and a button gadget to terminate the input.

  First we have to load the modules we are using:

    USE CITScreen
    USE CITWindow
    USE CITGadgets

  and then we will create our window:

    DIM DemoWindow OF CITWindow

  give it the right size, place it on the correct position in the containing
  screen and make it active when it opens:

    DemoWindow.Size(500,100)
    DemoWindow.Position(60,50)
    DemoWindow.Activate


  We want  to place  the window  in the  ComalScreen which  is a predefined
  screen found in the module CITScreen:

    ComalScreen.InsObject(DemoWindow,Error)

                                         164







  Any error code is returned in the variable Error. We have to test this va-
  riable before we proceed:

    IF Error THEN
      STOP "Could not create window"
    ENDIF


  The string input gadget is created, sized and positioned in the same way:

    DIM NameInput OF StringGadget
    NameInput.Size(300,15)
    NameInput.Position(160,10)


  We want a text to the left of the gadget:

    NameInput.Label("Enter your name: ",LEFT)


  This input gadget is placed in the window in exactly the same  way as the
  window was placed in the screen:

    DemoWindow.InsObject(NameInput,Error)


  And now the button gadget:

    DIM OkButton OF ButtonGadget
    OkButton.Size(40,15)
    OkButton.Position(10,-(6+15))
    OkButton.Label("Ok",INSIDE)           // Place text inside the gadget
    DemoWindow.InsObject(OkButton,Error)


  Note that the y-coordinate of the position is negative. Then the gadget is
  positioned relative to the buttom border of the window.

  The variable Error is only changed if an error occurs. Let's test it now:

    IF Error THEN
      STOP "Could not create one or more gadgets"
    ENDIF


  Now we will sleep until the Ok button is pressed:

    WHILE NOT OkButton.Pressed DO WAIT

                                         165







  The text typed into the input gadget is found in a field by name Value$ in
  the OkButton structure:

    PRINT "Your name is: ",NameInput.Value$


  As the  final task we have to close the window (and all items inserted in
  the window):

    ComalScreen.RemObject(DemoWindow)


  The complete program looks like:

    USE CITScreen
    USE CITWindow
    USE CITGadgets

    DIM DemoWindow OF CITWindow
    DemoWindow.Size(500,100)
    DemoWindow.Position(60,50)
    DemoWindow.Activate
    ComalScreen.InsObject(DemoWindow,Error)
    IF Error THEN
      STOP "Could not create window"
    ENDIF

    DIM NameInput OF StringGadget
    NameInput.Size(300,15)
    NameInput.Position(160,10)
    NameInput.Label("Enter your name: ",LEFT)
    DemoWindow.InsObject(NameInput,Error)

    DIM OkButton OF ButtonGadget
    OkButton.Size(40,15)
    OkButton.Position(10,-(6+15))
    OkButton.Label("Ok",INSIDE)
    DemoWindow.InsObject(OkButton,Error)

    IF Error THEN
      STOP "Could not create one or more gadgets"
    ENDIF

    WHILE NOT OkButton.Pressed DO WAIT

    PRINT "Your name is: ",NameInput.Value$

    ComalScreen.RemObject(DemoWindow)

                                         166







  In our next example  we will  add a  Cancel button  to the  window and an
  event procedure to each of the button gadgets. These procedures are called
  each time the respective  buttons are  pressed. The  OkEvent procedure is
  added in this way:

    OkButton.EventHandler(OkEvent())

      :

    PROC OkEvent(Id OF USHORT)
      Terminate:=1
    ENDPROC OkEvent


  The Cancel button (including the event procedure) is made in this way:

    DIM CancelButton OF ButtonGadget
    CancelButton.Size(60,15)
    CancelButton.Position(-(10+60),-(6+15))
    CancelButton.Label("Cancel",INSIDE)
    CancelButton.EventHandler(CancelEvent())
    DemoWindow.InsObject(CancelButton,Error)
    PROC CancelEvent(Id OF USHORT)
      Terminate:=2
    ENDPROC CancelEvent


  Having done  this and having testet for errors we will go to sleep. While
  sleeping the system will call the event procedures for you:

    WHILE NOT Terminate DO WAIT


  The complete program looks like (note that the variable Terminate  has to
  be dimensioned before you go to sleep):

    USE CITScreen
    USE CITWindow
    USE CITGadgets

    DIM Terminate OF SHORT

    DIM DemoWindow OF CITWindow
    DemoWindow.Size(500,100)
    DemoWindow.Position(60,50)
    DemoWindow.Activate
    ComalScreen.InsObject(DemoWindow,Error)
    IF Error THEN

                                         167





      STOP "Could not create window"
    ENDIF

    DIM NameInput OF StringGadget
    NameInput.Size(300,15)
    NameInput.Position(160,10)
    NameInput.Label("Enter your name: ",LEFT)
    DemoWindow.InsObject(NameInput,Error)

    DIM OkButton OF ButtonGadget
    OkButtonSize(60,15)
    OkButtonPosition(10,-(6+15))
    OkButtonLabel("Ok",INSIDE)
    OkButtonEventHandler(OkEvent())
    DemoWindow.InsObject(OkButton,Error)
    PROC OkEvent(id OF USHORT)
      Terminate:=1
    ENDPROC OkEvent

    DIM CancelButton OF ButtonGadget
    CancelButton.Size(60,15)
    CancelButton.Position(-(10+60),-(6+15))
    CancelButton.Label("Cancel",INSIDE)
    CancelButton.EventHandler(CancelEvent())
    DemoWindow.InsObject(CancelButton,Error)
    PROC CancelEvent(id OF USHORT)
      Terminate:=2
    ENDPROC CancelEvent

    IF Error THEN
      STOP "Could not create one or more gadgets"
    ENDIF

    WHILE NOT Terminate DO WAIT

    CASE Terminate OF
    WHEN 1
      PRINT "Hello ",NameInput.Value$,". Welcome to the world of CIT"
    WHEN 2
      PRINT "You don't know your name!?"
    ENDCASE

    ComalScreen.RemObject(DemoWindow)


  In the tutorial other examples are shown (chapter II.8-9).





                                         168





  11.3 CIT reference.

  In the following subsections you will find a complete description of all the
  modules containing CIT objects, the object methods and the attributes that
  can be set in the objects.


  11.3.1 CITWorkbench.

  The  CITWorkbench  module  containns  the  basic object CITWorkbench. You
  cannot create a CITWorkbench object yourself. It is already created in the
  module.

  The following methods in CITWorkbench can be used:


    PROC InsObject(REF WBObject OF WorkbenchClass,REF Error OF SHORT)

      An object  of the class WorkbenchClass is placed on the CITWorkbench.
      For the time being  CITScreen is  the only  member of  the Workbench-
      Class.


    PROC RemObject(REF WBObject OF WorkbenchClass)

      Remove an object from the the CITWorkbench.


  If variables in the program are cleared all objects inserted in CITWorkbench
  are automatically removed.


  11.3.2 CITScreen.

  The object CITScreen found in  the  CITScreen  module  is  member  of the
  WorkbenchClass (the only member for the time being).

  A CITScreen  has to be created in a DIM (or LOCAL) statement and inserted
  in CITWorkbench. The screen will be a high resolution screen  (640 pixels
  horizontal).

  Example: 

    DIM MyScreen OF CITScreen
    MyScreen.Label("My Own Screen")
    MyScreen.Depth(3)
    CITWorkbench.InsObject(MyScreen,Error)


  The following methods in CITScreen can be used:

                                         169







    PROC InsObject(REF ScrObject OF ScreenClass,REF Error OF SHORT)

      An object of the class ScreenClass is placed in a CITScreen object.

      For the time being CITWindow is the only member of the ScreenClass.

    
    PROC RemObject(REF ScrObject OF ScreenClass)

      Remove a ScreenClass object from the CITScreen


    PROC Label(t$)

      Place a text in the drag bar of the screen. This method may be called
      only before the screen has been inserted in CITWorkbench.

    
    PROC Depth(d OF SHORT)

      Set the depth (the number of colors) of  the screen.  This method may
      be called only before the screen has been inserted in CITWorkbench.


    PROC Lores

      Make the screen a low resolution screen (the horizontal resolution is
      halfed).  This method may be called only  before the  screen has been
      inserted in CITWorkbench. 


    PROC Interlace

      Make the screen an interlace screen (the vertical resolution is doubled).
      This method may be called only before the screen has been inserted in
      CITWorkbench. 


    PROC ToBack

      Send the  screen behind all other screens.  This method may be called
      only after the screen has been inserted in CITWorkbench.


    PROC ToFront

      Bring the screen in front of all other screens. This method may be cal-
      led only after the screen has been inserted in CITWorkbench.

                                         170







    PROC Font(FontName$,FontHeight OF SHORT)

      Set the default font of this screen. FontName$ is the name of the font
      (including the '.font' extension) and FontHeight is the desired height of
      the font.

      If the  font with  the name  and the specified height cannot be found
      nothing will happen.


  Two predefined ready to use screens are exported from the  CITSreen modu-
  le: ComalScreen  (the screen used by the Comal system ) and WBScreen (the
  Workbench screen). They are used in  the  same  way  as  other CITScreens
  (except that they cannot be inserted in or removed from CITWorkbench).


  11.3.3 CITWindow.

  In the module CITWindow the object CITWindow is found.

  CITWindow  is  member  of  the  ScreenClass (the only member for the time
  being). A CITWindow has to be created in a DIM  (or LOCAL)  statement and
  inserted in a screen.


  Example: 

    DIM DemoWindow OF CITWindow
    DemoWindow.Size(530,150)
    ComalScreen.InsObject(DemoWindow,Error)


  The following methods in CITWindow can be used:


    PROC InsObject(REF WdObject OF WindowClass,REF Error OF SHORT)

      An object of the class WindowClass is placed in the CITWindow.

      All  kinds   of  IDCMP   events  except   MOUSEBUTTON  and  MOUSEMOVE
      events will be resend to the object as soon as they are recieved by the
      window.

    
    PROC RemObject(REF WdObject OF WindowClass)

      Remove a WindowClass object from the CITWindow.


                                         171






    PROC Position(x OF USHORT,y OF USHORT)

      Set position of the window. This method may be called both before and
      after the window has been inserted in a screen.


    PROC Size(w OF USHORT,h OF USHORT)

      Set the size of the window. This method may be called both before and
      after the window has been inserted in a screen.


    PROC Label(t$)

      Set the window title. This method may be called both before and after
      the window has been inserted in a screen.


    PROC Activate

      Make window the active.  This method  may be  called both  before and
      after the window has been inserted in a screen.


    PROC DepthGadget

      Make a  depth gadget  in the  upper right  corner of the window. This
      method may be called only before  the window  has been  inserted in a
      screen.


    PROC SizingGadget

      Make a  sizing gadget  in the  lower right corner of the window. This
      method may be called only before  the window  has been  inserted in a
      screen.


    PROC DragBar

      Add a  drag bar  to the  window (so  that the  window can be moved by
      the user). This method may be called only before the  window has been
      inserted in a screen.


    PROC NoBorder

      The window  will be  opened without a border. This method may be cal-
      led only before the window has been inserted in a screen.

                                         172






    PROC InterruptPriority(n BYTE)

      Set interrupt priority of window interrupts. This method may be called
      only before the window has been inserted in a screen


    PROC ToFront

      Bring the  window in  front of  all other windows on the screen. This
      method may be called  only after  the window  has been  inserted in a
      screen.


    PROC ToBack

      Bring the  window behind all other windows on the screen. This method


    PROC CloseGadget

      Make a  close gadget  in the  upper left  corner of  the window. This
      method may  be called  only before  the window has been inserted in a
      screen.


    FUNC ClosePressed

      This boolean function returns  TRUE if  the windows  close gadget has
      been pressed. The status of the gadget will be set to FALSE after the
      call.


    PROC CloseEventHandler(p OF CloseEventProc)

      By calling this method with a procedure p, this procedure will be called
      immediately if the close gadget is pressed.

      The parameter p is a procedure without parameters.

      Example:
          DemoWindow.CloseEventHandler(CloseEvent)

            :

          PROC CloseEvent

            <do necessary actions>

          ENDPROC CloseEvent

                                         173







    PROC SelectEventHandler(EventProc OF ButtonEventProc)

      By calling this the method the procedure EventProc will be called when
      the left mouse button (select button) is pressed.

      EventProc is an event procedure of the form:

        PROC Button(Down OF BYTE,x OF SHORT,y OF SHORT)

      At the call to the event procedure the parameter Down is TRUE  if the
      button was  pressed or  FALSE if it was released. x and y are the the
      mouse position coordinates.

      Example:

        DemoWindow.SelectEventHandler(Button(,,))

          :

        PROC Button(Down OF BYTE,x OF SHORT,y OF SHORT)
          <do anything>
        ENDPROC Button


    PROC MenuEventHandler(EventProc OF ButtonEventProc)

      By calling this the method the procedure EventProc will be called when
      the right mouse button (menu button) is pressed.

      For further description see SelectEventHandler.

      Note that this method must be called before the window is inserted in
      the screen. Alse note that after calling this  you cannot  have menus
      attached to the window.


    PROC PointerEventHandler(EventProc OF PointerEventProc)

      If the  method MouseMove  has been  called with the actual value TRUE
      the EventProc will be called when the mouse pointer is moved.

      EventProc is an event procedure of the form:

        PROC MousePos(x OF SHORT,y OF SHORT)


      At the call to the event procedure the parameters x and y are the the
      mouse position coordinates.

                                         174






      Note that you have to respond to the event very fast.


    PROC MouseMove(b OF SHORT)

      Start  mouse  move  event  calling.  As  described  above  your event
      procedure will be called if the parameter b has the value TRUE.


    PROC Coordinates(REF x OF SHORT,REF y OF SHORT)

      This method will return the current position coordinates of the mouse
      pointer.

      Example:

        DemoWindow.EventHandler(MouseMove(,))
        DemoWindow.MouseMove(TRUE)
          :
        PROC MouseMove(x OF SHORT,y OF SHORT)
          PRINT AT 20,5: USING "x = -### ,  y = -###": x,y,
        ENDPROC MouseMove


  A predefined  ready to  use window  ComalWindow is exported from the CIT-
  Window module. This is the standard IO window.

  Only the  methods InsObject,  RemObject, ToFront  and ToBack  can be used
  with ComalWindow  and the  window cannot  be removed  from its containing
  screen. Otherwise the window is used  in almost  the same  way as CITWin-
  dows. 


  11.3.4 CITBorder.

  The module CITBorder contains the window class objects CITBorder.

  The CITBorder  is used to draw borders in the window and is a rather spe-
  cial object that you will probably never use. It is inherited by the CITView
  and CITText. It contains the following methods:


    PROC Position(LeftEdge OF SHORT,TopEge OF SHORT)

      Set the position of the upper left corner of the border. Like gadgets
      the position coordinates may be negative. The the position of the upper
      left corner will be placed relative to the right and bottom border of
      the window.


                                         175






    PROC Size(Width OF USHORT,Height OF USHORT,Direc OF USHORT)

      Set the size of the border. The parameter Direc specifies if the border
      should be raised (use the value UP), recessed (use the value DOWN) or
      if there should be no visible border (use the value NOBORDER).

      The default size of the border is the size of  the containing element
      (CITWindow or CITView).


  The CITBorder  is inherited  by some  other WindowClass objects (CITView,
  CITText and CITGraphics).




  11.3.5 CITView.

  The CITView object is a WindowClass object that at first sight looks like
  the CITBorder (which it iherits) but it is a very special WindowClass object
  in that any WindowClass object (including the CITView itself) can be inser-
  ted in (and sometimes removed from) a CITView.

  A WindowClass object inserted in a CITView is placed relative to the bor-
  der of the view.

  The CITBorder contains the Size and Position methods  inherited from CIT-
  Border as  well as  InsObject and RemObject (see CITWindow for a descrip-
  tion of these methods).


  11.3.6 CITGadgets.

  In this module you will find a number of different gadgets to be inserted in
  a window.

  All these  gadgets are  member of  the class GadgetClass. Members of this
  class have these methods (with some minor individual modifications) along
  with other special methods:

    PROC Disable

      Disables the gadget so that the user cannot use it. Most of the gadgets
      will appear ghoasted.


    PROC Enable

      Enables the gadget.

                                         176







    PROC Position(LeftEdge OF SHORT,TopEdge OF SHORT)

      Set the position (of the upper left corner of the  gadget) inside the
      window (or view). If inserted in a window the parameters LeftEdge and
      TopEdge are relative to the inner of a window (excluding borders).

      If the parameters (one ore both) are negative the position of the gad-
      gets upper left corner will be relative to the right/lower border of the
      window (view).


    PROC Size(Width OF USHORT,Height OF USHORT)

      Set the width and height of the gadget.


    PROC Label(text$,Flags OF ULONG)

      Set a text to be shown with the gadget. The Flags is  used to specify
      the position and the highlightning status of the text:

      The position values are

        LEFT        -   place text to the left of the gadget
        RIGHT       -   place text to the right of the gadget
        ABOVE       -   place text above the gadget
        BELOW       -   place text below the gadget
        INSIDE      -   place text inside the gadget


      These position values may be BITORed (or added) with

        HIGHLIGHT   -   show text highlighted (white)


    PROC Font(Name$,Height OF SHORT)

      Set the name of the font to be used in the label. If the font cannot be
      found the default font (topaz80) will be used.


    PROC Id(ID OF USHORT)

      Set an identification number. This number is for your use only.





                                         177





    PROC EventHandler(p OF GadEventProc)

      By calling this method with a procedure p, this procedure will be called
      immediately if  the gadget  is activated  (for input gadgets when you
      press the <ENTER> key).

      The parameter p is a procedure with one  USHORT parameter  (the iden-
      tifiction  number set by the method Id).


  All the gadgets (except the TextGadget) have a current state. For all gad-
  gets this state can be read in the field Value (Value$ in the StringGadget).

  Note that this Value field should never be changed directly. If it is possible
  to change it, there will be a method available for this purpose.



  11.3.6.1 TextGadget.

  The TextGadget is a special read  only gadget.  A TextGadget  has a fixed
  label (set by the Label method) and a changeable text.

  The following methods are special for the TextGadget:


    PROC Text(t$)

      Set the  (changable) text.  The method  may be called both before and
      after the gadget has been inserted in a window.


    PROC Border

      Call this method to get a recessed border around the text.


  Example:

    DIM Text OF TextGadget
    Text.Position(214,5)
    Text.Text("CIT demo")
    DemoWindow.InsObject(Text,Error)

  Since this gadget is a read only gadget it makes no sence to add an event
  procedure to this gadget.





                                         178





  11.3.6.2 ButtonGadget.

  The well  known button gadget adds the following method to all the common
  methods:


     FUNC Pressed OF SHORT

      This boolean function returns  TRUE if  the gadget  has been pressed.
      The status of the gadget will be set to FALSE after the call.



  11.3.6.3 CheckboxGadget.

  The CheckboxGadget has the following methods:

    PROC On

      Set the state of the gadget to ON (the check mark will be shown). The
      method may be called both before and after the gadget has been inser-
      ted in a window.


    PROC Off

      Set the  state of the gadget to OFF (the check mark will be removed).
      The method may be called both  before and  after the  gadget has been
      inserted in a window.


  11.3.6.4 StringGadget.

  The StringGadget is used to enter a string of ASCII characters. This gadget
  has the following special method:


    PROC Replace

      Use this method to  change  the  typing  mode  to  the  replace mode.
      Otherwise it is insert mode. The method may only be called before the
      gadget has been inserted in a window.


    PROC TabCycle

      Make the gadget a TabCycle gadget. If the user types Tab or Shift-Tab
      into a  TabCycle gadget  the next or previous TabCycle gadget will be
      activated. The method may only be called before  the gadget  has been
      inserted in a window.

                                         179







    PROC MaxChar(n OF USHORT)

      This method  sets the  maximum number of input characters in the gad-
      get. The default number is 64. The method  may only  be called before
      the gadget has been inserted in a window.


    PROC Text(t$)

      Set the  content of the gadgets input field. The method may be called
      both before and after the gadget has been inserted in a window.


    PROC Activate

      Activate the gadget so that you can type in your text. The method may
      only be called after the gadget has been inserted in a window.


  Example:

    DIM StringGad OF StringGadget
    StringGad.Label("Type in a text",LEFT)
    StringGad.Position(150,48)
    StringGad.Size(170,15)
    DemoWindow.InsObject(StringGad,Error)



  11.3.6.5 IntegerGadget.

  The IntegerGadget is used to enter an integer number. This gadget has the
  following special method:


    PROC Replace

      Use this method to  change  the  typing  mode  to  the  replace mode.
      Otherwise it is insert mode. The method may only be called before the
      gadget has been inserted in a window.


    PROC TabCycle

      Make the gadget a TabCycle gadget. If the user types Tab or Shift-Tab
      into a  TabCycle gadget  the next or previous TabCycle gadget will be
      activated. The method may only be called before  the gadget  has been
      inserted in a window.

                                         180







    PROC MaxChar(n OF USHORT)

      This method  sets the  maximum number of input characters in the gad-
      get. The default number is 64. The method  may only  be called before
      the gadget has been inserted in a window.


    PROC Number(n OF ULONG)

      Set the  content of the gadgets input field. The method may be called
      both before and after the gadget has been inserted in a window.


    PROC Activate

      Activate the gadget so that you can type  in your  number. The method
      may only be called after the gadget has been inserted in a window.



  11.3.6.6 SliderGadget.

  A SliderGadget  is a  gadget used  to show  or control the amount of some
  quantity like a color, a volume or an intensity.

  This gadget has the following methods:


    PROC Limits(Min OF SHORT,Max OF SHORT)
    
      The method is used to set the level limits, i.e. the minimum and maxi-
      mum value  of the quantity in question. The method may only be called
      before the gadget has been inserted in a window.


    PROC Level(l OF SHORT)

      The method is use to set the actual level of the quantity. The method
      may be called both before and after the gadget has been inserted in a
      window.


  In the string used as parameter in the Label method (common for  all gad-
  gets) you  may place  the formatting  characters '#'  as in a PRINT USING
  statement. Then the current level will be printed.




                                         181





  Example:

      DIM SliderGad OF SliderGadget
      SliderGad.Position(150,88)
      SliderGad.Limits(0,100)
      SliderGad.Label("Fraction:###%",LEFT)
      DemoWindow.InsObject(SliderGad,Error)

    As a result of the line 

        SliderGad.Label("Fraction:###%",LEFT)

    a text of the form

        Fraction: 37%

    will be printed on the left side of the gadget.




  11.3.6.7 ScrollerGadget.

  A ScrollerGadget is a gadget used to show and/or adjust  the fraction and
  the position of a limited view into a larger area. The ScrollerGadget is a
  well known gadget. It is placed on the right side of the Comal editor win-
  dow and it is used in all the drawer windows opened by Workbench

  This gadget has the following methods:


    PROC Top(t OF SHORT)

      Sets the first visible position in the area that the scroller represents.
      The method may be called both  before and  after the  gadget has been
      inserted in a window.


    PROC Total(t OF SHORT)

      Sets the total size of the area that the scroller represents. The method
      may be called both before and after the gadget has been inserted in a
      window.


    PROC Visible(v OF SHORT)

      Sets the  number of  visible elements.  The method may be called both
      before and after the gadget has been inserted in a window.


                                         182






    PROC Arrows(Size OF SHORT)

      Place arrows at the end of the scroller. The parameter is the width of
      each arrow button for a horizontal scroller or the height for a vertical
      scroller. The default size is 10. Use a size value  of zero  to avoid
      arrows.

      The method  may only be called before the gadget has been inserted in
      a window.


    PROC Orientation(Or OF SHORT)

      Set the orientation of the scroller, The parameter values are:

        HORIZONTAL    -   make a horizontal scroller
        VERTICAL      -   make a vertical scroller

      The default orientation is horizontal.

      The method may only be called before the gadget has  been inserted in
      a window.


  The value found in the field Value is the current Top value.


  Example: Let's say we have a text of 137 lines (numbered 0 .. 136) and we
    can see 21 lines on the display (currently from line 32 to 52). The fol-
    lowing lines will create a scroller representing the text:

      DIM Scroler OF ScrollerGadget
      Scroller.Position(500,5)
      Scroller.Size(15,150)
      Scroller.Total(137)             // A total of 137 lines
      Scroller.Top(32)                // First visible (counting from zero)
      Scroller.Visible(21)            // 21 visible lines
      Scroller.Orientation(VERTICAL)
      DemoWindow.InsObject(Scroller,Error)



  11.3.6.8 CycleGadget.

  A CycleGadget is used to allow the user to choose one among several choi-
  ces. It appears as a raised rectangular button. A circular arrow glyph ap-
  pears to the left and the current choice to the right. Clicking once on the
  gadget the next choice in a list will appear, while shift-clicking will show
  the previous choice.

                                         183






  This gadget has the following methods:


    PROC Choices(REF Texts$())

      The texts in the array Texts$() (all texts or up to the first empty text)
      is the choices that will appear in the gadget. These texts must be valid
      through the whole lifetime of the gaget.

      The method  may be  called both  before and after the gadget has been
      inserted in a window.


    PROC Active(n OF SHORT)

      Sets the choice with the ordinal number n (counting from zero) as the
      active choice in the gadget.  The method may be called both before and
      after the gadget has been inserted in a window.


  Example:

    DIM CycleChoice$(4) OF 20
    READ CycleChoice$()
    DATA "Choice number 1"
    DATA "Choice number 2"
    DATA "Choice number 3"
    DATA "Choice number 4"

    DIM CycleGad OF CycleGadget
    CycleGad.Position(330,8)
    CycleGad.Size(190,14)
    CycleGad.Label("Press here",LEFT)
    CycleGad.Choices(CycleChoice$())
    CycleGad.Active(2)                    // 'Choice number 3'
    DemoWindow.InsObject(CycleGad,Error)


  11.3.6.9 RadioButtonGadget.

  Like the CycleGadget the RadioButtonGadget allow  the user  to choose one
  option from among several.

  All the choices are shown as a text beside a small raised oval that looks
  like a small radio button. Exactly one one button is recessed and highligh-
  ted to indicate the selected choice.

  This gadget has the following methods:


                                         184





    PROC Choices(REF Texts$(),Place OF ULONG)

      The texts in the array Texts$() (all texts or up to the first empty text)
      is the choices that will appear. These texts must be valid through the
      whole lifetime of the gaget.

      The parameter  Place specifies  where the  text is  to be placed. The
      values are:

        LEFT  - place text on the left side of the buttons
        RIGHT - place text on the right side of the buttons

      The method may only be called before the gadget has  been inserted in
      a window.


    PROC Spacing(s OF SHORT)

      Sets the amount of spacing (in pixels) between each button. The default
      value is zero (not very nice). The method may only be called before the
      gadget has been inserted in a window.


    PROC Active(n OF SHORT)

      Sets the choice with the ordinal number n (counting from zero) as the
      active choice in the gadget. The method may be called both before and
      after the gadget has been inserted in a window.


  11.3.6.10 ListViewGadget.

  A ListViewGadget,  like the CycleGadget and the RadioButtonGadget, allows
  the user to select one among several choices. It consists of a scroller with
  arrows, an area where the choices are visible and an optional place where
  the current selection is shown. The user can browse through the list using
  the scroller  or the  arrows and  may select an entry by clicking on that
  item.

  This gadget has the following methods:


    PROC ChoiceArray(REF Texts$())

      The texts in the array Texts$() (all texts or up to the first empty text)
      is the choices that will appear in the gadget. These texts must be valid
      through the whole lifetime of the gaget.

      The method may be called both  before and  after the  gadget has been
      inserted in a window.

                                         185







    PROC ChoiceList(REF List OF List)

      This is  an alternative way of supplying the choices. The choices are
      put ito the ln_Name field of an Exec list.

      The method may be called both  before and  after the  gadget has been
      inserted in a window.


    PROC Spacing(s OF USHORT)

      Sets the amount of spacing (in pixels) between each entry in the list-
      view. The default value is zero. The method may only be called before
      the gadget has been inserted in a window.


    PROC Top(n OF USHORT)

      The ordinal number (counting from zero) of the top item visible in the
      listview. The method may be called both before  and after  the gadget
      has been inserted in a window.


    PROC ScrollWidth(w OF USHORT)

      Sets the width of the scroller used in the listview. The method may be
      called only before the gadget has been inserted in a window.


    PROC ReadOnly

      Make the listview a read-only listview. The method may be called only
      before the gadget has been inserted in a window.


    PROC ShowSelected(REF StrgGad OF StringGadget)

      Use this method to attach a StringGadget to the listview. The currently
      selected item will be shown in the gadget  (ready to  be edited). The
      parameter StrgGad  must have  been created  (in a DIM or LOCAL state-
      ment) but not inserted in a window.

      The method may be called only before the gadget has  been inserted in
      a window.





                                         186





    PROC Selected(s OF USHORT)

      Ordinal number (counting from zero) of the item to be placed into the
      display under the listview.

      The method may be called both  before and  after the  gadget has been
      inserted in a window. If called before without a prior call of the Show-
      Selected method, a readonly TextGadget will be created.

  Example:

    DIM ListViewTexts$(4) OF 20
    ListViewTexts$(1):="Choice1"
    ListViewTexts$(2):="Choice2"
    ListViewTexts$(3):="Choice3"
    ListViewTexts$(4):="Choice4"

    DIM ListView OF ListViewGadget
    ListView.Position(200,128)
    ListView.Size(100,40)
    ListView.ChoiceArray(ListViewTexts$())
    ListView.Selected(1)                    // A TextGadget is created
    DemoWindow.InsObject(ListView,Error)


  11.3.6.11 PaletteGadget.

  The PaletteGadget lets the user pick a colour from a set of several. It con-
  sists of a number of coloured squares, one for each colour available.

  This gadget has the following methods:


    PROC Color(c OF UBYTE)

      The selected colour of the palette. Default is zero.

      The method  may be  called both  before and after the gadget has been
      inserted in a window.


    PROC IndicatorWidth(w)

      Specifies the width of the current-colour indicator. The default width is
      20 pixels.

      The method  may be called only before the gadget has been inserted in
      a window.



                                         187





    PROC IndicatorHeight(h)

      This method asks for a current-colour indicator to be placed above the
      the colour selection squares. The height is specified as a parameter.

      The method  may be called only before the gadget has been inserted in
      a window.


    PROC Depth(d OF SHORT)

      Specifies the number of bitplanes that the gadget represents. Default is
      two. The  method may be called only before the gadget has been inser-
      ted in a window.


    PROC ColorOffset(Offset OF SHORT)

      If there are more bitplanes in the screen than specified by the Depth
      method this is the number of the first colour to be displayed. Default
      is zero.

      The method may be called only before the gadget has  been inserted in
      a window.


  Example:

    DIM Palette OF PaletteGadget
    Palette.Position(350,128)
    Palette.Color(2)                      // Color 2 is the selected one
    DemoWindow.InsObject(Palette,Error)




  11.3.7 CITText.

  The module CITText contains the window class object CITText.

  The CITText  is used  to write  texts in the window. It contains the same
  methods as  CITBorder (it  inherits CITBorder)  as well  as the following
  methods:


    PROC PenColor(c OF SHORT)

      Set the colour of the drawing pen.



                                         188





    PROC BackColor(c OF SHORT)

      Set the colour of the back ground pen. This value is only used if the
      method Transparent is called with the parameter value FALSE.


    PROC Transparent(b OF SHORT)

      If b has the value TRUE the letters are drawn transparent, i.e. no back
      ground is drawn.


    PROC Font(Name$,Height OF SHORT)

      Set the  name of the font to be used. If the font cannot be found the
      default font (topaz80) will be used.


    PROC Print(x OF SHORT,y OF SHORT,t$)

      Print the text t$ at position x,y.


  Example:

    DIM Text OF CITText
    Text.Position(100,50)
    Text.Size(250,50,DOWN)
    DemoWindow.InsObject(Text,Error)

    Text.PenColor(3)
    Text.Font("times.font",24)
    Text.Print(10,10,"Hello world!")




  11.3.8 CITGraphics.

  CITGraphics in the module CITGraphics is a window class object containing
  a (very small) subset of the graphics routines of the Graphics module.

  It contains  the same methods as CITText (it inherits CITText) as well as
  the following methods:

    PROC Clear

      Clear the graphics area defined by Position and Size.



                                         189





    PROC Color(c OF SHORT)

      Set the color of the drawing pen.


    PROC MoveTo(x,y)

      Move (without drawing) the pen to the point with coordinates (x,y).


    PROC DrawTo(x,y)

      Draw a straight line from the current point to the point with coordina-
      tes (x,y).


    PROC Plot(x,y)

      Set a dot at the point with coordinates (x,y).


    PROC DrawText(x,y,t$)

      Print a text starting at the graphics coordinates (x,y). The Print me-
      thod of CITText is used to actually print the text.


    PROC Coordinates(Xmin,Xmax,Ymin,Ymax)

      Define a new coordinate system. Xmin and Xmax  will be  the new x-co-
      ordinates of the left and right edge of the drawing area. Ymin will be
      the new y-coordinate of the bottom edge and  Ymax wil  be the y-coor-
      dinate of the top edge.


  In CITGraphics all drawing are restricted to drawing area set by the Size
  method. This is also the case with DrawText.



  11.3.9 CITMenus.

  CITMenu in  the module  CITMenus is  a window  class object  used to make
  menus in a window. It contains the following methods:


    PROC Title(Label$,Flags OF USHORT,REF MenuId OF USHORT)

      Set the title of a new menu. The parameters are:


                                         190





        Label$

          The title (the headline) of a new series of menu items.

        Flags

          The only legal non zero value is

            DISABLED    disable this menu (all items and subitems) from start.
                        The menu can be enabled by calling the method On.

        MenuId

            An identification number is returned in this variable. The value is
            used in calls to the On and Off methods.


    PROC Item(Label$,Key$,Flags OF USHORT,Exlude OF LONG,
                              Prc OF MenuEvent,REF MenuId OF USHORT)

      Make a  new menu  item belonging to the menu created by the last call
      of the method Title. The parameters are:

        Label$

          The name of the menu item.

        Key$

          The first character in this string is used as a short cut for the
          menu item. If the string is the empty string there will be no short
          cut for this item.

        Flags

          This parameter can be zero or it can take on  one or  more of the
          following values:

            DISABLED    Disable the item (and all subitems belonging to it)
                        from start. The item can be enabled by  calling the
                        method On.

            CHECKIT     A check mark will be drawn to the left of the title
                        if the item is currently selected.

            MENUTOGGLE  By using this value the user  can toggle  the check
                        mark by selecting the item. Only to be used in con-
                        nection with the CHECKIT flag value.

                        Normally you will use this flag  in connection with

                                         191





                        the CHECKIT flag or you will set an exclude mask.

            CHECKED     Make the  item selected from start. Only to be used
                        in connection with the CHECKIT flag value.

          The values can be BITORed (or added) together.

        Exclude

          Set a 1 in a binary bitmask for each item  in this  (sub)menu you
          want to be deselected when the user selects this item. The right-
          most digit in the bitmask corresponds to the first item. Only to be
          used in connection with the CHECKIT flag value.

        Prc

          This is  an event  procedure with one unsigned short (USHORT) pa-
          rameter. This procedure will be called when the item is selected. At
          the call the actual parameter value will be the menu number retur-
          ned in the REF parameter MenuId. In this way you can identify the
          item if several items (or subitems) shares an event procedure.

        MenuId

          An identification number is returned in this variable. The value is
          used in calls to the On and Off methods.


    PROC SubItem(Label$,Key$,Flags OF USHORT,Exlude OF LONG,
                              Prc OF MenuEvent,REF MenuId OF USHORT)

      Create a subitem belonging to the last item.  The parameters  are the
      same as described above (the Item method).


    PROC Bar

      Make a separator bar between the previous and the next (sub)item.


    PROC On(MenuId OF USHORT)

      Enable the  menu or (sub)item identified by MenuId (the number retur-
      ned by calls to the methods Title, Item and SubItem).


    PROC Off(MenuId OF USHORT)

      Disable the menu or (sub)item identified by MenuId (the number retur-
      ned by calls to the methods Title, Item and SubItem).

                                         192







  Example:  This example  shows part of a program (most of the event proce-
    dures are not shown) creates a menu in the standard IO window.  


      USE CITWindow
      USE CITMenus

      DIM Terminate OF SHORT

      DIM Menu OF CITMenu
      Menu.Title("Project",0,ProjectId)
      Menu.Item("Open","O",0,0,PrcOpen(),OpenId)
      Menu.Item("Save","",0,0,PrcSave(),SaveId)
      Menu.Bar
      Menu.Item("Print Mode","",0,0,PrcMode(),ModeId)
      Menu.SubItem("Draft","",CHECKIT+CHECKED,%10,PrcMode(),DraftId)
      Menu.SubItem("NLQ","",CHECKIT,%01,PrcMode(),NLQId)
      Menu.Item("Print","P",0,0,PrcPrint(),PrintId)
      Menu.Bar
      Menu.Item("Quit","Q",0,0,PrcQuit(),QuitId)
      Menu.Title("Edit",0,EditId)
      Menu.Item("Cut","X",0,0,PrcCut(),CutId)
      Menu.Item("Copy","C",0,0,PrcCopy(),CopyId)
      Menu.Item("Paste","V",0,0,PrcPaste(),PasteId)
      Menu.Bar
      Menu.Item("Undo","Z",0,0,PrcUndo(),UndoId)
      ComalWindow.InsObject(Menu,Error)

      IF Error THEN
        PRINT "Could not create menu"
      ELSE
        WHILE NOT Terminate DO WAIT
        ComalWindow.RemObject(Menu)
      ENDIF

      // *********** Event procedures ************

      PROC PrcQuit(Num OF USHORT)
        Terminate:=TRUE
      ENDPROC PrcQuit

      PROC PrcOpen(Num OF USHORT)
        :
      ENDPROC PrcOpen

      PROC PrcSave(Num OF USHORT)
        :


                                         193







  11.3.10 CITRequester.

  CITRequester in the module CITRequesters is a window class object used to
  make system requesters in a window. It contains only one method:


      FUNC Request(Text$,YesNo$) OF LONG

        The parameters are:

        Text$:  This is the body text of the requester. This text may contain
                C-language formatting characters, or the new line character,
                CHR$(10).

        YesNo$: The gadget  text for  one or more gadgets in the requester.
                Each gadget text is separated by the chracter '|'.

        The return value is zero if the rightmost gadget was pressed. Other-
        wise the return value is the number of the gadget (counting from the
        left).

        Example:

          DIM Rq OF CITRequester

            :

          CASE Rq.Request("Text modified"10"Save text?","Yes|No|Cancel") OF
          WHEN 0

            :

          WHEN 1

            :

          WHEN 2

            :

          ENDCASE



  11.4 Creating your own CIT classes.

  In principle it is a simple task to make new CIT types belonging to one of
  the excisting CITClasses: WorkbenchClass, ScreenClasss, WindowClass etc.

                                         194






  But before we can do this it is necessary to know a little more about how
  CIT works.

  All the CIT classes (except the base class CITWorkbench) contain two cen-
  tral methods:

      FUNC CreateObject(REF Alfa OF CITAlfa, .. ) OF SHORT VIRTUAL

      PROC DeleteObject VIRTUAL


  and in addition all the WindowClasses contain an event method:

      PROC HandleEvent(REF Msg OF IntuiMessage) VIRTUAL



  The value  of the  first parameter in the CreateObject method is the con-
  tainer object into which this object is to be placed. This method may have
  some other parameters as indicated.

  As can be seen in a lot of examples, an AlfaClass object is inserted in a
  CITAlfa object by executing a line of the form:

    Alfa.InsObject(AlfaClassObject,Error)


  The method InsObject now calls the method CreateObject in AlfaClassObject
  and it is in fact this method that does the actual insertion. After a succes-
  full return from CreateObject the container  object Alfa  inserts the new
  object in a book keeping list such that it knows which objects are current-
  ly inserted.

  It can also be seen from the examples, that an AlfaClass object is removed
  from a CITAlfa object by executing a line of the form:

    Alfa.RemObject(AlfaClassObject)


  The method  RemObject now  calls the  method DeleteObject in AlfaClassOb-
  ject and after that removes it from its book keeping list. If AlfaClassObject
  itself contains inserted object the DeleteObject method in AlfaClassObject
  has to remove all its inserted objects (i.e. it calls the DeleteObject method
  in all the inserted object).

  If an  AlfaClass event occurs, the object Alfa traverses the book keeping
  list and calls the HandleEvent method in all the inserted  objects one by
  one.


                                         195





  An important  requiremet for  this to work with many different objects is
  that the methods CreateObject, DeleteObject and  HandleEvent are declared
  as virtual methods.

  To make a new AlfaClass type you have to define a structure that inherits
  the base class AlfaClass or one of its ancestors. In this structure you will
  probably define new data fields and you are likely to overload one or more
  of the virtual methods CreateObject, DeleteObject and HandleEvent.

  Here is a typical example:

    STRUC ExtAlfa
      INHERIT AlfaClass

          :
      < some data fields >
          :

      FUNC CreateObject(REF Alfa OF CITAlfa) OF SHORT VIRTUAL

        // First create the ancestor object
        IF AlfaClass.CreateObject(Alfa) THEN

          < do other ExtAlfa specific creation >

          RETURN TRUE
        ELSE
          RETURN FALSE
        ENDIF

      ENDFUNC CreateObject

      PROC DeleteObject VIRTUAL

        < do ExtAlfa specific deletion >

        // Finally delete the ancestor object
        AlfaClass.DeleteObject

      ENDPROC DeleteObject

      < other methods >

    ENDSTRUC ExtAlfa


  Note that the base class AlfaClass defined in connection with the definition
  of the  CITAlfa type  is only a skeleton. It would not make much sence to
  create an object of this type (although it is possible). It is defined as fol-
  low:

                                         196






    STRUC AlfaClass
      METHODTABLE

      DIM CITAlfa OF POINTER TO CITAlfa

      PROC Terminate DESTRUCTOR
        IF CITAlfa THEN
          LOCAL AlfaClass OF POINTER TO AlfaClass

          AlfaClass:=ADR(CITAlfa)-4
          CITAlfa@.RemObject(AlfaClass@)
        ENDIF
      ENDPROC Terminate

      FUNC CreateObject(REF Alfa OF CITAlfa, .. ) OF SHORT VIRTUAL
        CITAlfa:=ADR(Alfa)
        RETURN TRUE
      ENDFUNC CreateObject

      PROC DeleteObject VIRTUAL
        CITAlfa:=0
      ENDPROC DeleteObject

      PROC HandleEvent(REF Msg OF IntuiMessage) VIRTUAL
      ENDPROC HandleEvent

    ENDSTRUC AlfaClass


  The CreateObject method stores a pointer to the container object (you are
  free to use this in your own extensions) and DeleteObject deletes this poin-
  ter. The destructor Terminate removes the object if the object is going to
  loose its data area (at return from a procedure for instance).

  The HandleEvent method is only present in the WindowClass.

  You should look into  the modules  CITWorkbench, CITScreen  and CITWindow
  to see  the actual definitions (there are some minor differences from the
  general scheme) and to see what the content of the actual container object
  is.

  Example:  As a  complete example we will make an extension of the CITWin-
    dow (a member of the ScreenClass). This extension  should have  a fixed
    default position and size and an Ok-button in the lower left corner.

    In addition  there should be a method Wait. This method will wait until
    the Ok-button has been pressed.

    The definition could be made in this way:

                                         197






      STRUC MyWindow
        INHERIT CITWindow

        USE CITGadgets
        USE IntuitionScreen

        DIM CloseButton OF ButtonGadget

        FUNC Init CONSTRUCTOR
          NewWindow.LeftEdge:=50
          NewWindow.TopEdge:=30
          NewWindow.Width:=500
          NewWindow.Height:=150
          CloseButton.Size(60,14)
          CloseButton.Position(5,-(14+3))
          CloseButton.Label("STOP",INSIDE)
          RETURN TRUE
        ENDFUNC Init

        FUNC CreateObject(REF CITScr OF CITScreen0) OF SHORT VIRTUAL
          LOCAL Error OF SHORT

          IF CITWindow.CreateObject(CITScr) THEN
            InsObject(CloseButton,Error)
            IF Error THEN
              CITWindow.DeleteObject
              RETURN FALSE
            ELSE
              RETURN TRUE
            ENDIF
          ELSE
            RETURN FALSE
          ENDIF
        ENDFUNC CreateObject

        PROC Wait
          WHILE NOT CloseButton.Pressed DO WAIT
        ENDPROC Wait

      ENDSTRUC MyWindow

    Note that there is no DeleteObject  in the  structure. The DeleteObject
    method in CITWindow will do the work.


  You should  look through  the different CIT-modules to see other examples
  on definition of CIT classes.



                                         198





  V. MAKING EXECUTABLE PROGRAMS

  There are two ways to make executable programs (i.e. programs that can be
  started by clicking on its icon) out of a Comal program text.

  One way  is to  store the  program as a code file. To execute the program
  the whole Comal system (the interpreter and all modules used  in the pro-
  gram) must be present on the system disk.

  Another way is to combine the interpreter, the program and all the modules
  used by the program into one executable file.  This file  can be executed
  without the presence of the Comal system.


  1 Comal code file.

  A Comal  program can  be stored  on disk as a code file by using the menu
  item Save.. in the Programs menu.

  By double clicking on the icon created along with the code file, the pro-
  gram is started (without loading the editor).

  The program  can be  started from  the Shell (CLI) by executing a command
  like:

      Comal.Starter [parameters] ProgramName


  Example:

      Comal.Starter SCREEN=Workbench Bitplane=3 Programs/DemoProgram

    The parameter SCREEN=Workbench is  used by  Comal.Starter. Both parame-
    ters can be read by the program.


  The program is executed as a separate process and the current directory at
  start is the directory containing the code file.

  The code file is much shorter than the combined file described in the next
  section and  it is  the best  way to make an executable file if the Comal
  system is present.


  2 Combined files.

  The Combiner combines the interpreter, a program and all modules  used by
  this program into one single file that can be executed independent of the
  Comal system.


                                         199





  The Combiner strips off the comments from the  program and  all the Comal
  modules and it strips off the information text from mashine coded modules
  in the SystemModules directory before it combines it.

  The combined file can be executed by double clicking on  the icon created
  by the  Combiner or by starting it from the Shell (CLI) in the normal way
  (startup parameters are managed just like TOOLTYPE's put into the icon).

  The current directory at start of the execution is the directory containing
  the file.


  2.1 Starting the Combiner from the editor.

  The simplest  way to  make a  combined file is to start the Combiner from
  the editor by using the Combine.. menu item in the  Programs menu. Before
  the Combiner is started a file requester will pop up, and you may select the
  name and destination of the combined file.

  The current workspace size and stack size as well as the current state of
  Automatic Variables and Execute Window are used as default values for the
  combined file (may be changed by using the Tool Types in the icon).

  If the editor uses the Workbench screen, the Tool Type

      SCREEN=Workbench

  is put into the icon of the combined file.



  2.2 Starting the Combiner from Workbench.

  The Combiner can be started from the Workbench. Activate the  icon of the
  Combiner by clicking once on its icon, press the shift key and double click
  on the icon of a program (text or code).

  The following Tool Types can be put into the icon of the  Combiner or the
  icon of the program:


    SCREEN

      The only legal value is Workbench:

        SCREEN=Workbench

      The default value is a private screen.



                                         200





    AUTOVAR

      This tool type is used to select the default AutoVar state of the com-
      bined file. The default value for the Combiner is On.

      Example:  AUTOVAR=Off


    EXECWINDOW

      This tool type is used to select the default ExecWindow  state of the
      combined file. The default value for the Combiner is On.


    WORKSPACE

      Selects the  size of  the workspace used by the combined program. The
      default value is 75000 bytes.

      Example:  WORKSPACE=60000


    STACK

      Selects the size of the stack, if the combined program is started from
      Workbench. The default value is 8Kb.

      Example:  STACK=16000

      If the  combined program is started from the Shell, the stack size of
      the Shell will be used.


    OUTPUT

      Specify the path and/or the name of the ouput from the combiner.

      Example:  OUTPUT=Comal:Programs/
                OUTPUT=ram:Hanoi

      If the name ends with ':' or '/' the tool value is treated as a path and
      the name of the output will be the same as the input file name.


  If no output file path is specified the name of the combined file will be the
  name of the input file with the extension '.cmb'.





                                         201





  2.3 Starting the Combiner from the Shell (CLI).

  The Combiner may be started from the Shell. The command is:

      Combiner [options] ProgramName


  The options are the same as the Tool Types used if the Combiner is started
  from the Workbench.

  Example:

      Combiner OUTPUT=ram: WORKSPACE=50000 Demos/Polygon2



  3 Using ToolTypes in executable files.

  The field Tool Types inside the icon of an executable file (a code file or a
  combined file) is where you can set parameters for the program.

  The TOOLTYPE's and the ToolValue's recognized  by the  system are descri-
  bed below. All TOOLTYPE's are transfered to the program.


    SCREEN

      By using the SCREEN tool type you select if Comal should open its own
      screen or use the workbench screen. The only legal value is Workbench.

        SCREEN=Workbench

      The default value is a private  screen. But  this screen  will not be
      opened if EXECWINDOW is set to Off.


    AUTOVAR

      This tool type is used to select if the program should create variables
      automatically or if all variables has to be declared in a DIM or LOCAL
      statement before use.

      Example:  AUTOVAR=Off


    EXECWINDOW

      This tool type is used to select if the program should open the stan-
      dard execute window (used by PRINT, INPUT etc.). The default value is
      On.

                                         202






      Example:  EXECWINDOW=Off


    WORKSPACE

      Selects the  size of  the workspace  used by the program. The default
      value is 75000 bytes.

      Example:  WORKSPACE=60000









































                                         203





  VI. DESCRIPTION OF THE Comal SYSTEM

  The Comal system consists of several program files. During normal program
  devellopment three of these program files are used. These files are:

    Comal

      The editor

    Comal.CodeMan

      A program  used to manipulate the program buffer. It generates inter-
      mediate code from ASCII lines and regenerate ASCII  lines from inter-
      mediate code. It inserts new lines in the buffer and deletes lines from
      the buffer. And much more.

    Comal.Interpreter

      The interpreter that executes your programs.


  The programs are executed  as separate  processes. The  editor Comal com-
  municates with Comal.CodeMan and Comal.Interpreter by sending messages.


  1 The code manipulator Comal.CodeMan.

  The program  Comal.CodeMan is used to manipulate the program buffer. Once
  started it will stay as a resident process and all comumnication with the
  process goes through its message port Comal.CodeMan.

  A message  send to this port must be either a RexxMessage (explained in a
  later section) or it must be of the form:

    STRUC CodeCommandMsg
      INHERIT Message
      DIM Command OF POINTER TO CodeCommand
    ENDSTRUC CodeCommandMsg

  where Message is a standard Exec message and CodeCommand is

    STRUC CodeCommand
      DIM Cmd OF SHORT
      DIM Status OF SHORT
      DIM PrgBuf OF POINTER TO PrgBuf
      DIM Param1 OF ULONG
      DIM Param2 OF ULONG
      DIM CmdProc@ OF CmdProc
    ENDSTRUC CodeCommand


                                         204






  Before a message is send to  Comal.CodeMan the  Cmd field  is set  to the
  command  you  want  to  be  executed  and the fields Param1 and Param2 is
  filled with corresponding command parameters.

  Before Comal.CodeMan returns the  message, it  places the  command status
  code in the field Status (zero is ok) and return values in the fields Param1
  and Param2.

  The command that can be executed are:

    CODE_KILL (1)           Kill CodeMan process

      If there are no open program  buffers the  Comal.CodeMan process will
      be removed.

      There are no return values from this command.


    CODE_OPEN (2)           Open program buffer

      Create a  new program  buffer. If  status is ok the fields PrgBuf and
      CmdProc is set. CmdProc can be used to call CodeMan  directly without
      sending a message (considerably faster).

      Command parameters:   Param1 = program buffer length
                            Param2 = initial flags

      Return values:        None


    CODE_CLOSE (3)          Close program buffer

      A  program   buffer  previously   opened  by  executing  the  command
      CODE_OPEN is closed.

      Command parameters:   None

      Return values:        None


    CODE_CLEAR (4)          Clear program buffer

      Remove the current content of the program buffer.

      Command parameters:   None

      Return values:        None



                                         205





    CODE_LOAD (5)           Read file into program buffer

      Read program stored on disk in code form into the program buffer. The
      previous content of the program buffer is lost.

      Command parameters:   Param1 = Pointer to null terminated file name

      Return values:        Param1 = Line number
                            Param2 = Total number of lines in buffer

      Note: Since Comal.CodeMan  runs as a separate process (unless CmdProc
            is used) the complete path for the file must be specified.


    CODE_SAVE (6)           Write program buffer to file

      The content of the program buffer is stored on disk in code form. The
      program buffer is unchanged.

      Command parameters:   Param1 = Pointer to null terminated file name

      Return values:        None

      Note: Since Comal.CodeMan  runs as a separate process (unless CmdProc
            is used) the complete path for the file must be specified.


    CODE_GEN (7)            Generate code for ASCII line

      Intermediate code is generated for an ASCII program line. If an error
      occurs the return parameter Param1 points to the place where the error
      was detected.

      Command parameters:   Param1 = Pointer to null terminated program line
                            Param2 = Address of buffer for the code

      Return values:        Param1 = Pointer into program line
                            Param2 = Address of buffer for the code

      Note: The buffer must be large enough to hold the code generated (500
            bytes is recommented).


    CODE_GENDIRECT (8)

      Intermediate code is generated for an ASCII program line. An error is
      reported if the code generated cannot be executed directly (if the pro-
      gram line is part of a program structure). If an error occurs the return
      parameter Param1 points to the place where the error was detected.


                                         206





      Command parameters:   Param1 = Pointer to null terminated program line
                            Param2 = Address of buffer for the code

      Return values:        Param1 = Pointer into program line
                            Param2 = Address of buffer for the code

      Note: The buffer must be large enough to hold the code generated (500
            bytes is recommented).


    CODE_REGEN (9)          Regenerate ASCII line

      Regenerate the current line (pointed at by the buffer pointer) into the
      corresponding ASCII line. This command can be viewed of as the rever-
      se of CODE_GEN.

      Command parameters:   Param1 = ASCII buffer pointer

      Return values:        Param1 = Length of ASCII line generated
                            Param2 = Line number of current line

      Note: The generated ASCII line is null terminated and the length retur-
            ned includes the terminating zero


    CODE_INSERT (10)        Insert code line in buffer

      Insert intermediate code line in program buffer at the current position.

      Command parameters:   Param1 = Address of code

      Return values:        Param1 = Line number of current line
                            Param2 = Total number of lines in buffer


    CODE_DELETE (11)        Delete current line in buffer

      The current line in buffer is deleted

      Command parameters:   None

      Return values:        Param1 = Line number of current line
                            Param2 = Total number of lines in buffer


    CODE_REPLACE (12)       Replace line in buffer

      Replace the current line with the code given as parameter.  This is a
      combination of CODE_DELETE and CODE_INSERT.


                                         207





      Command parameters:   Param1 = Address of code

      Return values:        Param1 = Line number of current line
                            Param2 = Total number of lines in buffer


    CODE_UP (13)            Move pointer n lines up

      Move the buffer pointer up n lines (or to top of buffer).

      Command parameters:   Param1 = Number of lines to move up

      Return values:        Param1 = Line number of current line
                            Param2 = Total number of lines in buffer


    CODE_DOWN (14)          Move pointer n lines down

      Move the buffer pointer down n lines (or to buttom of buffer).

      Command parameters:   Param1 = Number of lines to move down 

      Return values:        Param1 = Line number of current line
                            Param2 = Total number of lines in buffer


    CODE_BOB (15)           Move pointer to bottom of buffer

      Move the buffer pointer to bottom of buffer.

      Command parameters:   None  

      Return values:        Param1 = Line number of current line
                            Param2 = Old line number


    CODE_TOB (16)           Move pointer to top of buffer

      Move the buffer pointer to top of buffer.

      Command parameters:   None  

      Return values:        Param1 = 1
                            Param2 = Old line number

    CODE_SETADR (17)        Set pointer to specified address

      The code pointer is set to the line containing the specified address

      Command parameter:    Param1 = address

                                         208






      Return values:        Param1 = Line number of current line
                            Param2 = Address offset in current line


    CODE_SETFLAGS (18)      Set flags

      Set the CodeMan flags

      Command parameters:   Param1 = New flag values of flags to chage
                            Param2 = Mask of flag bits to change

      Return values:        Param1 = new flag values
                            Param2 = old flag values


  Flags:
    $0001:  List standard identifiers in capital
    $0002:  List in PC format


  Status codes returned by CODE_OPEN:

    NOMEM (1)           Cannot allocate memory
    NUMINITERR (2)      IEEE initialization error


  Status code  returned by other commands:                                 

    NOTDIRECT (1)       Not direct command
    NOBUFSPACE (2)      Not enough room in program buffer 
    ENDOFBUF (3)        Pointer at end of buffer
    ADDRNOTFOUND (4)    Address not found in programbuffer
    NOFILE (5)          File not found
    READERROR (6)       Error during reading
    WRITEERROR (7)      Error during writing
    ILLFORMAT (8)       Not legal Comal program file
    CODEMANINUSE (9)    Cannot kill CodeMan

  All the structures and symbolic names are defined in the  module CodeMan-
  Include.

  The following  procedure CodeToAscii is an example of the use of the com-
  mands. The procedure changes a code program file to an ASCII text file.


    PROC CodeToAscii(Name$,NewName$) CLOSED
      USE System
      USE InterpreterInclude
      USE ExecLists

                                         209





      USE PortObjects
      USE ExecLibrary

      DIM CodeManPort OF POINTER TO MsgPort
      DIM CodeManMsg OF CodeCommandMsg
      DIM ReplyPort OF MsgPort
      DIM Command OF CodeCommand
      DIM Buffer(500) OF UBYTE

      CodeManMsg.mn_ReplyPort:=ADR(ReplyPort)

      // Find CodeMan port
      CodeManPort:=FindPort("Comal.CodeMan")
      IF CodeManPort=0 THEN
        CleanUp
        STOP
      ENDIF

      // .. and open CodeMan
      Command.Cmd:=CODE_OPEN
      Command.Param1:=$8000
      Command.Param2:=0
      SendCommand(Command)
      IF Command.Status THEN
        CleanUp
        STOP
      ENDIF

      Command.Cmd:=CODE_LOAD
      Command.Param1:=ADR(Name$)
      Command.Param2:=0
      SendCommand(Command)
      IF Command.Status THEN
        CleanUp
        STOP
      ENDIF

      OPEN FILE 1,NewName$,WRITE
      REPEAT
        Command.Cmd:=CODE_REGEN
        Command.Param1:=ADR(Buffer())
        Command.Param2:=0
        SendCommand(Command)
        IF Command.Status THEN
          CleanUp
          STOP
        ENDIF
        PRINT FILE 1: CharArrayToString$(ADR(Buffer()))
        Command.Cmd:=CODE_DOWN
        Command.Param1:=1

                                         210





        Command.Param2:=0
        SendCommand(Command)
      UNTIL Command.Status

      CleanUp

      PROC SendCommand(REF Command OF CodeCommand)
        CodeManMsg.Command:=ADR(Command)
        CodeManPort@.Put(CodeManMsg)
        CodeManMsg.Wait
      ENDPROC SendCommand

      PROC CleanUp
        IF Command.CmdProc THEN
          Command.Cmd:=CODE_CLOSE
          Command.CmdProc@(ADR(Command))
          Command.CmdProc:=0
        ENDIF
        CLOSE
      ENDPROC CleanUp

    ENDPROC CodeToAscii


  2 The interpreter Comal.Interpreter.

  The program Comal.Interpreter is used to execute a  program. Once started
  it will stay as a resident process and all communication with the process
  goes through message ports.

  A message send to the port must be either a  RexxMessage (explained  in a
  later section) or it must be of the form:

    STRUC InterpreterCommandMsg
      INHERIT Message
      DIM Command OF POINTER TO InterpreterCommand
    ENDSTRUC InterpreterCommandMsg

  where  Message  is  a  standard  Exec message and InterpreterCommand is a
  structure of the form:


    STRUC InterpreterCommand
      DIM Cmd OF SHORT           // Put your command in here
      DIM Status OF SHORT        // Look for command status here
      DIM Int_Port@ OF MsgPort   // Use this port in communication
      DIM Task@ OF Task          // Interpreter task pointer
      DIM BreakSigMask OF ULONG  // Signal used to stop running program
      DIM Param1 OF ULONG        // 1. parameter
      DIM Param2 OF ULONG        // 2. parameter

                                         211





      DIM Param3 OF ULONG        // 3. parameter
      DIM Flags OF ULONG
    ENDSTRUC InterpreterCommand


  Before a message is send to Comal.Interpreter the Cmd field is set to the
  command you  want to be executed and the fields Param1 and Param2 is fil-
  led with corresponding command parameters. Before the message is returned,
  the command status code in the field Status (zero is ok) and return values
  in the fields Param1, Param2 and Param3 are set.

  The command that can be executed are:


    INT_KILL (1)            Kill interpreter

      If there are no active interpreters the master process Comal.Interpreter
      will be removed. A non zero status means that the master process can-
      not be closed (others are using it). There are no  command parameters
      or return values.


    INT_OPEN (2)            Open interpreter

      A new interpreter is opened. The new interpreter is in fact a new pro-
      cess and all the next commands are send to the  port returned  in the
      field Int_Port.

      Command parameters:   Param1 = address of open structure
                            Param2 = stack size of new process
                            Param3 = RUN flags (only if LOAD and RUN)

      The open structure has the form:

        STRUC IntOpenStruc
          DIM Flags OF ULONG
          DIM WorkspaceLength OF ULONG
          DIM PrgId OF POINTER TO UBYTE
          DIM IO_Port OF POINTER TO MsgPort
          DIM Screen OF POINTER TO Screen
          DIM InterpreterId OF POINTER TO UBYTE
        ENDSTRUC IntOpenStruc

      and the open flags are:

        INTOPEN_AUTOVAR  ($00000001)    Automatic creation of variables
        INTOPEN_COMMPORT ($00000002)    Create communication port
        INTOPEN_LOADPRG  ($20000000)    Open and load program.
        INTOPEN_OPENSCR  ($40000000)    Open separate screen
        INTOPEN_LOADRUN  ($A0000000)    Load and run as separate process

                                         212






      If flag  bit 29  is set  (LOADPRG and  LOADRUN) the  field PrgId is a
      pointer to a null terminated file name. Otherwise it is an address of a
      program buffer (returned by CODE_OPEN command to Comal.CodeMan).

      return values:        Param1 =  address of communication port (if the
                            INTOPEN_COMMPORT is set)


    INT_CLOSE (3)           Close program buffer

      Close the interpreter and remove  the  process  created  by INT_OPEN.
      The master process Comal.Interpreter is not closed.

      Command parameters:   None

      Return values:        None


    INT_SCAN (4)            Scan program

      Make a  prepass scan of the program. The address returned can be send
      to CodeMan (CODE_SETADR) to get the exact error position.

      Command parameters:   None

      Return value:         Param1 = active program buffer
                            Param2 = pointer into program line (if error)


    INT_RUN (5)             Execute program

      Execute program in main program buffer.  The address  returned can be
      send to  CodeMan (CODE_SETADR)  to get  the exact  error position. If
      the run flag RUNFLAG_STDIO  ($00000001)  is  set,  an  execute window
      will be  the standard  IO device.  Otherwise the  IO_Port in the open
      structure is used.
      
      Command parameters:   Param1 = flags

      Return values:        Param1 = active program buffer
                            Param2 = pointer into program line (if error)
                            Param3 = address of return text


    INT_COMMAND (6)         Execute line as command

      Execute a single code line as command.

      Command parameters:   Param1 = pointer to line

                                         213






      Return values:        Param1 = active program buffer
                            Param2 = pointer into program line (if error)
                            Param3 = address of return text


    INT_CONTINUE (7)        Continue

      Continue breaked program execution

      Command parameters:   None

      Return values:        None


    INT_RETPRGBUF (8)       Return program buffer

      The  commands  INT_SCAN,  INT_RUN,  INT_COMMAND  transfers  the  pro-
      gram buffer  to the interpreter and you are not allowed to change the
      buffer. To get the program buffer back execute this command.

      Command parameters:   None

      Return values:        None


    INT_DISCARD (9)         Remove all modules

      Remove all modules loaded.

      Command parameters:   None

      Return values:        None


    INT_LISTMODULES (10)    Return list of all modules

      A list of module structures for all modules are returned.

      Command parameters:   None

      Return values:        Param1 = address of first structure in list


    INT_STARTTRACE (11)     Initiate tracing mode

      Scan program and start tracing. No lines are executed.

      Command parameters:   None


                                         214





      Return values:        Param1 = active program buffer
                            Param2 = pointer into line (if error)
                            Param3 = return text


    INT_STOPTRACE (12)      Stop tracing mode

      Command parameters:   None

      Return values:        None


    INT_SINGLESTEP (13)     Execute one step

      Execute one step of program. The program must be in tracing mode.

      Command parameters:   None

      Return values:        Param1 = program buffer
                            Param2 = pointer to line (if error)
                            Param3 = return text


    INT_LINESTEP (14)       Execute one line

      Execute one line of program. The program must be in tracing mode.

      Command parameters:   None

      Return values:        Param1 = program buffer
                            Param2 = pointer to line (if error)
                            Param3 = return text


    INT_SETBREAK (15)       Set break point

      Set break point at specified line in program or module

      Command parameters:   Param1 = address of program buffer
                            Param2 = line number of break point line

      Return values:        None


    INT_CLEARBREAK (16)     Clear break point

      Clear break point at specified line

      Command parameters:   Param1 = address of program buffer
                            Param2 = line number of break point line

                                         215






      Return values:        None


    INT_CLEARALL (17)       Clear all break points

      Clear all break points in main program and modules.

      Command parameters:   None

      Return values:        None



  Status codes returned by INT_OPEN:

    NOMEM (1)           Cannot allocate memory
    NUMINITERR (2)      IEEE initialization error


  Status codes returned by all other commands are the same as is  send by a
  running program.

  The structures and all the symbolic names are defined in the module Inter-
  preterInclude.

  The following procedure StartProgram is an example of the use of the com-
  mands. The procedure starts a program stored as a code program file.


    PROC StartProgram(PrgName$) CLOSED
      USE System
      USE InterpreterInclude
      USE ExecLists
      USE PortObjects
      USE ExecLibrary

      DIM IntCmd OF InterpreterCommand
      DIM IntCmdMsg OF InterpreterCommandMsg
      DIM ReplyPort@ OF MsgPort, ReplySigMask OF ULONG
      DIM MasterPort@ OF MsgPort
      DIM OpenStruc OF IntOpenStruc
      DIM RetSigMask OF ULONG
      DIM pos OF SHORT

      // Find Interpreter master port
      MasterPort:=FindPort(MasterPortName$)
      IF MasterPort=0 THEN
        STOP "Cannot find interpreter master"
      ENDIF

                                         216






      // Remove path from program name
      pos:=LEN(PrgName$)+1
      REPEAT
        pos:-1
      UNTIL pos=0 OR PrgName$(pos..pos)=":" OR PrgName$(pos..pos)="/"
      Name$:=PrgName$(pos+1..)

      OpenStruc.Flags:=INTOPEN_LOADRUN BITOR INTOPEN_AUTOVAR
      OpenStruc.WorkspaceLength:=$8000
      OpenStruc.PrgId:=ADR(PrgName$)
      OpenStruc.Screen:=0                 // Use Workbench screen
      OpenStruc.IO_Port:=0                // No IO port
      OpenStruc.InterpreterId:=ADR(Name$) // Id of new process

      IntCmdMsg.mn_ReplyPort:=ADR(ReplyPort)
      IntCmdMsg.Command:=ADR(IntCmd)
      IntCmd.Cmd:=INT_OPEN
      IntCmd.Param1:=ADR(OpenStruc)
      IntCmd.Param2:=$4000                // Stack size
      IntCmd.Param3:=RUNFLAG_STDIO
      MasterPort@.Put(IntCmdMsg)
      IntCmdMsg.Wait

      IF IntCmd.Status<>0 THEN
        STOP "Cannot load program"
      ENDIF

      // The program is started - return

    ENDPROC StartProgram



  3 The starter program Comal.Starter.

  The  default   tool  for  a  Comal  code  program  file  is  the  program
  Comal.Starter. This program loads the interpreter Comal.Interpreter (if not
  already loaded)  and opens an interpreter process that reads the file and
  executes  it  as  a  stand  alone  process  by   using  the   open  flags
  INTOPEN_LOADRUN (almost like the procedure StartProgram in section 2).

  Having done this the Comal.Starter terminates.








                                         217





  VII. THE Comal AREXX INTERFACE

  Each of  the three  main parts  of the Comal system (Comal, Comal.CodeMan
  and Comal.Interpreter) has an  AREXX interface.  This chapter  covers the
  commands supported and how to access them.


  1 The editor AREXX interface.

  For each  project opened a separate process is started with its own AREXX
  port. The name of the port is Comal.Project.<project number>, for instance
  Comal.Project.001 and Comal.Project.002.

  The AREXX  commands accepted  by the  editor are  (NOTE: for all commands
  the return code is RC_WARN (5) if the command is currently disabled):


    NEW                 Create a new project

      Command format: NEW

      Return values:  The name of the AREXX port of the project

      Return codes:   RC_OK (0) if success
                      RC_ERROR (10) if the command failed

      Example:        NEW


    CLEAR             Clear program buffer

      Command format: CLEAR [FORCE]

      Return values:  None

      Return codes:   RC_OK (0) if success
                      RC_WARN (5) if user pressed the NO gadget

      If the program in the program buffer has been changed the user will be
      prompted to accept unless the FORCE argument is specified.

      Example:        CLEAR FORCE


    OPEN              Open and load a new project file

      Command format: OPEN [FILENAME=<name>][MODE=ASCII|CODE][FORCE]

      Return values:  None


                                         218





      Return codes:   RC_OK (0) if success
                      RC_WARN (5) if NO or CANCEL gadget pressed
                      RC_ERROR (10) if illegal or no file

      If the file name is not specified the user is prompted for a file name
      via the file requester. The MODE argument specifies the file type (an
      ASCII text file or a Comal code file). The default mode is ASCII. If the
      program in the program buffer  has  been  changed  the  user  will be
      prompted to accept unless the FORCE argument is specified.

      Example:        OPEN FILENAME=Demos/Integral1 FORCE


    SAVE              Save project

      Command format: SAVE [MODE=ASCII|CODE]

      Return values:  None

      Return codes:   RC_OK (0) if success
                      RC_WARN (5) if CANCEL gadget pressed
                      RC_ERROR (10) else

      The MODE  argument specifies  the file  type (an ASCII text file or a
      Comal code file). The default mode is ASCII. If the current project is
      unnamed the user is prompted for a file name via the file requester.

      Example:        SAVE CODE



    SAVEAS            Save project under specified name

      Command format: OPEN [FILENAME=<name>] [MODE=ASCII|CODE]

      Return values:  None

      Return codes:   RC_OK (0) if success
                      RC_WARN (5) if NO or CANCEL gadget pressed
                      RC_ERROR (10) if illegal or no file

      If the file name is not specified the user is prompted for a file name
      via the file requester. The MODE argument specifies the file type (an
      ASCII text file or a Comal code file).

      Example:        SAVEAS FILENAME=Programs/Hanoi CODE





                                         219





    PRINT             Output the program to printer

      Command format: PRINT

      Return values:  None

      Return codes:   RC_OK (0)

      Example:        PRINT


    QUIT              Terminate the project

      Command format: QUIT [FORCE]

      Return values:  None

      Return codes:   RC_OK (0) if success
                      RC_WARN (5) if user pressed the NO gadget

      If the program in the program buffer has been changed the user will be
      prompted to accept unless the FORCE argument is specified.

      Example:        QUIT


    BLOCK             Set block mode on/off

      Command format: BLOCK

      Return values:  ON or OFF dependent on  the  block  mode  state after
                      the command is executed.

      Return codes:   RC_OK (0)

      Example:        BLOCK


    BLOCKSIZE         Return the size of the current block

      Command format: BLOCKSIZE

      Return values:  The returned  number is  in fact  not the size of the
                      block. The number of lines in the block  is -RetVal+1
                      if RetVal<0 and RetVal+1 else. The number can be used
                      directly as an argument in the  LINE command  to move
                      to the other end of the block.

      Return codes:   RC_OK (0)


                                         220





      Example:        BLOCKSIZE


    CUT               Move marked block to the clip board

      Command format: CUT

      Return values:  None.

      Return codes:   RC_OK (0) if success
                      RC_ERROR (10) if out of memory

      Example:        CUT


    COPY              Move a copy of the marked block to the clip board

      Command format: COPY

      Return values:  None.

      Return codes:   RC_OK (0) if success
                      RC_ERROR (10) if out of memory

      Example:        COPY


    PASTE             Insert content of clipboard into the program

      Command format: PASTE

      Return values:  None.

      Return codes:   RC_OK (0) if success
                      RC_ERROR (10) if out of memory

      Example:        PASTE


    ERASE             Delete marked block

      Command format: ERASE

      Return values:  None.

      Return codes:   RC_OK (0)

      Example:        ERASE



                                         221





    CURSOR            Move cursor

      Command format: CURSOR UP|DOWN|LEFT|RIGHT

      Return values:  None

      Return codes:   RC_OK (0) if success
                      RC_WARN (5) if border of text

      Example:        CURSOR DOWN


    POSITION          Move cursor to specified position

      Command format: POSITION <place>  where place is:
                        SOF start of file
                        EOF end of file
                        SOL start of line
                        EOL end of line
                        SOV start of view (top of window)
                        EOV end of view (bottom of window)

      Return values:  None

      Return codes:   RC_OK (0)

      Example.        POSITION EOF


    LINE              Move cursor up/down specified number of lines

      Command format: LINE <number>

      Return values:  None.

      Return codes:   RC_OK (0) if success
                      RC_WARN (5) if border of text

      Example:        LINE -5     /* Move 5 lines up  */


    COLUMN            Move cursor left/right specified number of characters

      Command format: COLUMN <number>

      Return values:  None.

      Return codes:   RC_OK (0) if success
                      RC_WARN (5) if border of text


                                         222





      Example:        COLUMN 5    /* Move 5 characters right  */



    TEXT              Write the text argument at current cursor position

      Command format: TEXT text

      Return values:  None.

      Return codes:   RC_OK (0)

      Example:        TEXT ' this text will be written'


    NEWLINE           Output a CR character

      Command format: NEWLINE

      Return values:  None.

      Return codes:   RC_OK (0)

      The command has the same effect as pressing the enter key.

      Example:        NEWLINE


    INSLINE           Insert en empty line at the cursor position

      Command format: INSLINE

      Return values:  None.

      Return codes:   RC_OK (0)

      Example:        INSLINE


    DELCHAR           Delete character under cursor

      Command format: DELCHAR

      Return values:  None.

      Return codes:   RC_OK (0)

      Example:        DELCHAR



                                         223





    DELLINE           Delete cursor line

      Command format: DELLINE

      Return values:  None.

      Return codes:   RC_OK (0)

      Example:        DELLINE


    GETTEXT           Get the content of cursor line

      Command format: GETTEXT

      Return values:  The cursor line.

      Return codes:   RC_OK (0)

      Example:        GETTEXT


    WRITESTATUS       Write text in status line

      Command format: WRITESTATUS text

      Return values:  None.

      Return codes:   RC_OK (0)

      Example:        WRITESTATUS 'this text will be written'


    GETFILE           Get file from file requester

      Command format: GETFILE [FILENAME=<Name>][HAIL=<HailText>]

      Return values:  Selected file name (including full path).

      Return codes:   RC_OK (0) if success
                      RC_WARN (5) if CANCEL gadget presse

      The command will open a file requester. If a file name is specified this
      name will be written as the current file. The hail test will be written
      in the top of the file requester window.

      Example:        GETFILE FILENAME=System.mod HAIL=Select a module




                                         224





    INSERT            Set insert mode

      Command format: INSERT [ON|OF]

      Return values:  ON or  OFF dependent  on the  insert mode state after
                      the command is executed.

      Return codes:   RC_OK (0)

      Flip insert mode or set insert mode to on or off.

      Example:        INSERT ON



    ENTERINSERT       Set enter mode

      Command format: ENTERINSERT [ON|OF]

      Return values:  ON or OFF dependent on  the  enter  mode  state after
                      the command is executed.

      Return codes:   RC_OK (0)

      Flip enter mode or set enter mode to on or off.

      Example:        ENTERINSERT ON



  2 The CodeMan AREXX interface.

  The  CodeMan  AREXX  port  is  named  Comal.CodeMan.  The  AREXX commands
  accepted by the are:


    KILL              Remove the CodeMan process

      Command format: KILL

      Return values:  None

      Return codes:   RC_OK (0) if success
                      RC_WARN (5) if the command failed

      Example:        KILL





                                         225





    OPEN              Create new program buffer

      Command format: OPEN [MEMORY=size]

      Return values:  An address which is used to identify the buffer.

      Return codes:   RC_OK (0) if success
                      RC_ERROR (10) if the command failed

      A new program buffer is created. If MEMORY  is not  specified the de-
      fault value $8000 will be used.

      Example:        OPEN MEMORY=25000


    CLOSE             Close program buffer opened by the OPEN command.

      Command format: CLOSE ID=Id

      Return values:  None

      Return codes:   RC_OK (0) if success
                      RC_ERROR (10) if the program buffer was not found

      A  program  buffer  opened  by  the OPEN command is closed. The argu-
      ment Id is the address returned by the OPEN command.

      Example:        CLOSE ID=BUFFER


    CLEAR             Clear program buffer

      Command format: CLEAR ID=Id

      Return values:  None

      Return codes:   RC_OK (0) if success
                      RC_ERROR (10) if the program buffer was not found

      The argument Id is the address returned by the OPEN command.

      Example:        CLEAR ID=BUFFER


    LOAD              Load program file into program buffer

      Command format: LOAD ID=Id FILENAME=Name

      Return values:  None


                                         226





      Return codes:   RC_OK (0) if success
                      RC_ERROR (10) if the buffer or file was not found

      A program stored as a code  file if  loaded. The  argument Id  is the
      address returned by the OPEN command.

      Example:        LOAD ID=BUFFER FILENAME=MyFile


    SAVE              Save program buffer as a code program file.

      Command format: SAVE ID=Id FILENAME=Name

      Return values:  None

      Return codes:   RC_OK (0) if success
                      RC_ERROR (10) if the no buffer or write error

      The content of the program buffer is stored as a code file. The argu-
      ment Id is the address returned by the OPEN command.

      Example:        SAVE ID=BUFFER FILENAME=MyFile


    MAKECODE          Generate intermediate code

      Command format: MAKECODE ID=Id ProgramLine

      Return values:  Address of the generated code.

      Return codes:   RC_OK (0) if success
                      RC_ERROR (10) else

      Intermediate code for the ASCII program line specified as argument is
      generated. The address of the generated code is returned.

      Example:        MAKECODE ID=BUFFER 'IF Alfa=7 THEN'


    MAKEDIREC         Generate intermediate code for direct command

      Command format: MAKEDIREC ID=Id ProgramLine

      Return values:  Address of the generated code.

      Return codes:   RC_OK (0) if success
                      RC_ERROR (10) else

      Intermediate code for the command specified as argument is generated.
      The address of the generated code is returned.

                                         227






      Example:        MAKEDIREC ID=BUFFER 'PRINT sin(1)'


    MAKEASCII         Change intermediate code to ASCII

      Command format: MAKECODE ID=Id

      Return values:  Address of the generated ASCII line.

      Return codes:   RC_OK (0) if success
                      RC_ERROR (10) else

      An ASCII text line for the current buffer line is generated. The address
      of the generated line is returned.

      Example:        MAKEASCII ID=BUFFER


    INSERT            Insert code in the program buffer

      Command format: INSERT ID=Id

      Return values:  None.

      Return codes:   RC_OK (0) if success
                      RC_ERROR (10) else

      The  code  generated  by  the  last MAKECODE or MAKEDIREC is inserted
      at the current position of the buffer pointer.

      Example:        INSERT ID=BUFFER


    DELETE            Delete the current buffer line

      Command format: DELETE ID=Id

      Return values:  None

      Return codes:   RC_OK (0) if success
                      RC_ERROR (10) else

      Example:        DELETE ID=BUFFER


    REPLACE           Replace code in the program buffer

      Command format: REPLACE ID=Id


                                         228





      Return values:  None.

      Return codes:   RC_OK (0) if success
                      RC_ERROR (10) else

      The current line in the program buffer is replaced by the code genera-
      ted by the last MAKECODE or MAKEDIREC.

      Example:        REPLACE ID=BUFFER


    POSITION          Move buffer pointer to specified position

      Command format: POSITION ID=Id <place>  where place is:
                        SOF start of file
                        EOF end of file

      Return values:  None

      Return codes:   RC_OK (0) if success
                      RC_ERROR (10) else

      Example.        POSITION ID=BUFFER SOF


    LINE              Move buffer pointer up/down specified number of lines

      Command format: LINE ID=Id <number>

      Return values:  None.

      Return codes:   RC_OK (0) if success
                      RC_WARN (5) if border of text
                      RC_ERROR (10) else

      Example:        LINE ID=BUFFER -5 /* Move 5 lines down  */




  3 The interpreter AREXX interface.

  For each  interpreter opened  a separate  process is started with its own
  AREXX port. The name of the port is  Comal.Interpreter.<id>. For  the in-
  terpreters opened  by the  editor the  <Id> if 001, 002 etc. For instance
  Comal.Interpreter.001.

  Only two AREXX commands are accepted by the interpreter:



                                         229





    KILL              Remove the Interpreter master

      Command format: KILL

      Return values:  None

      Return codes:   RC_OK (0) if success
                      RC_WARN (5) if the command failed

      Example:        KILL


    EXECUTE           Load and execute program file.

      Command format:
          EXECUTE FILENAME=Name [WORKSPACE=Size] [STACK=Size]

      Return values:  None

      Return codes:   RC_OK (0) if success
                      RC_WARN (10) else

      A program  stored on  disk in  code form  is loaded  and executed. If
      WORKSPACE is  not specified  the default size $8000 is used. If STACK
      is not specified the default stack size $4000 is used.

      Example:        EXECUTE FILENAME=Comal:Programs/GraphDemo



  4 An AREXX script example.

  The following script will cut off a marked block and save it on disk as a
  code file. This is done by opening a separate program buffer and move the
  block into this buffer line by line.


    /* Save block as code file  */

    OPTIONS RESULTS
    'BLOCKSIZE'
    IF RC > 0 
      THEN DO
        OPTIONS
        WRITESTATUS 'No block marked'
      END
      ELSE DO
        BlockSize = RESULT
        IF ARG() = 0
        THEN DO

                                         230





          'GETFILE'
          File = RESULT
        END
        ELSE DO
          File = ARG(1)
        END
        IF RC = 0
        THEN DO
          'BLOCK'
          IF BlockSize < 0
          THEN DO
            OPTIONS
            'LINE' BlockSize
            BlockLen = -BlockSize+1;
            BlockSize =0
          END
          ELSE DO
            BlockLen = BlockSize+1;
            BlockSize = -BlockSize
          END
          address 'Comal.CodeMan'
          OPTIONS RESULTS
          'OPEN'
          ID = RESULT
          DO BlockLen
            address
            OPTIONS RESULTS
            'GETTEXT'
            Line = RESULT
            'CURSOR DOWN'
            address
            OPTIONS
            MAKECODE 'ID ' ID Line
            INSERT 'ID ' ID
            'LINE ID ' ID 1
          END
          'SAVE ID ' ID FILENAME File
          'CLOSE' 'ID ' ID
          address
          'LINE' BlockSize
        END
      END









                                         231





  VIII. COMAL IO DEVICES


  1 What is a Comal device?

  A Comal IO device is a sort of a file with a predefined name. The name of
  a Comal device always ends with a colon (:). At the start of the Comal sy-
  stem three Comal devices are defined:

    "ds:"   the standard IO window
    "kb:"   the keyboard attached the the IO window
    "lp:"   the printer


  These device names can  be used  in a  OPEN FILE  satements and  a SELECT
  statements.

  Example:

    OPEN FILE 1,"ds:",WRITE
    SELECT OUTPUT "lp:"


  The devices  are primariy  intented for  use in SELECT statements. At the
  start of a program execution implicite

    SELECT OUTPUT "ds:"

  and

    SELECT INPUT "kb:"

  are executed.


  2 Making new devices.

  It is possible to add new devices or to replace one of the predefined devi-
  ces. A  new device  is added by calling the internal Comal procedure Add-
  ComalDevice (found in the module SystemCode).

  All devices are linked together in a list and AddComalDevice links the new
  device into  the start of this list. Thus adding a device having the same
  name as an excisting device will in fact replace that device.

  A device is removed from the list by calling RemComalDevice.

  The procedure AddComalDevice is called with an initialized device structure
  that completely describes the device:


                                         232





    STRUC IoDevice
      DIM NextDevice OF POINTER TO IoDevice
      DIM Name OF ULONG
      DIM Type OF USHORT
      DIM Reserved OF SHORT
      DIM Open OF ULONG
      DIM Close OF ULONG
      DIM Read OF ULONG
      DIM Write OF ULONG
      DIM ReadLn OF ULONG
      DIM WriteLn OF ULONG
      DIM Scan OF ULONG
      DIM GetStrmPtr OF ULONG
      DIM SetStrmPtr OF ULONG
      DIM StreamErr OF ULONG
    ENDSTRUC IoDevice


  Field desription:

    NextDevice

      A pointer to the next device in the list of devices. Set by the Comal
      system.


    Name

      A pointer to the name of the device. Must end with a colon (:).


    Type

      The possible device types are:

        SEQ_DEVICE (0)  sequential device (for instance a printer)
        CRT_DEVICE (1)  used by windows
        KBD_DEVICE (2)  keyboard
        RBF_DEVICE (3)  random access device


    Open          open device

      An address of an open procedure of the form:

        FUNC Open(Name OF ULONG,Mode OF USHORT,REF Eof OF SHORT)

      where

        Name is a null terminated string  of characters.  The start  of the

                                         233





          string is  the device  name. The  characters after  the colon are
          transfered from the name in  the  OPEN  or  SELECT  statement and
          can be used to specify special parameters for the device (for in-
          stance: SELECT OUTPUT "sp: baud=1200").


        Mode is one or more of the following BITORed together:

          ACCESS_READ (1)   read only device (such as a keyboard)
          ACCESS_WRITE (2)  write only device (such as a printer)
          ACCESS_NEW (4)    delete excisting (if window for instance)

      At return the function sets the parameter Eof (end of file) to TRUE or
      FALSE  and  returns  an  ULONG  which is used to indentify the device
      (zero means error).


    Close         close the device

      An address of a procedure of the form:

        PROC Close(Id OF ULONG)


      The procedure is called when a  CLOSE statement  is executed  or when
      another device is selected in a SELECT statement. Id is the identifier
      returned by the open procedure.

      The procedure should not necessarily close the device. If for instance
      the device  is a  CRT_DEVICE like  "ds:" it is not smart to close the
      window each time another device is selected by the SELECT statement.


    Read          read a block of bytes

      An address of a function of the form:

        FUNC Read(Id,Data,REF MaxBytes,REF BreakMask) OF SHORT

      where

        Id OF ULONG is the device identifier returned by OPEN.
        Data OF ULONG is the address of a read buffer.
        MaxByte OF LONG is the number of bytes to read.
        BreakMask OF ULONG is a  mask  of  signals  that  should  break the
          reading. Not all devices supports this facility.

      Before return the routine must set the actual number of bytes read in
      MaxBytes and then return -1 (if error), 0 if OK or 1 if end of stream
      was reached.

                                         234







    Write         write a block of bytes

      An address of a function of the form

        FUNC Write(Id,Data,Length) OF SHORT

      where

        Id OF ULONG is the device identifier returned by OPEN.
        Data OF ULONG is the address of the data to write.
        Length OF LONG is the number of bytes to write.

      Before return the routine must set the actual number of bytes written
      in Length and then return TRUE (1) if OK or FALSE (0) if error.

      If this field is set for a KBD_DEVICE, guide texts in INPUT statements
      are sent to this function.


    ReadLn        read one line

      An address of a function of the form

        FUNC ReadLn(Id,Data,REF MaxLength) OF SHORT

      where

        Id OF ULONG is the device identifier returned by OPEN.
        Data OF ULONG is the address of a read buffer.
        MaxByte OF LONG is the maximal number of bytes to read.

      The routine reads characters until a line end character is read or Max-
      Bytes characters are read. The routine is responsible for editing during
      the input (if CRT_DEVICE).

      Before return the routine must set the actual number of bytes read in
      MaxBytes (The line end character not included) and then return -1 (if
      error), 0 if OK or 1 if end of stream was reached.


    WriteLn         write one line

      An address of a function of the form

        FUNC WriteLn(Id,Line,Length) OF SHORT

      where


                                         235





        Id OF ULONG is the device identifier returned by OPEN.
        Line OF ULONG is the address of the line to write.
        Length OF LONG is the length of the line to write.

      The routine must terminate by writing a line termination character to
      the device. Before return the routines must set the actual  number of
      bytes written  in Length  and then return TRUE (1) if OK or FALSE (0)
      if error.


    Scan          scan the device

      An address of a function of the form

        FUNC Scan(Id,Data,REF Length) OF SHORT

      where

        Id OF ULONG is the device identifier returned by OPEN.
        Data OF ULONG is the address of a read buffer.
        Length OF LONG is the length of the read buffer.

      The function should test if there are "characters" ready in the device.
      If this is the case ot should return the first "character". Otherwise it
      should return without reading  any characters.  A "character"  may be
      more than  one byte.  A keyboard  for instance  returns more than one
      byte if a function key is pressed.

      Before return the routine must set the actual number of bytes read in
      Length and  then return  -1 (if error), 0 if OK or 1 if end of stream
      was reached.


    Get           get position of the stream pointer

      Get is the address of a function. The format of this function depends
      on the device type.

        CRT_DEVICE:   FUNC Get(Id,REF Row, REF Col) OF SHORT

          where

            Id OF ULONG is the device identifier.
            Row OF SHORT is used to return row number of the cursor
            Col OF SHORT is used to return column number of the cursor

        RBF_DEVICE:   FUNC Get(Id,REF Offset) OF SHORT

          where


                                         236





            Id OF ULONG is the device identifier.
            Offset  OF  LONG  is  used  to return the current offset of the
              stream pointer.

      Other devices should set these fields to zero. For both functions the
      return value is TRUE if operation succeded and otherwise FALSE.


    Set           set position of the stream pointer

      Set is the address of a function. The format of this function depends
      on the device type.

        CRT_DEVICE:

            FUNC Set(Id,Row,Col) OF SHORT

          where

            Id OF ULONG is the device identifier.
            Row OF SHORT is the new row number of the cursor
            Col OF SHORT is the new column number of the cursor


        RBF_DEVICE:

            FUNC Get(Id,REF Offset) OF SHORT

          where

            Id OF ULONG is the device identifier.
            Offset OF LONG is the new offset of the stream pointer.

      Other devices should set these fields to zero. For both functions the
      return value is TRUE if operation succeded and otherwise FALSE.


    StreamErr

      For the time being not used. Set to zero.


  The fields  Name, Type,  Open, Close,  Get (CRT and RBF) and Set (CRT and
  RBF) must be set. Other fields may be set to zero if the device  does not
  support the function.


  The following module adds a serial device "sp:" to the list of Comal devices:



                                         237





  // Serial device "sp:"

  MODULE SerialDevice

    USE System
    USE SystemCode
    USE PortObjects
    USE IoObjects
    USE ExecLibrary

    STRUC IOTArray
      DIM TermArray0 OF ULONG
      DIM TermArray1 OF ULONG
    ENDSTRUC IOTArray

    STRUC IOExtSer  // Standard IO request to serial.device
      INHERIT IOStdReq             // Standard IO-request
      DIM io_CtrlChar OF ULONG     // xON, xOFF, INQ, ACK
      DIM io_RBufLen OF ULONG      // length of read buffer
      DIM io_ExtFlags OF ULONG     // Not used
      DIM io_Baud OF ULONG         // Baud rate
      DIM io_BrkTime OF ULONG      // duration of break signal in micros
      DIM io_TermArray OF IOTArray // Termination characters
      DIM io_ReadLen OF UBYTE      // Bits per read character
      DIM io_WriteLen OF UBYTE     // Bits per write character
      DIM io_StopBits OF UBYTE     // Number of stop bits
      DIM io_SerFlags OF UBYTE     // Flags set by serial device
      DIM io_Status OF USHORT
    ENDSTRUC IOExtSer

    STRUC IoDevice
      DIM NextDevice OF POINTER TO IoDevice
      DIM Name OF ULONG       // Pointer to device name 'sp:'
      DIM Type OF USHORT      // Type of device
      DIM Reserved OF SHORT
      DIM Open OF ULONG
      DIM Close OF ULONG
      DIM Read OF ULONG
      DIM Write OF ULONG
      DIM ReadLn OF ULONG
      DIM WriteLn OF ULONG
      DIM Scan OF ULONG
      DIM GetStrmPtr OF ULONG
      DIM SetStrmPtr OF ULONG
      DIM StreamErr OF ULONG
    ENDSTRUC IoDevice

    DIM CMD_READ OF SHORT
    DIM CMD_WRITE OF SHORT
    DIM CMD_NONSTD OF SHORT

                                         238






    CMD_READ:=2
    CMD_WRITE:=3
    CMD_NONSTD:=9

    DIM SerReq OF POINTER TO IOExtSer
    DIM ReplyPort OF POINTER TO MsgPort
    DIM DevOpen OF BOOL
    DIM SerDevice OF IoDevice
    DIM SerDevName$ OF 3

    SerDevName$:="sp:"
    SerDevice.Name:=ADR(SerDevName$)
    SerDevice.Type:=0         // Sequential device
    SerDevice.Open:=ADR(SerOpen(,,))
    SerDevice.Close:=ADR(SerClose())
    SerDevice.Read:=ADR(SerRead(,,,))
    SerDevice.Write:=ADR(SerWrite(,,))
    SerDevice.ReadLn:=ADR(SerReadLn(,,))
    SerDevice.WriteLn:=ADR(SerWriteLn(,,))
    AddComalDevice(ADR(SerDevice))

    FUNC SerOpen(Name OF ULONG,Mode OF USHORT,REF Eof OF SHORT)
                                                          OF ULONG
      ALLOCATE(SerReq,MEMF_PUBLIC)
      ALLOCATE(ReplyPort,MEMF_PUBLIC)
      SerReq@.mn_ReplyPort:=ReplyPort

      // Open serial.device'
      IF OpenDevice("serial.device",0,SerReq,0) THEN
        DEALLOCATE(SerReq)
        RETURN 0
      ENDIF
      DevOpen:=TRUE

      // Set TermArray
      SerReq@.io_Command:=CMD_NONSTD+2
      SerReq@.io_TermArray.TermArray0:=$1C0A0A0A
      SerReq@.io_TermArray.TermArray1:=$0A0A0A0A
      SerReq@.Do  // Set TermArray

      Eof:=FALSE
      RETURN $FFFFFFFF
    ENDFUNC SerOpen

    PROC SerClose(Id OF ULONG)
      IF DevOpen THEN
        SerReq@.Abort
        dummy:=SerReq@.mn_ReplyPort@.Get
        CloseDevice(SerReq)

                                         239





        DEALLOCATE(SerReq)
        DEALLOCATE(ReplyPort)
        DevOpen:=FALSE
      ENDIF
    ENDPROC SerClose

    FUNC SerRead(Id OF ULONG,Buffer OF ULONG,REF MaxLen OF, 
                 LONG,BreakMask OF ULONG) OF SHORT
      SerReq@.io_SerFlags:=SerReq@.io_SerFlags BITAND %10111111
      SerReq@.io_Command:=CMD_READ
      SerReq@.io_Length:=MaxLen
      SerReq@.io_Data:=Buffer
      SerReq@.Send               // Read data
      SerReq@.Wait
      MaxLen:=SerReq@.io_Actual  // Return number of bytes read
    ENDFUNC SerRead

    FUNC SerWrite(Id OF ULONG,Data OF ULONG,REF Length OF LONG)
                                                           OF BOOL
      SerReq@.io_Command:=CMD_WRITE
      SerReq@.io_Length:=Length
      SerReq@.io_Data:=Data
      SerReq@.Send               // Write data
      SerReq@.Wait
      IF SerReq@.io_Error THEN
        RETURN FALSE
      ELSE
        RETURN TRUE
      ENDIF
    ENDFUNC SerWrite

    FUNC SerReadLn(Id OF ULONG,Buffer OF ULONG,REF MaxLen OF LONG)
                                                             OF SHORT
      LOCAL Ptr OF POINTER TO UBYTE

      SerReq@.io_SerFlags:=SerReq@.io_SerFlags BITOR %01000000
      SerReq@.io_Command:=CMD_READ
      SerReq@.io_Length:=MaxLen
      SerReq@.io_Data:=Buffer
      SerReq@.Send                // Read data
      SerReq@.Wait
      IF SerReq@.io_Error THEN
        RETURN -1
      ENDIF
      Ptr:=ADR(Buffer)+SerReq@.io_Actual-1
      CASE Ptr@ OF
      WHEN $1C
        MaxLen:=SerReq@.io_Actual-1
        RETURN 1        // End of file
      WHEN $0A

                                         240





        MaxLen:=SerReq@.io_Actual-1
        RETURN 0
      OTHERWISE
        MaxLen:=SerReq@.io_Actual
        RETURN 0
      ENDCASE
    ENDFUNC SerReadLn

    FUNC SerWriteLn(Id OF ULONG,Data OF ULONG,REF Length OF LONG)
                                                           OF BOOL
      LOCAL ch OF UBYTE

      SerReq@.io_Command:=CMD_WRITE
      SerReq@.io_Length:=Length
      SerReq@.io_Data:=Data
      SerReq@.Send               // Write data
      SerReq@.Wait
      IF SerReq@.io_Error THEN
        RETURN FALSE
      ENDIF
      ch:=$0A
      SerReq@.io_Command:=CMD_WRITE
      SerReq@.io_Length:=1
      SerReq@.io_Data:=ADR(ch)
      SerReq@.Do     // Write LF
      IF SerReq@.io_Error THEN
        RETURN FALSE
      ELSE
        RETURN TRUE
      ENDIF
    ENDFUNC SerWriteLn

    PROC SerSignal(s OF LONG) SIGNAL
      CASE s OF
      WHEN SIG_CLOSE,SIG_DISCARD
        RemComalDevice(ADR(SerDevice))
      OTHERWISE
        // No action
      ENDCASE
    ENDPROC SerSignal

  ENDMODULE SerialDevice


  3 Making your own IO window.

  It is possible to replace the standard IO window opened by the interpreter
  by your own window. To see how this can be done it  is necessary  to know
  what is happening before a program is started.


                                         241





  When the  interpreter receives an execute command the IO is redirected to
  the IO port (see section V.2) and the standard console  device closes the
  execute window (if it is open). The console device closes the window when
  the signal SIG_CLEAR is send.

  Then the program is scanned and during this scanning process  all modules
  are loaded and are initialized by executing the initialization part of the
  module. A module can use this initialization to add a new "ds:" device and a
  new "kb:" device.

  After  the  scanning  an  implicite  SELECT OUTPUT "ds:" and an implicite
  SELECT INPUT "kb:" is executed. If a module has added a new  "ds:" device
  and a new "kb:" device these devices are put into the start of the device
  list and it will be these devices that are used by the SELECT statements.

  As a consequence your IO window will be opened and  the standard  IO win-
  dow will not be used.

  In the directory ModuleDev you will find an example of such a module pro-
  grammed in C (IoWindow.c and IoWindow.interface).































                                         242





  IX. MASHINE CODED MODULES

  Comal has been designed to be a modular language. It is possible to devide
  a project into several modules. These modules can be written in as different
  languages as assembler, C as well as the Comal language itself.

  Seen from the users of the modules there are no difference in the modules
  wether they are written in assembler, C or Comal. The routines in the mo-
  dules are called in exactly the same way. Very often a module is first writ-
  ten in Comal and then (if necessary) it is rewritten in C or even assembler.

  In chapter  III.9 development  of modules in Comal was described. In this
  chapter machine coded modules (written in C or assembler) will be descri-
  bed.


  1 The format of a mashine coded module.

  A mashine coded module consists of three parts:

    - the interface part
    - the initialization and signal routines
    - the routine part (procedures and functions in the module)


  In the following sections 1.1-1.3 the three parts are described. C program-
  mers do not need to read these sections.


  1.1 The interface part.

  In the interface part of a module all necessary informations about the ex-
  ported routines  are stored (the name and the address of the routine, the
  type of the routine and its parameters etc.).

  The interface part is a table of elements of the  form (described  in not
  100% correct C):

    struct NameTable NameTable[NumNames];

  where the NameTable structure is defined as

    struct NameTable
    {
      void *RoutineAddress;   /* The address of the routine           */
      struct ProcType *Type;  /* The address of the type descriptor   */
      char *RoutineName;      /* A pointer to the name of the routine */
    }



                                         243





  The information of the type of the routine is stored in a structure Proc-
  Type. Routines with the same type can use the same structure.

  The structure ProcType has the form:

    struct Param
    {
      UBYTE Flags;          /* Set to 0x80 if REF parameter       */
      BYTE  SecondaryType;  /* Set to zero                        */
      WORD  PrimaryType;    /* Parameter type (se include files)  */
    };

    struct ProcType
    {
      WORD TypeId;          /* PROC_ID or FUNC_ID                 */
      WORD ReturnType;      /* Return type (FUNC) or zero (PROC)  */
      WORD *TypeDescriptor; /* Set to zero                        */
      UWORD Flags;
      UBYTE StackUse1;      /* Stack use of type descriptors      */
      UBYTE StackUse0;      /* Primary parameter stack use        */
      UWORD NumPar;         /* Number of formal parameters        */
      struct Param Param[NumPar];
    };

  Set Flags to 0x0001 if it is a string function returning a C string (null ter-
  minated ASCII string).

  The stack  use are the number of bytes used to transfer the parameters on
  the stack. The secondary stack is used to transfer the address of  a type
  descriptor for  structured types  (ARRAY, FUNC  and PROC) and REF parame-
  ters. 


  1.2 The initialization and signal routines.

  The initialization routine is the very first mashine instructions in the mo-
  dule. It is a function with the C format:

    short Init(struct ComalStruc *ComalStruc,struct Module *Module)


  This means  that the addresses of the ComalStruc and the module structure
  for the module in question are pushed onto the stack (as two  long words)
  before a call is made to the first instruction of the module.

  The initialization  routine should as a minimum fill the module structure
  with the address of the name table and  the number  of names  in the name
  table and then return the status code (in D0.W). Zero is the OK code.



                                         244





  Very often an initialization routine also stores the address of the Comal-
  Struc for later use.

  A signal routine is a procedure with the C format:

    void signal(short SignalNum)


  To tell the comal system that there is a signal routine, the address of the
  routine must be placed in the module structure by the initialization routine.
  Otherwise there are no difference between a signal routine in a Comal mo-
  dule and a mashine coded module. The same signal numbers are send.

  All routines, including the initialization routine and the signal routine, may
  use registers D0-D1/A0/A1. The content of all other registers must be re-
  stored before a return.


  1.3 The routine part.

  Procedures and  functions that  are to be exported from the module (those
  referenced in the name table) must have the form used by most C compilers
  (for instance SAS/C ver. 5.xx).

  When a procedure or a function is called from Comal, the actual parameters
  are pushed onto the stack.

  Float value parameters are pushed as two long words. Value parameters for
  the integer types are pushed as one long word (byte and short integers are
  sign extended before they are pushed).

  Text values are transfered as an address to the text value that is null ter-
  minated.

  Parameters  of  the  type  STRUC,  ARRAY,  PROC  and  FUNC as well as REF
  parameters of any kind are pushed as addresses.

  Values returned by functions are passed back in register D0 (D0 and D1 if
  float).

  All routines  may use registers D0-D1/A0/A1. The content of all other re-
  gisters must be restored before a return.


  2 An assembler programmed module.

  Let us as an example show  how a  module is  programmed in  assembler. We
  are going  to make a module containing the following procedures and func-
  tions:


                                         245





    FUNC Even(i OF LONG) OF SHORT
    FUNC Hex$(i OF ULONG)
    PROC Capital(REF t$)


  The interface part contains a name table of three elements


  NameTable:     DC.L    CapitalName      * Name of procedure
                 DC.L    CapitalType      * Type descriptor
                 DC.L    Capital          * Routine address

                 DC.L    HexName
                 DC.L    HexType
                 DC.L    Hex

                 DC.L    EvenName
                 DC.L    EvenType
                 DC.L    Even

  CapitalName:   DC.B    'Capital',0
  HexName:       DC.B    'Hex$',0
  EvenName:      DC.B    'Even',0


  and type descriptors for each function/procedure:


  CapitalType:   DC.W    ProcTypeId       * Procedure
                 DC.W    0                * No return value
                 DC.L    0                * Always set to zero
                 DC.W    $0000            * No flags
                 DC.B    4                * Sec. stack (REF)
                 DC.B    4                * Address = 4 bytes
                 DC.W    1                * One parameter
                 DC.B    $80              * REF parameter
                 DC.B    0                * Always zero
                 DC.W    StringTypeId     * Parameter type

  HexType:       DC.W    FuncTypeId       * Function
                 DC.W    StringTypeId     * Return value
                 DC.L    0
                 DC.W    $0001            * C string is returned
                 DC.B    0                * No sec. stack use
                 DC.B    4                * Int paramter = 4 bytes
                 DC.W    1                * One parameter
                 DC.B    $00              * Value parameter
                 DC.B    0
                 DC.W    UlongTypeId      * Parameter type


                                         246





  EvenType:      DC.W    FuncTypeId       * Function
                 DC.W    ShortTypeId      * Return type
                 DC.L    0
                 DC.W    $0000            * No flags
                 DC.B    0                * No sec. stack use
                 DC.B    4                * Int parameter = 4 bytes
                 DC.W    1                * One parameter
                 DC.B    $00              * Value parameter
                 DC.B    0
                 DC.W    LongTypeId       * Parameter type


  The initialization routine is very simple:

  Init:    MOVE.L  4(A7),ComalStruc         * Store ComalStruc addr
           MOVE.L  8(A7),A0
           MOVE.L  #NameTable,MS_Names(A0)  * Name table address
           MOVE.W  #3,MS_NumName(A0)        * Number of names
           MOVEQ   #0,D0                    * Initialization OK
           RTS


  The complete source for the module looks like:


  ; Module Demo

           include 'Comal.i'

           SECTION DemoCode,CODE
           CNOP    0,2

  Start:   MOVE.L  4(A7),ComalStruc         * Store ComalStruc addr
           MOVE.L  8(A7),A0
           MOVE.L  #NameTable,MS_Names(A0)  * Name table address
           MOVE.W  #3,MS_NumName(A0)        * Number of names
           MOVEQ   #0,D0                    * Initialization OK
           RTS

  Capital: MOVE.L  4(A7),A1                 * Get string address
  CapLoop: MOVE.B  (A1)+,D0                 * Get next character
           BEQ.S   CapEnd                   * Return if zero
           CMP.B   #'a',D0                  * Small letter?
           BCS     CapLoop                  * No -> loop
           CMP.B   #'z',D0
           BHI     CapLoop
           BCLR    #5,-1(A1)                * Change to capital
           BRA     CapLoop
  CapEnd:  RTS


                                         247





  Hex:     MOVE.L  ComalStruc,A0            * Get address of ComalStruc
           MOVE.L  CS_CurrWorkBottom(A0),A1 * .. and work space start
           MOVE.L  A1,D0                    * Set as return value
           LEA     10(A1),A1                * Reserve 10 bytes
           MOVE.L  A1,CS_CurrWorkBottom(A0) * Set new bottom
           MOVE.L  D0,A1                    * Get start of string
           MOVE.B  #'$',(A1)+               * Start with hex specifier
           MOVE.B  #'0',(A1)+
           MOVE.L  4(A7),D1                 * Number to D1
           BEQ.S   HexEnd                   * Return if zero
           MOVEM.L D2/D3,-(A7)              * Store registers
           SUBQ.L  #1,A1                    * Point after $
           MOVEQ   #8,D3
           LEA     Digits(PC),A0         
  HxLoop1: ROL.L   #4,D1                    * Find first non zero digit
           SUBQ.W  #1,D3
           MOVE.W  D1,D2
           AND.W   #$000F,D2
           BEQ     HxLoop1
  HxLoop2: MOVE.B  0(A0,D2.W),(A1)+         * Nex hex digit to buffer
           ROL.L   #4,D1
           MOVE.W  D1,D2
           AND.W   #$000F,D2
           DBRA    D3,HxLoop2
           MOVEM.L (A7)+,D2/D3
  HexEnd:  CLR.B   (A1)                     * Terminate string
           RTS                              * .. and return

  Digits:  DC.B    '0123456789ABCDEF'

  Even:    MOVE.L  4(A7),D0
           ADDQ.L  #1,D0
           AND.L   #1,D0
           RTS


           SECTION DemoData,DATA
           CNOP    0,2

  CapitalType:   DC.W    ProcTypeId       * Procedure
                 DC.W    0                * No return value
                 DC.L    0                * Always set to zero
                 DC.W    $0000            * No flags
                 DC.B    4                * Sec. stack (REF)
                 DC.B    4                * Address = 4 bytes
                 DC.W    1                * One parameter
                 DC.B    $80              * REF parameter
                 DC.B    0                * Always zero
                 DC.W    StringTypeId     * Parameter type


                                         248





  HexType:       DC.W    FuncTypeId       * Function
                 DC.W    StringTypeId     * Return value
                 DC.L    0
                 DC.W    $0001            * C string is returned
                 DC.B    0                * No sec. stack use
                 DC.B    4                * Int paramter = 4 bytes
                 DC.W    1                * One parameter
                 DC.B    $00              * Value parameter
                 DC.B    0
                 DC.W    UlongTypeId      * Parameter type

  EvenType:      DC.W    FuncTypeId       * Function
                 DC.W    ShortTypeId      * Return type
                 DC.L    0
                 DC.W    $0000            * No flags
                 DC.B    0                * No sec. stack use
                 DC.B    4                * Int parameter = 4 bytes
                 DC.W    1                * One parameter
                 DC.B    $00              * Value parameter
                 DC.B    0
                 DC.W    LongTypeId       * Parameter type


  NameTable:     DC.L    CapitalName
                 DC.L    CapitalType
                 DC.L    Capital

                 DC.L    HexName
                 DC.L    HexType
                 DC.L    Hex

                 DC.L    EvenName
                 DC.L    EvenType
                 DC.L    Even

  CapitalName:   DC.B    'Capital',0
  HexName:       DC.B    'Hex$',0
  EvenName:      DC.B    'Even',0

                 SECTION DemoBlock,BSS
                 CNOP    0,2

  ComalStruc:    DS.L    1

                 END


  This source has to be compiled and linked.  Public domain  assemblers and
  linkers will do the work.


                                         249






  3 Programming modules in C.

  It is  a very simple task to write modules in C (provided you know how to
  program in C).

  The interface part is written as (almost) normal Comal FUNC or PROC lines
  in a  separate text  file and then compiled by the compiler CompInterface
  (found in the ModuleDev directory on the Comal.Extras disk).

  The initialization routine (optional), the signal routine (optional) and all
  other routines are written as normal C procedures or functions.

  As an  example let's  make a  module containing these three functions and
  procedures:

    FUNC Even(i OF LONG) OF SHORT
    FUNC Hex$(i OF ULONG)
    PROC Capital(REF t$)


  3.1 The interface part.

  First let's make the interface part. The source text for the interface com-
  piler CompInterface is written in an editor. The source looks like:

    // Interface for Module Demo

      FUNC Even(i OF LONG) OF SHORT
      FUNC Hex$(i OF ULONG)
      PROC Capital(REF s$)


  Note that comments and empty lines may be used as in Comal.

  This source file is stored on disk as Demo.interface and then it is compiled
  by executing the following  shell (CLI)  command (provided Demo.interface
  and CompInterface are placed in the current directory)

      CompInterface Demo.interface


  The output  from CompInterface  is an assembler source text with the name
  Demo.interface.a. This is compiled with a normal assembler (for instance the
  SAS/C assembler)  by executing the command (the include file Comal.i must
  be in the current directory):

      lc:asm Demo.interface.a



                                         250





  The output from the assembler is an object file Demo.interface.o ready to
  be linked.


  3.2 The procedures and functions.

  The routines  in a  C programmed  module are  made as normal C procedures
  and functions. The names and the  types are  those used  in the interface
  source. You only have to know that strings (marked with a dollar sign ($)
  in the interface source) must be changed to 'char *' and that  the dollar
  sign are omitted from the name (see the function Hex$ in the example).

  The C source for the module we are making as an example looks like this:

  /* Module Demo */

  #include <exec/types.h>
  #include <string.h>
  #include <ctype.h>
  #include "Comal.h"
  #include "Comal_protos.h"

  short Even(LONG i)
  {
    return( (i+1) & 0x01 );
  }

  char *Hex(ULONG i)
  {
    char *String;
    
    /* Place return string in workspace    */
    String = ComalStruc->CurrWorkBottom;
    if ( ComalStruc->CurrWorkBottom+14 >= ComalStruc->CurrWorkTop )
      ErrorText("Out of memory");
    ComalStruc->CurrWorkBottom += 14;
    String[0] = '$';
    (void)stcl_h(String+1,i);
    return( String );
  }

  void Capital(char *Str)
  {
    while ( *Str )
    {
      if ( islower(*Str) )
        *Str = *Str - ('a'-'A');
      Str++;
    }
  }

                                         251







  This program  is stored  on disk  under the name Demo.c and then compiled
  by executing the command (SAS/C compiler)

    lc:LC -b0 -fi -oDemo.o Demo


  The output from the compiler is an object file ready to be linked.


  3.3 Linking the object files.

  The object files made by the assembler and the C compiler has to be linked
  together. This is done by the following command (again the SAS/C linker is
  used):

    lc:blink Demo.interface.o Demo.o TO Comal:Modules/Demo
                    LIBRARY Comal.lib lib:lcnb.lib lib:Amiga.lib

  (type in as one line).

  The output from the linker is a module Demo placed in the  directory Com-
  al:Mudules and the module can be used by placing the line

    USE Demo

  in a Comal program.


  3.4 An initialization routine.

  The interface compiler CompInterface makes the basic initialization code for
  you. If you need to do more initialization you have to place the following
  function in your code (NOTE! The name must be ModuleInit):

    short ModuleInit(void)
    {

        :

    }


  The function returns zero if the initialization succeded. Otherwise an error
  code greater than 5 is returned.


  Example: Here is a simple initialization routine:


                                         252





    short ModuleInit(void)
    {
      if ( !(DOSBase = OpenLibrary("dos.library",0)) )
        return(100);
      else
        return(0);
    }


  3.5 A signal routine.

  If you need to receive signals from the  Comal system  a signal procedure
  has to placed in your code (NOTE! The name must be signal):

    void signal(short s)
    {
      :
    }

  Example: A signal routine is very often used to close libbraries.

    void signal(short s)
    {
      switch(s)
      {
        case SIG_CLOSE:
        case SIG_DISCARD: 
          if( DOSBase )
          {
            CloseLibrary(DOSBase);
            DOSBase = NULL;
          }
        case SIG_CLEAR:
        case SIG_RUN:
        case SIG_STOP:
        case SIG_END:
          break;
      }
    }


  3.4 Using the script file MakeMod.

  All the command used to make a module is placed in a script file MakeMod
  (found in the ModuleDev directory on the Comal.Extras disk). Provided that
  both source files (Demo.interface and Demo.c) are placed in the same direc-
  tory as  MakeMod and  Comal.h, Comal_protos.h, Comal.i, and Comal.lib the
  module can be made simply by executing the command:

    MakeMod Demo

                                         253







  3.5 The interface compiler CompInterface.

  The interface compiler CompInterface takes as input a text file containing
  normal Comal procedure and function heads. The command template is:

    CompInterface [options] filename


  Options are specified as a hyphen followed by a single letter. Current op-
  tions are:

    -o  This option is followed by a  directory path.  The output  from the
        compiler is placed in that directory. Default output path is the cur-
        rent directory.

    -i  If this option is used, the interface source is added as the modules
        informationtext (the  text you  get when selecting the Show Modules
        menu item in the Program menu).

    -p  If this option is used, all functions (except string functions) can be
        used as procedures, too.


  The output  from CompInterface is an assembler source text that has to be
  compiled by a normal assembler.


  4 Calling internal Comal routines from modules.

  A number of routines in the  Comal system  may be  called from  a module.
  These routines are:


    void ErrorNumber(short ErrorCode);
            -6                D2

      Stop execution with error message. No return from the routine.


    void ErrorText(char *ErrorText);
            -12            D0

      Stop execution with error text. No return from the routine.






                                         254





    void ExecBreak(void);
            -18

      Call this routine if the break flag is set in the Comal structure (see
      the include files). No return from the routine unless the execution are
      continued or the break is disabled (SET ESC statement).


    struct Window *LockComalWindow(void);
            -24

      Get a  shared lock  for the standard IO window. The routine will open
      the standard IO window (if not already opened) and return a pointer to
      the window structure.


    void UnlockComalWindow(void);
            -30

      Release the lock for the standard IO window.


    void AddComalDevice(struct IoDevice *Device);
            -36                       A0

      Add an IO device. The parameter is a pointer to an initialized IO devi-
      ce structure. This structure will be put into the start of the list of
      standard IO devices (like "ds:").

    void RemComalDevice(struct IoDevice *Device);
            -42                       A0

      Remove an  IO device  from the list of IO devices. The parameter is a
      pointer to the IO device to be removed.


    unsigned long ComalWait(unsigned long SignalMask);
            -48                             D0

      Wait for the signals in the signalmask and all the Comal signals. The
      routine ORs  all the  Comal signals  to SignalMask and then calls the
      Exec routine Wait.


    void AddExcept(struct ExceptStruc *Except);
            -54                         A0

      Add an exception routine to the list of exception routines in the Comal
      system. The parameter is an initialized structure of the form:


                                         255





        struct ExceptStruc
        {
          struct ExceptStruc *Next;
          ULONG SignalMask;
          void  (*ExceptRoutine)(ULONG,struct ExceptStruc *);
          APTR  IdField;
        };

      The field  SignalMask is a mask containing the exception signals. Ex-
      ceptRoutine is the address of your exception routine. This routine is
      called with a mask containing the signals that caused the exception and
      a pointer to the exception structure. The field IdField is for your own
      purpose.


    void RemExcept(struct ExceptStruc *Except);
            -60                         A0

      Remove an exception routine from the list of exception routines in the
      Comal system. The parameter is a pointer to the exception structure to
      be removed from the exception list.


    void AddSignal(unsigned long SignalMask);
            -66                     D0

      Add a signal to the Comal signals.


    void RemSignal(unsigned long SignalMask);
            -72                     D0

      Remove a signal from the Comal signals.


    short GetAccept(char *Str, char *Accept, char *Cancel);
            -78           A0          A1            A2

      Set up  a requester. The parameter Str is the text written in the re-
      quester. This string may contain the C formatting character '\r'. The
      parameters Accept  and Cancel are the text filled in the positive and
      negative gadgets respectively.

      The routine returns TRUE if the positive gadget was pressed. Otherwise
      it returns FALSE.


  The routines are organized as an Amiga library with jump vectors at nega-
  tive offsets (the numbers shown under the names) from the Comal structure.
  From an  assembler program  it must  be called  with the  address of this

                                         256





  structure in register A6. Here is a typical calling sequence:

          MOVE.L  ComalStruc,A6
          JSR     -LVORemSignal(A6)

  All the routines uses the registers D0-D1/A0-A1. All other registers are un-
  changed.


  5 Calling comal programmed routines from a module.

  A Comal programmed procedure or function is called as a C procedure, i.e.
  the parameters are transfered on the stack and a return value is returned
  in register D0 (D0 and D1 if float).

  Example: We  will make  a module  containing an  integral function of the
    form:

      FUNC integral(f OF FltFnc, a, b)

      TYPE FltFnc=FUNC(FLOAT) OF FLOAT


    The interface looks like:


    // Interface for integral module

      FUNC integral(f OF FUNC OF FLOAT,a OF FLOAT,b OF FLOAT) OF FLOAT


    and the code like this:


    /* Integral module    version 92.07.29  */

    #include <math.h>
    #include "Comal.h"
    #include "Comal_protos.h"

    double integral(double (*f)(),double a, double b, struct ProcType *Inf)
    {
      double xi, s, dx;
      int n;
    
      if ( (Inf->NumPar != 1) ||
            (*((ULONG *)(Inf->Param)) != (0x0000FFFF & FLOAT_ID)) )
        ErrorText("Illegal function parameter");

      n = 16;

                                         257





      dx = (b-a)/n;
      xi = a;
      s = (f(a)-f(b))*dx/6.0;
      while ( (n--) > 0 )
      {
        xi = xi+dx;
        s = s+(f(xi)+2*f(xi-dx/2))*dx/3;
      }
      return(s);
    }


  One thing has to be noted. At the call to a Comal  programmed routine the
  register A4  must point to the data used by the Comal system. At the call
  of your own routine the register A4 has the correct  value and  if you do
  not touch this register, there is no problem.

  But what  about C compiled code? How can you be sure that the register A4
  is not used by the code made by your compiler?

  As a general answer to that question we have to  say, that  you cannot be
  sure! Fortunately it seems as if SAS/C (version 5.10) do not use the regis-
  ter if it is compiled in "non base relative" mode (use the nb-libraries) and
  if you do not use the math library (the one containing the functions sin,
  cos, tan, ...). Replacement for most of the functions are placed in Comal.lib.


  6 Making a library interface.

  From time to time new libraries for the Amiga appears (a lot  of them are
  public domain).  It is a very simple task to make an interface for such a
  library.

  As an example we will make an interface  for the  ReqTools.Library (found
  on Fish disk #575).

  First we  have to make an interface source file. This is best done if you
  have a C proto include file for the functions in the library. Such a file is
  supplied with the ReqTools.Library. A small part of this file looks like:

    APTR rtAllocRequestA(ULONG, struct TagItem *);
    void rtFreeRequest(APTR);
    void rtFreeReqBuffer(APTR);
    LONG rtChangeReqAttrA(APTR, struct TagItem *);
            :


  The proto file is changed to an interface file in this way:



                                         258





  // Interface for reqtools.library

    FUNC rtAllocRequestA type OF ULONG, TagItem OF ULONG) OF ULONG
    PROC rtFreeRequest(req OF ULONG)
    PROC rtFreeReqBuffer(req OF ULONG)
    FUNC rtChangeReqAttrA(req OF ULONG, TagItem OF ULONG) OF LONG
            :


  Note that pointers are changed to ULONG.

  Now we  are going  to make the code. This contains only an initialization
  and a signal routine:


  /*********************************************************************/
  /*                                                                   */
  /*    ReqTools library - version 92.05.11                            */
  /*                                                                   */
  /*********************************************************************/

  #include <exec/types.h>
  #include <proto/exec.h>
  #include <pragmas/exec.h>
  #include "Comal.h"
  #include "Comal_protos.h"

  struct Library *ReqToolsBase = NULL;

  short ModuleInit(void)
  {
    if ( !(ReqToolsBase = OpenLibrary("reqtools.library",37)) )
      return(0);
    else
      return(150);
  }

  void signal(short s)             /* Signal routine */
  {
    switch(s)
    {
      case SIG_CLOSE:
      case SIG_DISCARD: 
        if( ReqToolsBase )
          CloseLibrary(ReqToolsBase);
    }
  }




                                         259





  The files are compiled as usual. To link it you have to specify the ReqTools
  linker library:

    blink ReqToolsLibrary.Interface.o ReqToolsLibrary.o
      TO Comal:Modules/ReqToolsLibrary
      LIBRARY Comal.lib reqtoolsnb.lib Lib:lcnb.lib Lib:Amiga.lib

  (NOTE! one line only).











































                                         260





  X. APPENDIX


  A. Customizing the Comal text.

  All texts used in the Comal system are stored in two files Comal.texts and
  Comal.interpreter.texts found in the Prefs/Texts drawer.  These files are
  simple ASCII files and it is a simple matter to edit these files.

  The format  of the  files is  shown in the following fraction of the file
  Comal.texts:

  /* Other window title texts */
  #052  "Comal - project"
  #053  "Command - project"
  #054  "Error - project"
  #055  "Comal module library"
  #056  "Comal module information text"
  #057  "Comal watch window"

  /* About window texts */
  #058  "About Comal ..."
  #059  "Comal version 3.01"
  #060  "Total memory available:"
  #061  " bytes of memory for program text"
  #062  " bytes of workspace memory for running program"
  #063  "Comal was designed and developed by:"
  #064  "Svend Daugaard Pedersen"


  /* Menu texts  */
  #065  "Project"                         /* Menu name    */
  #066  "New"                             /* Item name    */
  #067  "N"                               /* Short cut    */
  #068  "Open..."                         /* Item name    */
  #069  "O"                               /* Short cut    */
  #070  "Save"                            /* etc.         */
  #071  "S"
  #072  "Save As..."
  #073  "A"
  #074  "Print"
  #075  "P"
  #076  "Open Command Window"
  #077  "K"
  #078  "About..."
  #079  ""
  #080  "Clear Program Buffer"
  #081  ""
  #082  "Quit Project"
  #083  "Q"

                                         261






  Each text is placed in one line and this line starts with the number of the
  text. The text itself is placed in double quotes and consists of ASCII cha-
  racters or the C style codes '\n' (new line), '\"' (double quote) or '\\' (for
  the character '\').

  All texts  must be  defined and  the numbers must be placed in increasing
  order. The first line has number zero. Empty lines and the use of C style
  comments are allowed.

  Note that  each menu item and subitem text consist of two texts - one for
  the text itself and one for the menu short cut. If the short cut text is the
  empty text (the text ""), no short cut will be available.

  Having made  a new set of text files they must be stored in the directory
  Prefs/Texts using file names of the forms:

    Comal.<text_id>

  and

    Comal.Interpreter.<text_id>


  Normally the <text_id> is the name of a language but it could be anything
  (but the same for both files!).


  B. The format of the AREXX macro file.

  At start Comal searches for a file Comal.macro containing macro definitions.
  The format of a macro file is shown in the following example:

  #0 SaveBlock
  #1 SaveModule
  #2 SaveModule3
  #3 StartProgram
  #4 'StartProgram Comal:Programs/Integral'


  Each macro line starts with a number sign (#), one digit (0,1,2,..,9), a blank
  (a space) and the macro string itself. If a line does not start with # it is
  treated as a comment.

  The line starting with #0 is attached the function key F1, the line starting
  with #1 is attached the function key F2 etc.





                                         262





  C. Customizing Comal.lib.

  The link library file Comal.lib found in the directory ModuleDev contains
  interfaceroutines for the Comal routines as well as initialization and termi-
  nation code for a C programmed module.

  The initialization and termination code is compiler dependent and the code
  found in Comal.lib is for use with the  SAS/C compiler.  In the directory
  ModuleDev you will also find a file Comal0.lib which is a library file with-
  out the initialization and termination code. This one can be used to make a
  library file for other C compilers.

  As part of the initialization a routine by name C_Init is called and when
  the module is removed a routine C_Term is called. These routines should do
  the initialialization and termination necessary for your C compiled code. To
  make it you should look at the source for the c.o startup code.

  Here is the code used for SAS/C:


  ; Module startup for SAS C v.5.xx  -  version 92.07.29
  ;

                 include  Comal.i

                 XDEF     __base
                 XDEF     _XCEXIT
                 XDEF     __xcovf
                 XDEF     _SysBase
                 XDEF     C_Init
                 XDEF     C_Term

                 XREF     _LinkerDB
                 XREF     ___fpinit
                 XREF     ___fpterm
                 XREF     _ComalStruc

  AbsExecBase    EQU       $4

                 SECTION  InterfaceCode,CODE
                 CNOP     0,2

  ; Initialization routine
  ;
  C_Init:        MOVE.L  _ComalStruc,A0       ; Get Comal structure
                 MOVE.L  CS_MinStack(A0),D0   ; Get stack bottom
                 ADD.L   #$200,D0             ; Calculate "safe" bound
                 MOVE.L  D0,__base            ; .. and store
                 MOVE.L  AbsExecBase,_SysBase ; Set Exec base


                                         263





                 MOVE.L  A4,-(A7)             ; Initialize floating point
                 LEA     _LinkerDB,A4
                 JSR     ___fpinit
                 MOVE.L  (A7)+,A4

                 CLR.W   D0                   ; Set status
                 RTS                          ; .. and return

  ; Termination routine
  ;
  C_Term:        MOVE.L  A4,-(A7)
                 LEA     _LinkerDB,A4
                 JSR      ___fpterm           ; Terminate floating point
                 MOVE.L  (A7)+,A4
                 RTS
                 

  ; Report stack overflow (only used if stack check is turned on)
  ;
  __xcovf:       MOVEQ     #5,D2
  _XCEXIT:       MOVE.L    _ComalStruc,A0    ; Get Comal library base
                 JSR       -6(A0)            ; .. and return

                 SECTION   InterfaceBlock,BSS
                 CNOP      0,2

  _SysBase:      DS.L      1                 ; Exec library base
  __base:        DS.L      1                 ; "Safe" low bound of stack

                 END





















                                         264

