╔════════════════════════════════════════╗ 
                                                        
                     A D V A N C E D    T O P I C S     
                   ══════════════════════════════════   
                 ╚════════════════════════════════════════╝ 

                 ┌────────────────────────────────────────┐ 
                 │ <shft> F1  =  Toggle to regular window │ 
                 └────────────────────────────────────────┘ 

    ┌──────────────────────────────────────────────────────────────────┐ 
                                                                     
          If you are reading this document for the first time and    
      are unfamiliar with the commands for scrolling the text, you   
      should be aware that <ctrl> ▲ (hold down the <ctrl> key and    
      push the cursor UP key) will scroll the text UP and <ctrl> ▼   
      will scroll the text DOWN.  You can also use the PageUp and    
      PageDown keys to scroll quickly through the document.          
                                                                     
    └──────────────────────────────────────────────────────────────────┘ 

        Table of Contents                                   Line 
        ─────────────────                                  ────── 
        1. Wordwrap                                          64 

        2. Dictionaries                                     308 

        3. The INIT file                                    444 

        4. Zbex                                             680 

           1. Introduction  .   .   .   .   .   .   .   .   680 
           2. Zbex programs in files and in other windows   774 
           3. Properties of Zbex    .   .   .   .   .   .   834 
           4. Zbex utility programs .   .   .   .   .   .   921 
           5. Declaration statements    .   .   .   .   .   977 
           6. Assignment statements .   .   .   .   .   .  1110 
           7. The run and stop statements   .   .   .   .  1214 
           8. Input and output  .   .   .   .   .   .   .  1232 
           9. Comments  .   .   .   .   .   .   .   .   .  1650 
          10. Subscripts    .   .   .   .   .   .   .   .  1669 
          11. Program control (loop, if, goto)  .   .   .  1758 
          12. Relations .   .   .   .   .   .   .   .   .  1943 
          13. Functions .   .   .   .   .   .   .   .   .  2084 
          14. Procedures    .   .   .   .   .   .   .   .  2752 
          15. Tables    .   .   .   .   .   .   .   .   .  3000 
          16. Handling directories  .   .   .   .   .   .  3107 
          17. Special variables and labels  .   .   .   .  3213 
          18. Instructions for debugging    .   .   .   .  3500 
          19. Conditional compiles and other features   .  3731 

                         Advance Topics 

          20. Controlling the (text) display with putc     3932 
          21. Graphics  .   .   .   .   .   .   .   .   .  4031 
          22. MIDI instructions .   .   .   .   .   .   .  4978 


    ────────────────────────────────────────────────────────────────── 


                        ┌─────────────────────┐ 
                        │   W O R D W R A P   │ 
                        └─────────────────────┘ 

1.1       The Dmuse editor's normal mode of operation is with wordwrap 
      off.  In this mode, the right and left margins are ignored when 
      you type.  Furthermore, there are no restrictions on the movement 
      of the cursor; you can put the cursor anywhere in a window and 
      begin typing.  
 
1.2       Most word processors that have a wordwrap feature operate 
      under the following restrictions: 
 
         (1) Wordwrap is always on.  
 
         (2) The cursor must always stay within the margins.  
 
         (3) The cursor cannot move below the end of the text.  
 
1.3       To include a wordwrap feature in an editor that normally 
      operates without wordwrap requires a hybred form of wordwrap, 
      whose behavior can sometimes seem confusing.  This is why 
      wordwrap is treated as an advanced topic.  
 
1.4       This point can be illustrated with a simple example.  Suppose 
      wordwrap is off, and you move the cursor outside the right margin 
      (remember, you can put the cursor anywhere you want when wordwrap 
      is off).  Now you turn wordwrap on and begin to type; how should 
      the cursor behave?  This question never comes up with ordinary 
      word processors, because the cursor can never go outside the 
      margins.  
 
1.5       With wordwrap on, the Dmuse editor cursor behaves in the 
      following way: 
 
         (1) You may move the cursor anywhere in the window (inside or 
               outside the margins) using the cursor keys.  
 
         (2) When you type with the cursor outside the margins, the 
               cursor moves to the right in the ordinary way.  
 
         (3) When you type with the cursor inside the margins, the 
               cursor moves to the right until it bumps into the right 
               margin.  When this happens, the word that is currently 
               being typed, together with the cursor, jumps to the 
               left margin on the next line.  This way, the text inside 
               the margins never crosses a margin boundary.  This 
               jumping behavior is what gives the wordwrap feature 
               its name.  
 
1.6       The principal advantange of wordwrap is that it allows you to 
      type text without having to think about typing the Enter key 
      (carriage return) at the end of each line.  The Enter key has a 
      a special meaning when wordwrap is on.  It is used to mark the 
      end of a paragraph (sometimes called a "hard" carriage return).  
      The Enter key will place a carriage return character (CR) at the 
      point of the cursor and will place the cursor at the left margin 
      on the next line.  Any text to the right of the cursor will also 
      be moved to the next line.  
 
1.7       When Dmuse is first installed on your computer, the wordwrap 
      feature is disabled.  You must use the  F9  command with            
      sub-command "w" to turn the wordwrap feature on.  Once this 
      feature is turned on, it will be on every time you run Dmuse 
      until you use the  F9 , "w" command to turn the feature off.  
      The command KeyPad * will toggle the current window into and 
      out of wordwrap mode.  


    ────────────────────────────────────────────────────────────────── 


2.1       When wordwrap is on, moving the margins has a significant 
      effect on text that is inside the margins.  If the space between 
      the margins is compressed (left margin moves right or right 
      margin moves left), it is likely that one or more words will 
      bump the right margin.  This will cause the text between the 
      margins to be reformatted so that all encounters with the right 
      margin are again avoided.  If the space between the margins is 
      expanded (left margin moves left or right margin moves right), 
      the gap between end of each line of text and the right margin 
      will increase, and there will be cases where an extra word will 
      now fit at the end of a line where formerly it had to go on 
      the next line.  This will cause the text between the margins 
      to be reformatted.  
 
2.2       The commands for moving the margins are described in all 
      three of the help topics relating to the editor.  We summarize 
      them here.  
 
          left <shft-alt> ◄  = move left margin to the left (◄──).  
 
          left <shft-alt> ►  = move left margin to the right (──►).  
 
         right <shft-alt> ◄  = move right margin to the left (◄──).  
 
         right <shft-alt> ►  = move right margin to the right (──►).  
 
              <ctrl-shft> ◄  = move both margins to the left (◄──).  
 
              <ctrl-shft> ►  = move both margins to the right (──►).  
 
2.3       Since each line in a window has its own left and right 
      margins, the important question is which lines will have their 
      margins changed by these commands.  Certainly the line the 
      cursor is on will have its margin(s) changed.  By convention 
      (default) all lines in the paragraph containing the current 
      line will also have their margins changed.  Paragraphs are set 
      off by (1) a carriage return (CR) character, (2) a blank line, 
      or (3) an indented line of text.  You can change the number of 
      paragraphs over which the margin commands operate by pressing 
      <ctrl> F10 , choosing the "m" option and following the 
      instructions.  
 
2.4       If you want to move the margins for a specific group of 
      lines, either within a paragraph or spanning more than one 
      paragraph, the procedure is to use the <shft> ▼ keystroke to 
      create a stream highlight spanning the specific group of lines 
      in question, and then move the margin.  Care must be taken when 
      using this technique, since the reformatting operation will 
      sometimes add a line or subtract a line unexpectedly.  
 
2.5       If the lines of a paragraph have different margins, only 
      those lines which are contiguous to the line the cursor is on 
      and whose margins are the same as those of the line the cursor 
      is on will have their margins moved.  


    ────────────────────────────────────────────────────────────────── 


3.1       The ability to switch into and out of wordwrap mode, and to 
      reformat paragraphs in wordwrap mode by changing the margins is 
      both a strength and a weakness of the Dmuse editor.  It is a 
      strength because it provides you with a powerful tool for 
      reformatting paragraphs.  Let us say, for example that you want 
      to reformat the text in this paragraph so that the paragraph is 
      half as wide as it is now.  You can do this by placing the 
      cursor somewhere inside this paragraph and using the Left 
      <shft-alt> ► keystroke to move the left margin up to the left 
      edge of the paragraph.  Then (assuming you have activated the 
      wordwrap feature < F9 >) turn wordwrap on using the keyPad * 
      command.  Now use the Right <shft-alt> ◄ keystroke to move the 
      right margin to the left.  You will see the paragraph get 
      thinner and thinner.  Use the right <shft-alt> ► keystroke to 
      make the paragraph fat again.
 
3.2       With wordwrap still on, put the cursor somewhere inside 
      this paragraph.  Note that left margin for this paragraph has 
      not been moved to the left edge of the paragraph.  Now use 
      the Right <shft-alt> ◄ keystroke to move the right margin to 
      the left.  When the right margin bumps the text, everything 
      gets out of whack.  
 
3.4       Even worse, suppose the table below were part of a document 
      you were working on, and by accident you put the cursor in the 
      middle of it and used the right <shft-alt> ► keystroke to move 
      the right margin to the right.  Watch in horror as your table 
      falls apart completely (do this now).  
 
                ╔═══════════► Operation Table ◄════════════╗ 
                ╟──────────────────────────────────────────╢ 
                ║operation│  +   -   *   /   |   &  <<  >> ║ 
                ║─────────┼────────────────────────────────║ 
                ║literal  │  1   2   3   4   5   6   7   8 ║ 
                ║int var  │  9  10  11  12  13  14  15  16 ║ 
                ║function │ 17  18  19  20  21  22  23  24 ║ 
                ╚══════════════════════════════════════════╝ 
 
3.5       Best to turn wordwrap off now (Keypad *).  It should be 
      clear that care must be taken when going back and forth between 
      wordwrap_on and wordwrap_off.  If you do it right, it can 
      be a powerful feature of the editor and can save you a lot 
      of time.  If you do it wrong, it can cause you real headaches.  
      The main thing to remember is to be sure your margins are 
      in the right place before you turn wordwrap on.            


    ────────────────────────────────────────────────────────────────── 


4.1       The Dmuse editor maintains three separate data buffers to 
      aid in the storage and transfer of text within a window and 
      between windows: the box buffer, the line buffer, and the stream 
      buffer.  The box buffer is operational only when wordwrap is 
      off; the stream buffer is operational only when wordwrap is on.  
      The line buffer is operational all of the time.  
 
4.2       The stream buffer is so named because it stores text data 
      as one long stream of text, as opposed to a set of lines (line 
      buffer) or a rectangular area of the screen (box buffer).  When 
      data is put into the stream buffer, the line structure of that 
      data is ignored; when data is retrieved from the stream buffer, 
      the line structure (position of the line breaks) must be 
      constructed from the current position of the margins.  This 
      can only be done when wordwrap is on; hence the stream buffer 
      is only active when wordwrap is operating.  
 
4.3       The mechanism for selecting data for insertion into the 
      stream buffer is the stream highlight.  The stream highlight 
      works only when wordwrap is on.  A stream highlight can be 
      started by any of the four keystrokes: <shft> ◄, <shft> ▲, 
      <shft> ►, or <shft> ▼.  Once a stream highlight has been  
      started, the starting position serves as one of the endpoints 
      of the stream.  The other endpoint is the current position of 
      the selecting cursor.  You can move the selecting cursor within 
      margins using the same four commands: 
 
            <shft> ◄  =  move the selecting cursor to the left.  
 
            <shft> ►  =  move the selecting cursor to the right.  
 
            <shft> ▲  =  up one line.          
 
            <shft> ▼  =  down one line.
 
      If the selecting cursor is outside the right margin, it behaves 
      as if it were at the end of a line just inside the right margin.  
 
4.4       Once a stream of text is highlighted, there are two things 
      you can do.    
 
            Delete  will delete the highlighted stream.  The text will 
               be reformatted to fill in the space left by the deleted 
               stream.  
 
            <ctrl> Delete  will delete the highlighted stream and place 
               it in the stream buffer.  The text will be reformatted 
               to fill in the space left by the deleted stream.  
 
      Note: in the current implementation of Dmuse, the stream buffer 
      and the box buffer use the same storage space.  This means that 
      inserting material into the stream buffer will overwrite the 
      box buffer, and vise versa.  
 
4.5       To retrieve data from the stream buffer, you simply put the 
      cursor where you would like the steam to appear and press <ctrl> 
      Insert keystroke.  The stream will be inserted into the text,  
      irrrespective of whether insert mode is on or off.  


    ────────────────────────────────────────────────────────────────── 


                     ┌─────────────────────────────┐ 
                     │   D I C T I O N A R I E S   │ 
                     └─────────────────────────────┘ 

1.1       Dictionaries have several potential uses.  They can be 
      designed as general, foreign language dictionaries; they can be 
      designed as a combination of dictionary and commentary for 
      specific works in a foreign language; and they can be used to 
      include footnotes in a document.  A major advantage in the way 
      dictionaries are set up with Dmuse is that the user is able to 
      construct his/her own dictionaries for whatever purposes he/she 
      might have.  Dmuse provides the access apparatus for such 
      dictionaries.  
                        ▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂ 
1.2       A dictionary consists of a set of records.  Each record has 
      two fields.  The first field contains a key work; the second field 
      contains text, which might contain a definition, a translation, 
      or a pointer to another word in the dictionary.  
 
1.3       Dictionaries are stored in files on the disk.  In order for 
      Dmuse to access a dictionary, the dictionary must be loaded into 
      the Dmuse dictionary buffer.  This is done by pressing  F9  and 
      choosing the "d" option.  You will see that there are eight 
      slots into which dictionaries can be loaded.  For each of these, 
      you use the plus (+) sign to cycle through the dictionaries that 
      are available to load.  At the moment, no dictionaries are 
      included with Dmuse.  
 
1.4       If a dictionary is loaded into one of the eight slots, you 
      may access it through the commands: 
 
            slot 1   KeyPad Enter 
            slot 2   <shft> KeyPad Enter 
            slot 3   <ctrl> KeyPad Enter 
            slot 4   <ctrl-shft> KeyPad Enter 
            slot 5   <alt> KeyPad Enter 
            slot 6   <shft-alt> KeyPad Enter 
            slot 7   <ctrl-alt> KeyPad Enter 
            slot 8   <ctrl-shft-alt> KeyPad Enter 
 
      What happens when you type one of these commands is that Dmuse 
      takes the word that the cursor is currently under and searches 
      for this word among the key words in the dictionary.  If the 
      word is found, a box opens at the bottom of the screen showing 
      and word and the text (definition, etc.) associated with it.  
 
1.5       Dictionaries can also be used to register footnotes for 
      documents.  There is a provision for the keyword field to 
      contain a line number.  When a line number is present, Dmuse 
      will register a "find" only if the keyword occurs on the 
      specified line number.  For mechanism to work properly, you 
      would need to load the proper dictionary for each document 
      you were viewing.  
 
1.6       The target application for the footnote feature described 
      above is texts for people learning to read a foreign language.  
      Suppose, for example, that you are learning latin and you want 
      to read Caesar's Gallic Wars in the original text.  The editor 
      of that text might decide that you may need help with a certain 
      gramatical construction.  If each of the words in that 
      construction are entered into the dictionary with the proper 
      line number, then accessing the dictionary at one of these words 
      would produce the translation of that particular passage.  If 
      the line number did not match, Dmuse would then look in the 
      dictionary for a simple entry for the word in question.  


    ────────────────────────────────────────────────────────────────── 


2.1       At the moment, there are no dictionaries that work with 
      Dmuse.  This may change in time.  However, it is possible for 
      you to construct your own dictionaries.  

          The first record in a dictionary is the title of the 
      Dictionary.  It is ignored by Dmuse.  All subsequent records 
      are dictionaary records.  

2.2       A standard dictionary record has two parts: a key, and a 
      definition.  The key must start in column one and must be 
      highlighted with the  F6  function key (red).  The key must 
      be followed by one or more blanks (with no highlight).  The 
      definition field of the record may consist of either a 
      regular (non-highlighted) definition, or a reference to another 
      record in the dictionary.  The reference is another key, and 
      it must be highlighted with the  F6  function key (red).  

2.3       Since the maximum size of the dictionary box is 64 colunns, 
      a definition may need to have some line breaks in it (if it is 
      longer that 60 bytes).  We use the "\" character as a line break.  
      A definition may contain up to eight lines (seven line breaks).  
 
          Example of a section of a dictionary: 
 
          angehen           angehen  = concern \  ging, gegangen 
     angehören         angehören  = belong to 
     Angehörig         der Angehörig-  = person belonging to 
     Angeklagt         der Angeklagt-  = defendant, accused 
     Angelegenheit     die Angelegenheit, -en  = affair 
     angenehm          angenehm  = pleasant, agreeable 
     2.4      It is also possible to have line specific entries in a 
      dictionary.  This allows one to enter commentary to a document 
      or a specific translation of section of a foreign text.  A line 
      specific entry begins with the line number, followed immediately 
      by the key.  Both the line number and the key are highlight with 
      the  F6  function key (red).  When looking up a key in a 
      dictionary, Dmuse will first check the line specific entries 
      and then the standard entries.  For this reason, the line specific 
      entries must be the first ones in the dictionary, and they must 
      be in order of lines.  Standard entries must be in alphabetical 
      order.  Example of line specific entries from a dictionary: 

      122lief           lief nich mehr = wouldn't run any more 
     122nicht          122lief 
     122mehr           122lief 
     125den            den Bären = Hotel Bären 
     125Bären          125den 
     │ 
      Notice that all three words in the phrase "lief nich mehr" are 
      referenced in the dictionary (on line 122) but that definition 
      does not need to be repeated three times.  122lief is used as 
      a cross reference key to the single definition.  

2.5       Dictionaries are time consuming to build, but a lot of fun 
      to use.  It is hoped that this application could open up new 
      ways to teach foreign languages, and to enjoy reading works 
      in their original text.  


    ────────────────────────────────────────────────────────────────── 


                   ┌─────────────────────────────────┐ 
                   │   T H E    I N I T    F I L E   │ 
                   └─────────────────────────────────┘ 

1.1       When Dmuse starts up, it reads a file called INIT in the 
      home directory for Dmuse (default = /usr/local/apps/disp).  This 
      file contains the values of several parameters that determine 
      the configuration of Dmuse for your computer.  You can load this 
      file in a window and examine and/or change these parameters, if 
      you wish.  But you must use caution in doing this; otherwise the 
      next time you try to run Dmuse, it might not work.  
 
1.2       The INIT file consists of a minimum of 70 records.  A sample 
      INIT file is displayed below.  
 
      Record #    Contents 
      ────────    ────────────────────────────────────────── 
         1        Starting Window Size: 100 x 43 (columns, rows) 
         2        Starting Window Position: 20, 20 (x, y) 
         3        Starting Window Font: 10 x 21 (width, height) 
         4        color[ 0] black 
         5        color[ 1] red 
         6        color[ 2] green 
         7        color[ 3] coral 
         8        color[ 4] steel blue 
         9        color[ 5] violet 
        10        color[ 6] dark turquoise 
        11        color[ 7] white 
        12        color[ 8] indian red 
        13        color[ 9] goldenrod 
        14        color[10] sienna 
        15        color[11] sea green 
        16        color[12] forest green 
        17        color[13] dark orchid 
        18        color[14] pink 
        19        color[15] blue 
        20        Window Title      0x60  --> Black on dark turquoise 
        21        Select Highlight  0x7f  --> Blue on white 
        22        Select Highlight2 0x5f  --> Blue on violet 
        23        Select Shadow     0x1f  --> Blue on red 
        24        Dialog Border     0x10  --> Black on red 
        25        Box Highlight     0x10  --> Black on red 
        26        Window Text       0x07  --> White on black 
        27        Dialog Text       0x47  --> White on steel blue 
        28        Txt Highlight[ 1] 0x10  --> Black on red 
        29        Txt Highlight[ 2] 0x01  --> Red on black 
        30        Txt Highlight[ 3] 0x80  --> Black on indian red 
        31        Txt Highlight[ 4] 0xa7  --> White on sienna 
        32        Txt Highlight[ 5] 0x30  --> Black on coral 
        33        Txt Highlight[ 6] 0x90  --> Black on goldenrod 
        34        Txt Highlight[ 7] 0xe0  --> Black on pink 
        35        Txt Highlight[ 8] 0x02  --> Green on black 
        36        Txt Highlight[ 9] 0x20  --> Black on green 
        37        Txt Highlight[10] 0xc7  --> White on forest green 
        38        Txt Highlight[11] 0x50  --> Black on violet 
        39        Txt Highlight[12] 0x47  --> White on steel blue 
        40        Txt Highlight[13] 0x60  --> Black on dark turquoise 
        41        Txt Highlight[14] 0xd7  --> White on dark orchid 
        42        Txt Highlight[15] 0xb7  --> White on sea green 
        43        Graphics background: 7      White 
        44        Graphics bitplane 1: 0      Black 
        45        Graphics bitplane 2: 3      Coral 
        46        Graphics bitplane 3: 1      Red 
        47        Graphics bitplane 4: 15     Blue 
        48        MIDI Installed: N 
        49        Ibex Active: Y 
        50        Wordwrap Active: Y 
        51        Dictionary Active: N 
        52        Humdrum Support: 0   (values: 0=none, 1=standard, 2=extended)
        53        Follow Links: N 
        54        ZPATH[0]          (must be full path names) 
        55        DICTIONARIES[0]   (file names only, maximum length = 12) 
        56        QPATH[0]          (maximum path size = 99) 
        57        Maximum length of zbex temporary string = 10000000 
        58        Speed Parameter = 40000 
        59        Printer I default 
        60                    type: LaserJet III                    | 
        61                    port: 0                               | 
        62                 topline: 5                               | 
        63         number of lines: 54                              | 
        64          lines per inch: 6                               | 
        65             left margin: 80                              | 
        66        number of copies: 1                               | 
        67             orientation: 0                               | 
        68                   font1: 12.0 Upright Medium Courier     | 
        69                   font2: 12.0 Upright Bold Courier       | 
        70                   font3: 12.0 Italic Medium Courier      | 
        71                   font4: >(10U>(s1p14.4v0S               | 
           
          
1.3   Record 1 gives the starting X Window size, expressed in columns 
      and rows.
 
 
1.4   Record 2 gives the starting X Window position, expressed in <x,y> 
      coordinates measured from the upper left corner of the Dislay.   
      
 
1.5   Record 3 gives the starting Text font size, exressed in pixels.  
      Allowed values at the moment include:  7 x 6, 8 x 12, 8 x 15, 
      9 x 18, 10 x 21, and 11 x 24.  

1.6   Records 4 to 19 contain the 16 colors used by Dmuse.  These  
      color names are part of a standard UNIX list common to almost 
      all UNIX and Linux workstations (PC's).  You may make changes 
      to this list, provided that you know the list of color names   
      available on your computer.                                      

1.7   Records 20 to 27 specify the color combinations for various 
      parts of the Dmuse screen.  For example, Window Text (here, 
      White on black) is the main blackboard of Dmuse; Dialog 
      Border (here, Black on red) are the colors of the Dmuse 
      main border.  The first number (0 to f) is the color of the 
      background; the second number is the color of the foreground 
      (text).  

1.8   Records 28 to 42 contain the 15 possible color attributes for 
      screen characters.  Including the default "white on black," 
      there are 16 combinations.  
 
      0.  F5     4. <shft> F5     8. <ctrl> F5    12. <ctrl-shft> F5 
      1.  F6     5. <shft> F6     9. <ctrl> F6    13. <ctrl-shft> F6 
      2.  F7     6. <shft> F7    10. <ctrl> F7    14. <ctrl-shft> F7 
      3.  F8     7. <shft> F8    11. <ctrl> F8    15. <ctrl-shft> F8 
 
      F5 is for ordinary text (no highlight), and this attribute is 
      already given in record 26.  The attributes for the remaining 
      conbinations are given in the order shown.  For example, the 
      colored boxes above were made by pressing <F6>.  In the case of 
      the example INIT file, the attribute for <F6> (Txt Highlight[1]) 
      is hexidecimal 10; i.e., black text on a red background.  If 
      these are the colors you see, then your INIT file has the same 
      value for Txt Highlight[1].
 
1.9   Records 43 to 47 specify the colors (0 = black, 1 = dark blue, 
      etc.) used for the graphic bitplanes.  Dmuse can address four 
      bitplane colors on top of a background (how this is done is 
      explained in the section on Zbex).  
 
 
1.10  Record 48 indicates whether or not a MIDI interface is installed 
      on the system, and if so, the name of the port, the type of    
      device, and the path location of the midi_in program.  
 
1.11  Records 49 to 51 contain the flags that tell whether Zbex, 
      Wordwrap, and the Dictionaries are active or not.  
 
 
1.12  Record 52 indicates the level of Humdrum support.  Humdrum is 
      a set of Linux type commands (bash shell), which are useful 
      in analyzing files containing musical data in various formats.  

1.13  Record 53 specifies whether directory listings should follow 
      links to actual files/directories, or merely list links as 
      links.  

1.14  Record 54 gives the number of sub-directories in which the Ibex 
      compiler will look for programs.   In our example, the number 
      is zero; but normally it will be one or greater.  The actual 
      full path directories follow in succeeding records.  The path 
      names start in column 3 (with the / character).  

      Normally when you run an Ibex program, you must first type "zz" 
      to call the compiler or "ww" to call the interpreter.  However, 
      if you have an Ibex program that you expect to use many times (a 
      utility program, for example) you may want to put the program in 
      a designated sub-directory, where Ibex will know to look.  Then 
      all you need to do to run the program is to type the name of the 
      program (actually the name of the file containing the program).  
      For more information on this feature, see the section on Ibex in 
      this document.  

1.15  Record 55 specifies the dictionaries which should be loaded at 
      startup time, and also any other dictionaries that Dmuse should 
      know about.  All dictionary files specified in this record must 
      exist in the sub-directory DICT of your Dmuse home directory.  
      For more information on this subject see the section on 
      dictionaries in this document.  
 

1.16  Record 56 specifies the number virtual disk assignments.  In this 
      example, the number is zero, but normally it might be as large 
      as 20.  Shown below is a more realistic case: 

      QPATH[19]         (maximum path size = 99) 
        A:/ -->   /mnt/floppy 
        C:/ -->   / 
        D:/ -->   /usr/local/apps/arc 
        E:/ -->   /home/wbh 
        F:/ -->   /usr/local/apps/musedata/baroque    (bach, handel, corelli, telemann)
        G:/ -->   /usr/local/apps/musedata/classical  (haydn, mozart, beethoven)
        H:/ -->   /usr/local/apps/musedata/romantic   (schubert, brahms) 
        I:/ -->   /usr/local/apps/musedata/other      (everything else) 
        J:/ -->   /usr/local/apps/xdata/part1 
        K:/ -->   /usr/local/apps/xdata/part2 
        L:/ -->   /usr/local/apps/xdata/part3 
        M:/ -->   /usr/local/apps/release 
        N:/ -->   /usr/local/apps/musprint 
        O:/ -->   /usr/local/apps/temp 
        P:/ -->   /usr/local/apps/research 
        Q:/ -->   /usr/local/apps/editions 
        R:/ -->   /usr/local/apps/disp 
        S:/ -->   /mnt/zip 
        T:/ -->   /mnt/cdrom 
 
      Column 3 contains a "disk" letter.  These could be capital and in 
               alphabetical order.  
      Columns 4 - 11 read ":/ -->   " 
      Columns 12 -- contain the path name to be assigned to the specified 
               disk letter.  Comments may follow after one or more blanks.  
 
      Dmuse maintains a current working directory for each of these virtual 
      disks.  This makes it easy to jump between various diverse branches 
      of the file system tree.  
 
      one time on your system.  Dmuse needs to know this number in 
      order to keep Ibex programs from trying to open too many files.  

1.17  Record 57 specifies a maximum length for storing temporary 
      strings in Ibex.  See the section on Ibex in this document.  
 

1.18  Record 58 contains the cursor speed parameter (not used in Linux) 
 
1.19  Records 59 to 71 provide default parameters for a printer, if one 
      is connected to the system.  The parameters are for use by the 
      "print screen" command.  At the moment, only LaserJet compatable 
      printers are supported in this capacity.  For a description of 
      these parameters, see the section on the print-screen command in 
      the Using the screen editor help topic or the section on printer 
      parameters in the Editor quick reference help topic.  


    ────────────────────────────────────────────────────────────────── 


                            ┌─────────────┐ 
                            │   Z B E X   │ 
                            └─────────────┘ 

1.1       Each of the 30 Dmuse windows can be made to behave like a 
      computer terminal.  The command to make this happen is 
      <shft> KeyPad *.  This command actually toggles the connect 
      mode flag; i.e., press it again and the window stops acting 
      like a terminal.  
 
1.2       The main thing that is different with connect mode on is 
      the action of the Enter key.  With connect mode on, the Enter 
      key will send the contents of the line the cursor is on to 
      the computer as an input.  How the computer responds to that 
      input depends on the current state of the window.  When the 
      window is waiting for you to give it the name of a program to 
      run, it will present the prompt, "Ready for program."  If you 
      simply press the Enter key, that is, enter a blank line, the 
      window will respond with the lines: 
 
     │Not found 
     │Ready for program 
     │╱
 
      This happens because Dmuse does not recognize a blank line 
      as the name of a program.  If you enter a line that Dmuse 
      does recognize as a program, it will tranfer control to 
      that program; i.e., that program will begin to run.  
 
1.3       There are several programs that Dmuse will recognize, but 
      we should start with the main one, namely, the Zbex compiler 
      and interpreter.  This program will compile an Zbex program 
      that you specify and then run that program.  The Zbex 
      compiler/interpreter is called by entering "zz".  If you 
      type "zz <Enter>", you will see the following lines appear 
      in your window.  
 
     │zz 
     │Current Library is <current directory> 
     │Program file?  
     │╱ 
      
1.4       The prompt for a program file is the way the Zbex compiler 
      asks you for the Zbex program you want to run.  You have three 
      options: (1) you may enter the name of a file, or (2) you may 
      enter a line with the form "*<window number>", e.g., *6, or 
      (3) you may enter a blank line.  If you enter the name of a 
      file, the Zbex compiler assumes that the file contains an Zbex 
      program, and it will attempt to compile that program into an 
      executable module that can be handed off to the interpreter.  
      If you enter "*<window number>", the Zbex compiler assumes 
      that the specified window contains the Zbex program you want 
      to run.  If you enter a blank line, the Zbex compiler expects 
      you to enter the program, line by line in the current window.  
 
1.5       Let's follow the third option for a moment.  Suppose you      
      enter a blank line at the "Program file?" prompt.  Then you will     
      see the following lines in your window: 
 
     │zz 
     │Current Library is <current directory> 
     │Program file?  
     │                              (blank line entered here) 
     │Input program from terminal.  
     │╱ 
 
1.6       You are now expected to enter a program.  Let's suppose you 
      enter the following two-line program: 
 
      putc Hello World 
      run 
 
      You should then see the following lines in your window: 
 
     │zz 
     │Current Library is <current directory> 
     │Program file?  
          │Input program from terminal.  
     │putc Hello World 
     │run 
     │** S=2, P=8, L=231, M=410 ** 
     │Hello World 
     │Ready for program 
     │╱ 
 
      The line with the numbers tells you that the program successfully 
      compiled; the next line "Hello World" is the output from the 
      program; and the "Ready for program" prompt tells you that the 
      window is again waiting for you to give it the name of a program
      to run.  

    ────────────────────────────────────────────────────────────────── 

             ┌────────────────────────────────────────────────┐ 
             │ 2. Zbex programs in files and in other windows │ 
             └────────────────────────────────────────────────┘ 
                                                   
2.1       The first option (Zbex program in a file) and the second 
      option (Zbex program in another window) work in essentially 
      the same manner as the third option described above (Zbex 
      program entered in the current window).  

2.2       Let's suppose that you create a file called "TEST.Z" in the 
      current directory, and that this file holds the following two 
      records: 
 
      putc Hello World 
      run 
 
      Then if you type "zz <Enter>", and enter "TEST.Z" at the  
      "Program file?" prompt, you will see the following lines in 
      your window: 
 
     │zz 
     │Current Library is <current directory> 
     │Program file?  
     │TEST.Z 
     │** S=2, P=8, L=231, M=410 ** 
     │Hello World 
     │Ready for program 
     │╱ 
 
2.3       As before, the line with the numbers tells you that the 
      program successfully compiled; the next line "Hello World" is 
      the output from the program; and the "Ready for program" prompt 
      tells you that the window is again waiting for you to give it 
      the name of a programto run.  
 
2.4       Suppose window 4 contains the following two line: 
 
      putc Hello World 
      run 
 
      You can run this program from your current window by typing 
      "*4" at the "Program file?" prompt.  You will then see the 
      following lines in the window: 
 
     │zz 
     │Current Library is <current directory> 
     │Program file?  
     │*4    
     │** S=2, P=8, L=231, M=410 ** 
     │Hello World 
     │Ready for program 
     │╱ 
 
 
2.5       This concludes the documentation on how to compile and 
      run an Zbex program.  What follows now is a description of 
      the Zbex programming language.  

    ────────────────────────────────────────────────────────────────── 

                     ┌─────────────────────────┐ 
                     │  3. Properties of Zbex  │ 
                     └─────────────────────────┘ 

3.1       The Zbex programming language is very similar to the 
      Pascal programming language.  If you have programmed in 
      Pascal, you will find learning Zbex quite easy.  There are 
      two cautions you need to keep in mind.  (1) While Zbex 
      statements on the surface look the same as Pascal statements, 
      the implimentation of the statements (that is, what the 
      program actually does) may be slightly different in some 
      cases.  (2) Zbex is not as versatile as Pascal.  There are 
      many things you can do with Pascal that you cannot do with 
      Zbex.  Zbex, for example, does not allow you to use pointers.  
      You cannot compile and link separate modules with Zbex.  This 
      means that you cannot build programs from separate pieces.  
 
3.2       If Zbex is essentially a scaled down version of Pascal, what 
      are its advantages?  Why not just program in Pascal?  The 
      answer to this question has several parts.  
 
      (1) Zbex compiles into an intermediate language, which is 
          executed by an interpreter.  The advantage is that your 
          Zbex program never really controls the computer; the 
          interpreter controls the computer, with your Zbex program 
          telling the interpreter what to do.  If your Zbex program 
          asks the interpreter to do something that is illegal, 
          that is, something that would crash the system, the 
          interpreter won't do it; instead, the interpreter 
          will flag the offending line of code in your Zbex 
          program, giving you information about what was wrong, and 
          then halt the execution of your program.  The remaining 
          windows of Dmuse will be unaffected by this.  
 
      (2) All Zbex programs require the same interpreter.  The             
          interpreter has been set up to handle more than one Zbex 
          program at the same time.  It does this by switching 
          between programs after executing a specified number of 
          instructions.  Zbex programs, once started, will continue 
          to run in the background when you change to another 
          window.  You can use the Dmuse editor in one window, 
          while several Zbex programs are running in other windows.  
 
      (3) Zbex was designed primarily as a language to manipulate 
          string variables.  In Zbex, string variables actually 
          have two components: (1) the current run-time length of 
          the string, and (2) the actual characters in the string.  
          A string variable may be declared to have a maximum 
          length of 100 characters, but the actual length of the 
          string during the running of the program may vary from 
          zero to 100 characters.  
 
      (4) Zbex not being a standard language has allowed us to add 
          extra features, without running the risk of confusing 
          people who are use to a standard language.  Features added 
          to Zbex include, (1) specialized functions to perform 
          common tasks more quickly, (2) specialized variable types 
          such as random access tables, (3) specialize instructions 
          for applications in music.  Included in this group are 
          commands to display musical notation in graphics mode and 
          commands to control a (Roland) MPU 401 type midi card.  
 
      (5) The real purpose of Zbex is to allow you to write "throw 
          away" programs quickly and easily.  It has been estimated 
          that 80 to 90 percent of the programs written in course of 
          doing research on the computer are used once and then thrown
          away.  In this type of situation, it is important to have 
          (1) good diagnostics at compile time, (2) good run-time 
          debugging tools, (3) quick turnaround between successive 
          versions of a program, and (4) a safe execution enviroment 
          that will not trash the system.  Zbex has all of these 
          features.  
 
3.3       In spite of Zbex's real purpose being the ability to write 
      "throw away" programs, I have found Zbex to be an excellent 
      language for writing software for complicated tasks such as 
      music printing.  It is hard enough to get such programs to run 
      properly, without having to worry about whether I have 
      overwritten some critical section of computer memory in the 
      process.  Zbex allows me to check out the logic of a program 
      in a friendly environment, and later to convert that logic to 
      a less forgiving language such as C.  This two-step process can 
      save a lot of time in the long run.  


    ────────────────────────────────────────────────────────────────── 

                    ┌──────────────────────────┐ 
                    │ 4. Zbex utility programs │ 
                    └──────────────────────────┘ 

4.1       Dmuse comes with six Zbex utilities program.  These programs 
      are in the library zprogs of the Dmuse home directory.  Programs 
      in this library can be run simply by typing their name (without 
      the .z extension) and Enter.  You can add your own Zbex programs 
      to this library and they, too, can be run simply by typing the 
      name of the program.  Like the programs already in the library, 
      they must have a .z extension.  


4.2                        Six Zbex Utility Programs 
          ──────────────────────────────────────────────────────────── 
 
      dcom       This program will compare the contents of two libraries 
                 and report on all differences.  The contents of all 
                 sub-libraries will be examined.  
 
      diff       This program will compare two files and report on all 
                 differences.  The program works only when the two files 
                 are nearly the same.  
 
      fcompare   This is a program to compare all files in two libraries 
                 with a specified extension.  The files will be printed 
                 out with the labels BAD or OK, depending on whether they 
                 are identical or not.  The files in library 1 will serve 
                 as the model list.  
 
      lost       This is a program to find a specified string in a set 
                 of path names.  It is called lost, because it is useful 
                 in helping you to find a file whose location (and possibly 
                 whose complete name) you have forgotten.  

      qed        This is a line editor that allows you to edit very large 
                 flat ASCII files that won't fit in a Dmuse window.  The 
                 maximum number of records that will fit in a Dmuse window 
                 is 98000.  
 
      search     This program will search for a specified string in all 
                 files in the current library having a particular extension. 
                 This program is useful in searching a set of .c files 
                 for all examples of the use of a particular variable.  

 

4.3       The next several chapters describe the details of the of the 
      Zbex programming language.  After you have studied this documentation 
      and have tried writing a few programs, you might want to return to 
      this section and examine the six programs above to see how they 
      use various features of the Zbex language.  


    ────────────────────────────────────────────────────────────────── 

                    ┌───────────────────────────┐ 
                    │ 5. Declaration statements │ 
                    └───────────────────────────┘ 

5.1       The Zbex language recognizes four data types: strings, 
      bit-strings, integer numbers, and real numbers.  Each of 
      these data types can have two forms: literal (fixed for the 
      life of the program) and variable.  Literal data types need 
      no introduction; the compiler accepts them wherever they are 
      found.  Variable data types (called variables) must be 
      declared (identified to the compiler) before they can appear 
      in a program.  
 
      Strings:  A string is a sequence of bytes of specified length.  
         The length is a integral part of the data type.  Literal 
         strings are enclosed in double quotes.   Examples: 
 
             "TEXT.Z",  "Now is the time.",  "123435.6789" 
 
         Note that the contents of a string is case sensitive.  
         Variable strings are declared using the str statement, 
         following by a list of variables (separated by commas).  
         Each variable name must be followed immediately by a 
         dot and a number.  The number tells the compiler how 
         many bytes of compute memory to allocate for the string 
         variable.  Example: 
 
             str temp.100, file.100, big.10000000 
 
         This statement identifies three string variables to the 
         compiler: (1) temp, with maximum length of 100 bytes, 
         (2) file, with maximum length of 100 bytes, and (3) big, 
         with maximum length of ten million bytes.  

         At the onset of a program, all string variables are assigned 
         a run-time length of zero.  We call this the null string.  
         When a string takes on a value other than the null string, 
         this value has two components, a length, and a sequence of 
         bytes.   Example: 

             temp = "Now is the time  " 

         In this statement, the string variable, temp, is assigned a 
         length of 17 and the byte seqence Now is the time  .  
 
      Bit strings:  A bit string is a sequence of bits of specified 
         length.  The length is a integral part of the data type.  
         Literal strings are represented as a stream of zeros and 
         ones enclosed in double quotes.   Examples: 

             "010011",  "0000000101010101",  "00000000000" 

         Variable bit strings are declared using the bstr statement, 
         following by a list of variables (separated by commas).  
         Each variable name must be followed immediately by a 
         dot and a number.  The number tells the compiler how 
         many bits of compute memory to allocate for the bit string 
         variable.  Example: 
 
             bstr test1.100, test2.100, bigbit.5000000 
 
         This statement identifies three bit string variables.  
         (1) test1, with maximum length of 100 bits, (2) test2, 
         with maximum length of 100 bits, and (3) bigbit, with 
         maximum length of five million bits.  

         At the onset of a program, all bit string variables are 
         assigned a run-time length of zero.  We call this the null 
         bit string.  When a bit string takes on a value other than 
         the null bit string, this value has two components, a length, 
         and a sequence of bits.   Example: 

             test1 = "10101010101" 

         In this statement, the bit string variable, test1, is assigned 
         a length of 11 and the bit seqence 10101010101.  
 
      Integer numbers:  An integer number can have any value from 
         -2,147,483,648 to +2,147,483,647.  Literal integers can be 
         represented either in decimal format or in hexidecimal format.  
         Examples: 

             0  10  010  +20  -50   0xff   0x00ff   0x7fffffff 

         The second and third examples are equivalent.  The plus sign 
         in the fourth example is optional.  The minus sign in the 
         fifth example is not optional, and it must come immediately 
         before the number.  The sixth and seventh examples are 
         equivalent and have the decimal value of 255.  The eighth 
         example is the largest positive value that can be taken 
         by an integer number.  

         Variable integers are declared using the int statement, 
         following by a list of variables (separated by commas).  
         Example: 

             int a,b,d,  d,e,f 

         This statement identifies six integer variables to the 
         compiler: a, b, c, d, e and f.  
 
         At the onset of a program, all integer variables are assigned 
         a run-time value of zero.  

 
      Real numbers:  A real number can have up to sixteen significant 
         digits and take on absolute (plus or minus) values from 
         1.0 * 10(exponent -309) to 9.9 * 10 (exponent 308) as well 
         as the value 0.  Literal real numbers be represented either in 
         fixed point format or floating point format.   Examples: 
 
             0.0  1.0  1.000   +2.34e+20    -3.54e-30 
 
         The second and third examples are equivalent.  The plus signs 
         in the fourth example are optional.  The minus signs in the 
         fifth example is not optional, and they must come immediately 
         before the numbers thay modify.  

         Variable real numbers are declared using the real statement, 
         following by a list of variables (separated by commas).  
         Example: 

             real x,y,z 

         This statement identifies three real variables to the 
         compiler: x, y and z.  
 
         At the onset of a program, all real variables are assigned 
         a run-time value of zero.  


    ────────────────────────────────────────────────────────────────── 

                    ┌──────────────────────────┐ 
                    │ 6. Assignment statements │ 
                    └──────────────────────────┘ 

6.1       The most common way to set the value of a variable is with 
      an assignment statement.  Assignment statements take the form 
      <variable> = <expression>.  The two sides of an assignment 
      statement must have data types of the same type.  The expression 
      on the right may have one or more elements.  For strings and 
      bit strings, elements in an expression are combined using the 
      concatenate operator (//).   For integers and real numbers, 
      elements are combined using one or more of the arithmetic 
      operators: 

              integer and real           integer only 
            ────────────────────      ──────────────────── 
               + = add                 &   =  and 
               - = substract           |   =  or 
               * = multiply            >>  =  shift right 
               / = divide              <<  =  shift left 
 
      In the absense of parentheses, arithmetic operators are processed 
      in a left to right fashion, i.e., not according to the rules of 
      algebra.  
 
      An element of an expression may be a literal, a variable or 
      a function.  Functions will be covered later.  Examples: 

      Strings: 

         str temp.80,name.80 

         name = "<your name>" 
         temp = "Good afternoon, " // name // ", I am pleased " 
         temp = temp // "to meet you." 

         In this case, the string temp has the value 

         Good afternoon, <your name>, I am pleased to meet you.  

         Note that the concatenate operator (//) must have a 
         blank on either side of it.  
 
      Bit strings: 
 
         bstr test1.80, test2.80 
 
         test1 = "1010101" 
         test2 = test1 // "000" // test1 

         In this case, the bit string test2 has the value 

         10101010001010101 

      Intergers: 

         int  a,b,c 

         a = 10 
         b = 20 
         c = a >> 2 * b + 4 

         In this case, the value of c is 

            10 shifted right 2 = 2, times 20 = 40 plus 4 = 44 

      Real numbers: 

         real  x,y,z 

         x = 10.0 
         y = 5.0 
         z = x - 3.0 / y * 3.0 

         In this case, the value of z is 

            10.0 minus 3.0 = 7.0, divided by 5.0 = 1.40, time 3.0 = 4.2 

         For interger and real number operations, you can use 
         parentheses to control the order in which operations are 
         performed.  

      For all assignment statements the right hand side is evaluated 
      before the left hand side is changed.  Examples: 
 
      1. bstr test1.80 
 
         test1 = "1010101" 
         test1 = test1 // "000" // test1 

         In this case, the bit string test1 has the value 

         10101010001010101 

      2. int  a 

         a = 10 
         a = a + 1 
 
         In this case, the value of a is 11 


    ────────────────────────────────────────────────────────────────── 

                   ┌────────────────────────────────┐ 
                   │ 7. The run and stop statements │ 
                   └────────────────────────────────┘ 

7.1       The last statement in an Zbex program must be the run 
      statement.  The program will compile but will not run without 
      this statement.  Any code or text below (after) the run 
      statement in a file or in a window will be ignored.  

7.2       You can cause your program to terminate at any location 
      with the stop statement.  The compiler automatically compiles 
      a stop statement before the run statement and before any 
      procedure statement.  This prevents program control from 
      entering a procedure by accident.  


    ────────────────────────────────────────────────────────────────── 

                        ┌─────────────────────┐ 
                        │ 8. Input and output │ 
                        └─────────────────────┘ 

8.1       After mastering the declaration statements and assignement 
      statements, the most important Zbex instructions to learn are the 
      input and output instructions.  

      getc  =  get data from the next line entered from the screen.  

          getc may stand alone as an instruction, or it may take 
          variable arguments.  If it stands alone, the program will 
          stop running until the Enter key is pressed.  If it has 
          arguments (variables), Zbex will attempt to assign values 
          to the variables using the contents of the next entered 
          line.  Examples: 
 
          int a,b 
          str temp.10 
          real x 
 
          getc a x b     (entered line = "10   2.0   15") 
 
             In this case, a will be assigned the value 10, x the 
             value 2.0, and b the value 15 

          getc temp a b  (entered line = "1 2 3 4 5 6 7 8 9") 

             In this case, temp will be given the value 1 2 3 4 5 
             (i.e., the first 10 bytes of the line, a will be 
             assigned the value 6, and b the value 7.  

          You may reset the point at which Zbex processes a line 
          by using the .t<number> format command.  For example, 
          .t1 will reset the process pointer to column one of the 
          input line.  Example: 
 
          getc temp .t1 a b   (entered line = "1 2 3 4 5 6 7 8 9") 
 
             In this case, temp will be given the value 1 2 3 4 5 
             (i.e., the first 10 bytes of the line, a will be 
             assigned the value 1, and b the value 2.  
 
          If you respond to a getc by entering two exclaimation points 
          (!!) at the begining of a line, your Zbex program will 
          terminate.  This is an important fact to remember, since 
          there are only two ways to stop an Zbex program once it 
          has been started (the other is with the <ctrl> Break key).  
 
      putc  =  put data to screen at the point where the cursor is.  
 
          putc may stand alone as an instruction, or it contain an 
          output string.  If the output string has variable arguments, 
          they must be preceded immediately the ~ character and 
          followed immediately by a blank.  If the output string ends 
          with three dots (...), no carriage return is given (the 
          cursor stays on the same line); otherwise the cursor is 
          moved to the next line after writing the output string to 
          the screen.  Examples: 

          int a,b 
          str temp.10 
          real x 

          a = 10 
          temp = "Hello" 
          putc temp                 (writes temp to the screen) 
          putc ~temp                (writes Hello to the screen) 
          putc a                    (writes a to the screen) 
          putc ~a                   (writes 10 to the screen) 
 
          putc Hello ...  
          putc there.               (writes Hello there to the screen) 

          There are several format commands that can be used to control 
          how things are written to the screen.  All format commands 
          start with a dot (.) and end with a blank.  Format commands 
          may be combined.  Examples: 
 
            .t20  =  move cursor to column 20 
            .t(a) =  move cursor to column <a> where a is declared as 
                       an integer variable and has some value.  
            .w6   =  right justify integer and real variables in a 
                       space with 6 columns 
            .x    =  display integers in hexadecimal format 
            .e4   =  display real numbers in floating point notation 
                       with 4 digits to the right of the decimal point 
            .f2   =  display real numbers in fixed point notation with 
                       2 digits to the right of the decimal point 
            .c1   =  precede integers with a dollar sign ($).  
            .c(a) =  precede integers with a <depends on a> sign.  
            .d2   =  display integers as if they had two digits to 
                       the right of a decimal point 

            a = 120 
            putc .c1d2 ~a    (line out = $1.20) 
            b = 4 
            putc .c(b)d2 ~a  (line out = DM1.20) 
 
          There is also an easy way to put out any byte value from 
          0 to 255 using the .b command.  This command follows the 
          same rules as the format commands; i.e., it starts with 
          .b, followed by an literal integer or an integer variable 
          in parentheses, followed by a blank.  Example: 
 
            putc .b27 A...  
 
          This statement puts out the two byte sequence <esc>A, which 
          happens to be the command for moving the cursor up one line 
          (for a list of all escape sequences, see the section on 
          controlling the (text) display with putc).
                  
 
      putp  =  put data to the printer port LPT1.  
 
          putp works the same way as putc, except that the output is 
          directed to the printer port LPT1 (to your printer, if it 
          is on).  
 

8.2       At this point, you should try experimenting with writing 
      Zbex programs.  You know how to enter numbers and strings into 
      the computer, how to combine them, and how to print them out 
      on the screen.  The last instruction in an Zbex program must 
      be "run" See example in paragraph 1.6.  

8.3       When you have mastered the getc and putc commands, it is 
      time to learn how to get records from a file and how to put 
      records into a file.  To operate on a file, you must first 
      open it.  A file can be opened for seven different types of 
      operations: 

        1 = open file for use by getf (get ASCII records sequentually 
              from the file), 
        2 = open file for use by putf (put ASCII records sequentually 
              to a file.  Write over any previous contents), 
        3 = open file for use by putf (append ASCII records onto an 
              existing file; no contents are over written), 
        4 = open file for random read only (for use with files with 
              read permission but no write permission), 
        5 = open file for use by random read and write (blocks of 
              arbitrary size), 
        6 = open file for use by write (create new file), 
        7 = open file for use by write (append blocks of arbitrary 
              size).  

8.4       When you open a file, you must give it a tag.  The tag 
      is a number from 1 to 9.  You may have up to nine files 
      opened at one time.  Any operation (getf, putf, read, write, 
      or close) on an opened file must refer to that file by its 
      tag.  The open instruction must supply three pieces of 
      information: (1) the tag for the file you are opening (integer 
      from 1 to 9), (2) the type of operation you want to perform 
      on the file (integer 1,2,3,4,5,6 or 7), (3) the name of the 
      file you want to open.   Examples: 

         open [1,2] "outfile" 

      This will open a file in the current directory.  The name will 
      be outfile.  The file will be given the tag of 1.   The file 
      may exist already, or it may be new.  The file is opened for 
      new output, specifically putting records sequentially to the 
      file.  

         open [6,4] "bigfile" 

      This will open a file called "bigfile" in the current directory.  
      The file must already exist and must have read permission.  The 
      file will be given the tag of 6.  The file is opened for reading 
      ONLY at random (arbitrary) points in the file.  The size of the 
      blocks read is determined at run time.  

         open [3,5] "bigfile" 

      This will open a file called "bigfile" in the current directory.  
      The file must already exist and must have BOTH read and write 
      permissions.  The file will be given the tag of 3.  The file is 
      opened for reading and/or writing at random (arbitrary) points 
      in the file.  The size of the blocks read or written is determined 
      at run time.  
 
         str filename.80 
 
         filename = "infile" 
         open [5,1] filename 

      This will open a file called "infile" in the current directory.  
      The file must already exist.  The file will be given the tag of 
      5.  The file is opened for getting ASCII records sequentially 
      from the top of the file.  
 
      It sometimes happens that the open command fails.  This can 
      occur for a variety of reasons.  The file might not exist 
      (for reading); permission might be denied (for reading and 
      writing); permission to create a new file (for writing) might 
      be denied; etc.  When this happens, the default behavior is 
      for Zbex to prompt the user for a new (different) file name.  
      This will suspend the operation program until a new name is 
      supplied.  Sometimes, however, the user does not welcome 
      this interruption; if the file or directory can't be opened, 
      the user wants the program to ignor the open request and 
      move on.  This will happen if bit 12 of the zoperation flag 
      is set = 1.  (see Section 18.3 on how to do this using the 
      setflag instruction).   In this case, how does the user's 
      program know that open has failed, and why?  Answer, Zbex 
      sets the special variable err to one of the following values: 
 
           0  =  open was successful 
           1  =  cannot expand to full file name 
           2  =  file already open somewhere else 
           3  =  cannot open the file at all 
           4  =  not a file or a directory 
           5  =  unable to read directory 
           6  =  read access on file is denied 
           7  =  cannot open file for writing 
           3  =  cannot open the file at all 
           8  =  writing allowed only on regular files 
           9  =  read/write access on file is denied 
           10 =  write access on file is denied 
           11 =  cannot create new file 
 
      Program control moves on to the next Zbex instruction, which 
      almost certainly would be to test the value of err.  (See 
      Section 17.3 for more on the err special variable) 

8.5       When you are finished with a file, you should close it.  
      Closing a file frees up the tag for further use.  Example: 
 
          close [5] 
 
      This will close the file whose tag is 5.  
 
8.6       The instructions for getting and putting ASCII records 
      sequentially to a file work the same way as those for getting 
      and putting lines to the screen.  The only extra item of 
      information required is the file tag.  Examples: 
 
      getf  =  get data from the next record in a file.  
 
          getf may stand alone as an instruction, or it may take 
          variable arguments.  If it stands alone, the program will 
          get the next record in a file, but the contents of the 
          record will be ignored.  If getf has arguments (variables), 
          Zbex will attempt to assign values to the variables using 
          the contents of the record.  Examples: 

          int a,b 
          str temp.10 
          real x 

          open [5,1] "<name>" 

          getf [5] a x b   (if next record = "10   2.0   15") 

             a will be assigned the value 10, x the value 2.0, 
             and b the value 15 
 
          getf [5] temp    (if next record = "ABCDEFGHIJKLM") 
 
             temp will be given the value ABCDEFGHIJ, (i.e., the 
             first 10 bytes of the record.  The rest of the record 
             will be ignored.  
 
          getf retrieves records sequentially from a file.  But what 
          happens when there are no more records to retrieve?  In 
          Zbex, trying to retrieve beyond the last record will cause 
          control to jump to the label eofx, where "x" is the tag 
          (digit from 1 to 9) given to the file when it was opened.  
          If the label is not present in the program, the program will 
          terminate when it comes to the end of the file.  In the 
          example above, [5] was the tag assigned to the file when it 
          was opened, and, after getting the last record, control will 
          jump to eof5: if it exists.  

 
      putf  =  assemble the data in the output string into a record 
               and append it to the output file referenced by the tag.  

          putf may stand alone as an instruction, or it contain an 
          output string.  If the output string has variable arguments, 
          they must be preceded immediately the ~ character and 
          followed immediately by a blank.  If the output string ends 
          with three dots (...), the record is assembled but not 
          put to the file (yet).  The data from the next putf statement 
          is append to this record.  Examples: 

          int a,b 
          str temp.10 
          real x 

          a = 10 
          temp = "Hello" 
          putf [1] temp     (adds a record containing the string "temp" 
                               to file [1]) 
          putf [1] ~temp    (adds a record containing the string "Hello" 
                               to file [1]) 
          putf [1] a        (adds a record containing the string "a" 
                               to file [1]) 
          putf [1] ~a       (adds a record containing the string "10" 
                               to file [1]) 
          putf [1] Hello ...  
          putf [1] there.   (adds a record containing the string 
                               "Hello there." to file [1]) 

          The format commands described for putc also work for putf.  

8.7       At this point, you should experiment with opening files 
      for putting and getting records.  Try getting records first.  
      One possible program you might try is this.  

      str file.80, rec.120 

      putc File name?  
      getc file 
 
      open [1,1] file 
 
      getf [1] rec 
      putc ~rec 
      close [1] 
      run 

      Can you guess what this program does?  


8.8       The read and write commands allow you to create and access 
      files which are not organized as a sequence of ASCII records.  
      We call these files binary files, because there is no limitation 
      placed on value of the bytes they contain.  (ASCII bytes normally 
      have values between 32 and 255).  

      read  =  read a block of data from a file into a string 

          The read command requires three pieces of information: 
      (1) the file tag number, (2) the point in the file where you 
      want to start reading, and (3) the string where you want the 
      read data to be placed.  The read starting point (2) may be 
      specified explicitly or may be implied.  This gives rise to 
      two formats, (a) and (b), for the read instruction: 
 
      (a) read [<num>] string           <num> is file tag number, 
                                          start is implied 

      (b) read [<num>,<start>] string   <start> is starting point 

      Note: <start> = 1 indicates start reading at the first byte 

      The number of bytes read is determined by the run-time length 
      of the destination string.  If the run-time length is zero, 
      no bytes are read.  

      Each file that is open for reading has a special variable 
      called the read pointer.   When the file is first opened, 
      Zbex automatically sets this (read pointer) variable to 1.  
      (i.e., pointing at the first byte in the file).  If a read 
      instruction is encountered in the (b) format above, the 
      read pointer is set to the value of <start> before the 
      read takes place.  And after any read takes place, the read 
      pointer is set to the byte (in the file) following the last 
      byte read.  Thus it is possible to used both formats of 
      the read instruction for a file open for reading.  In 
      practice, however, format (a) is best suited for sequential 
      reading applications and format (b) is best suited for random 
      reading.  

      Can you guess what the following program would do?  

      str file.80, rec.120 

      putc File name?  
      getc file 
 
      open [1,5] file 
 
      rec = pad(10)            
      read [1,1] rec 
      putc ~rec 
      close [1] 
      run 
 
      Answer: It will open and try to read the first 10 bytes of 
              the file you specify.  The result is sent to the 
              screen as an ASCII string.  
 
      write  =  write a block of data to a file.  
 
          If a file is open for both read and write (type 5 access), 
      the write instruction requires three pieces of information: 
      (1) the file tag number, (2) the point in the file where you want 
      to start writing (1 = first byte), and (3) the string containing 
      the data you want to write.   If a file is open for write only 
      (type 6 or type 7 access), then the write instruction requires 
      only two pieces of information: (1) the file tag number, and 
      (2) the string containing the data you want to write.  In this 
      case, the data will be appended to whatever has already been 
      written to the file.  Example: 

      str file.80, rec.120 

      putc File name?  
      getc file 
 
      open [1,6] file 
 
      rec = "This is a data string.  " 
      write [1] rec 
      putc ~rec 
      close [1] 
      run 
 
      This program tries to open the file you specify for writing.  
      If the file does not already exist, it is created; if it does 
      exist, it is over-written.  If successful, the file will consist 
      of 26 bytes:  the 24 byte string above, and the line termination 
      sequence CR/LF (ascii-10, ascii-11) 
 
    ────────────────────────────────────────────────────────────────── 

                          ┌─────────────┐ 
                          │ 9. Comments │ 
                          └─────────────┘ 

9.1       You can include comments in your Zbex code.  In Zbex, 
      each statement occupies exactly one line.  The compiler 
      scans the line from left to right.  If it encounters the 
      character pattern /*, it treats everything that follows as 
      a comment.  If a line has a star (*) in column one, or if 
      column one is highlighted with a color, the entire line is 
      treated as a comment.  The latter feature makes it easy to 
      comment out (change code lines to comment lines) whole 
      sections of code.  You simply use the box highlight to 
      highlight column one of all the lines you wish to comment 
      out and then press  F6  to turn that column red.  


    ────────────────────────────────────────────────────────────────── 

                        ┌────────────────┐ 
                        │ 10. Subscripts │ 
                        └────────────────┘ 

10.1      Subscripts occur in two contexts: partial strings and 
      array type variables.  

10.2      All four data types (strings, bit strings, integers, and 
      real numbers) can occur as array type variables.  An array may 
      have from one to eight dimensions.  The size of each dimension 
      must be stated in the declaration statement.  Examples: 
 
         str temp.80(4)        One dimension, size = 4.  Here we have 
                               four string variables, temp(1), temp(2), 
                               temp(3) and temp(4), each with maximum 
                               length of 80.  
                                                            
         bstr bits.32(2,3)     Two dimensions, size1 = 2, size2 = 3.  
                               Here we have six (2 x 3) bit string 
                               variables, bits(1,1), bits(1,2), bits(1,3), 
                               bits(2,1), bits(2,2) and bits(2,3), each 
                               with maximum length of 32 bits.  

         int t(2,3,2)          Three dimensions, size1 = 2, size2 = 3, 
                               size3 = 2.  Here we have twelve (2 x 3 x 2) 
                               integer variables.  

         real x(100)           One dimension, size = 100.  Here we have 
                               one hundred real number variables.  

10.3      When referring to an element of an array variable within 
      an Zbex program, the numbers inside the parentheses (called 
      subscripts) must be integer types, either literals, variables, 
      functions, or some combination of the these (integer expression).  
      Zbex does not allow you to refer to an entire array; neither 
      does it allow you to refer to a single row or a single column 
      in an array.  Examples: 

         int s(4,6), t(4,6), u(4) 

         s = t            (not allowed) 
         s(*,*) = 0       (not allowed) 
         t(*,3) = u(*)    (not allowed) 

      In Zbex, you must refer separately to each element in an array.  
      To set all elements of s in the above example to 0 you would 
      write: 

         loop for i = 1 to 4 
           loop for j = 1 to 6 
             s(i,j) = 0 
           repeat 
         repeat 

10.4      Subscripts can also be used to denote sub-strings within 
      a larger string.  In this case, the subscript(s) appear within 
      curly brackets {}.  Examples: 

         str temp.80 

         temp = "For the last time, Mr. Smee, take the princess home!" 

         temp{4,11}     Start at position 4 and include 11 characters.  
                           " the last t" 
         temp{4..11}    Include all characters from positions 4 to 11.
                           " the las" 
         temp{11}       character at position 11 
                           "s" 
         temp{11..}     All characters from position 11 to the end 
                           "st time, Mr. Smee, take the princess home!" 

10.5      When string subscripts occur in a variable on the left side 
      of an assignment statement, they reference the part of the 
      string that is to be replaced by the right hand side.  Example: 

         str temp.80 

         temp = "For the last time, take the princess home!" 

         temp{18} = ", Mr. Smee,"     This statement will replace the 
                                      comma at position 18 of temp with 
                                      the string ", Mr. Smee,".  The 
                                      result will be: 
 
                "For the last time, Mr. Smee, take the princess home!" 
 

    ────────────────────────────────────────────────────────────────── 

                      ┌─────────────────────┐ 
                      │ 11. Program control │ 
                      └─────────────────────┘ 

11.1      An essential feature of any computer language is the 
      ability to branch based on some condition.  Zbex provides 
      two mechanisms for branching: loops and the if statement.  

11.2      There are three types of loop statements: (1) the simple 
      loop, (2) loop while a relation is true, and (3) loop with a 
      counter.  Every loop statement must be balanced with a repeat 
      statement.  All lines of code between a loop statement and its 
      associated repeat statement are considered to be inside the 
      loop by the compiler.  You may not use a goto statement to 
      jump into the interior of a loop from anywhere outside the 
      loop.  Examples: 
 
         int i,j 
 
         loop                    This is a simple loop with no exit 
           i = i + 1             condition.  The way it is written, 
         repeat                  program control will never leave the 
                                 loop.  We call this an infinite loop.  

         loop for i = 1 to 10    This is a loop with a counter.  It 
           putc ~i               will put the numbers 1 to 10 on the 
         repeat                  screen.  

         j = 5                   This example illustrates an important 
         loop for i = 1 to j     point about Zbex.  When a counting 
           j = j - 1             loop statement is encountered at run 
         repeat                  time, the interpreter evaluates the 
                                 limits and fixes them.  Nothing inside 
                                 the loop can change the limits.  In 
                                 this example, the loop will execute 
                                 5 times, even though the value of j 
                                 is subsequently changed inside the 
                                 loop.  
 
         loop for i = 1 to 9 step 3   The output from the code inside 
           putc ~i                    the loop will be the numbers 
         repeat                       1, 4, and 7.  The last putc 
         putc ~i                      statement will put out the 
                                      number 10.  The repeat statement 
                                      causes the counter (i) to be 
                                      incremented and then tested.  
                                      If the test fails,control passes 
                                      to the statement beyond the 
                                      repeat.  
         i = 0 
         loop while i < 5        This loop will execute five times.  
           i = i + 1             Upon exit, the value of i will be 5 
         repeat 

         i = 0 
         j = 10 
         loop for i = 1 to j     The output from this code will be 
           i = i + 1             the numbers 2, 4 and 6.  Note that 
           putc ~i               the value of the counter can be 
         repeat while i < 5      changed inside the loop.  Also note 
                                 that the exit condition in this 
                                 case is provided by the repeat 
                                 statement, not the loop statement.  
                                 Had j been set to 4 instead of 10, 
                                 the output would have been the 
                                 numbers 2 and 4, and the exit 
                                 condition would have come from the 
                                 loop statement.  
 

11.3      The if statement in Zbex is part of an if-else-end grouping.  
      The else is optional.  Zbex uses the if statement to test a 
      simple condition.  If the condition is true, control passes to 
      next statement in line.  If the condition is false and there is 
      an else statement in the group, control passes to the statement 
      following the else statement; otherwise control passes to the 
      statement following the associated end statement.  Examples: 

         int i 
         loop for i = 1 to 5      The output from this code will 
           if i > 3               be the numbers 0, 0, 0, 4 and 5.  
             putc ~i 
           else 
             putc 0 
           end 
         repeat 
 
         loop for i = 1 to 5                The output from this code 
           if (i > 2 and i < 5) or i = 1    will be the numbers 1, 3 
             putc ~i                        and 4.  Observe the use of 
           end                              and and or in this example.  
         repeat                             These are called boolean 
                                            operators and can occur only 
                                            in if and while statements.  
                                            Also observe the use of 
                                            parentheses to establish 
                                            the order of the boolean 
                                            operations.  

11.4      The normal way to transfer program control to a different 
      part of a program is with the goto statement.  The goto 
      statement takes a label as an argument.  You may not use a 
      goto statement to jump into a loop or to jump into an if-else-end 
      group.  You may use goto to jump around inside a loop or to jump 
      out of a loop (as long as you are not jumping into another loop).  
      Examples: 

         int i 

         i = 1                   This code does precisely the same thing 
      AA:                        as the loop code below.  Notice that 
         if i <= 5               the label AA must begin in column one 
           putc ~i               and must be followed by a colon (:).  
           i = i + 1 
           goto AA 
         end 

         loop for i = 1 to 5 
           putc ~i 
         repeat 


11.5      Zbex does not have case statements (these are a feature of 
      both the Pascel and the C programming languages).  But Zbex 
      does provide a means for jumping to different points in a program 
      depending on the value of an integer.  To do this, we have 
      implemented a variable type called a label.  All label variables 
      must be declared using the label declaration statement.  All 
      labels must be one dimensional arrays.  To illustrate how label 
      variables work, we show two pieces of code, each doing the same 
      thing.  

         int i                            int i 
         loop for i = 1 to 5              label A(5) 
           if i = 1                       loop for i = 1 to 5 
             putc Eleanor                   goto A(i) 
           else                       A(1): putc Eleanor 
             if i = 2                       goto B 
               putc Walter            A(2): putc Walter 
             else                           goto B 
               if i = 3               A(3): putc Jim 
                 putc Jim                   goto B 
               else                   A(4): putc Bill 
                 if i = 4                   goto B 
                   putc Bill          A(5): putc Mary 
                 else                       goto B 
                   putc Mary          B:  repeat 
                 end 
               end 
             end 
           end 
         repeat 
 
      The second piece of code is not only more compact, it is much 
      faster because the test which determines the name to put out 
      for each value of i happens only once, whereas in the first 
      piece of code, four tests must be made before the names, Bill 
      and Mary, can be put out.

11.6      Label variables also have the feature that they can 
      identify ranges of an integer for which certain actions should 
      be taken.  The two pieces of code below produce the same effect.  

           int i                           int i 
           label A(5)                      label A(5) 
           loop for i = 1 to 5             loop for i = 1 to 5 
             goto A(i)                       goto A(i) 
      A(1):  putc Girl                A(1):  putc Girl 
             goto B                          goto B 
      A(2):                           A(2):  putc Boy 
      A(3):                                  goto B 
      A(4):  putc Boy                 A(5):  putc Girl 
             goto B                          goto B 
      A(5):  putc Girl                B:   repeat 
             goto B 
      B:   repeat 

      In the second piece of code, the labels A(3) and A(4) don't 
      exist, so the program jumps to the label A(2).  The rule is 
      that if a label doesn't exist, jump to the next lowest one 
      that does.  


    ────────────────────────────────────────────────────────────────── 

                      ┌───────────────────┐ 
                      │   12. Relations   │ 
                      └───────────────────┘ 

12.1      A relation is used to describe a test condition.   Examples 
      of relations are:  x < y, x = y, x > y.  The condition described 
      by a relation is either true or false, depending on the values 
      of the variables at run time.  
 
          A relation has the following structure: 
 
                  <entity> <relational operator> <entity> 
 
      The <entity> may be a variable, a literal, a non-string 
      function, a non-string expression, or (right hand side) 
      a set of characters (numbers between 0 and 255).  The 
      entities on each side of a relation must be of the same 
      variable type.  
 
      Strings 

12.2      For strings there are eight relational operators: 
   
           =     equals 
           >     greater than 
           <     less than 
           <>    not equal 
           >=    greater than or equal to 
           <=    less than or equal to 
           in    entirely contained in (a set) 
           con   contains (string) or containing a member of (set) 

12.3      The first six operators evaluate the two strings of a 
      relation, byte by byte, treating each byte arithmetically.  
      If a difference is found, a definitive result can be given 
      for operators two through six.  Only if all bytes are found 
      to be the same, and the lengths also the same, are the 
      strings determined to be equal.  If the lengths are unequal, 
      but the strings are identical up to the length of the shorter 
      string, the longer string is determined to be greater than 
      the shorter string.  

12.4      The "in" operator requires that the right hand side be a 
      set.  The left hand side must be a string or a substring.  The 
      relation is evaluated as true if and only if all characters in 
      the string or substring are members of the set.  If the left 
      hand side is a single character string (string with length 
      one), the relation is true if that character is a member of 
      the set.  

12.5      The "con" operator may have a string, a substring, or set 
      on the right hand side.  The left hand side may be a string or 
      a substring.  If the right hand side is a string or substring, 
      the relation is true if the left hand string is equal to or 
      contains the string or substring on the right.  If the right 
      hand side is a set, the relation is true if any byte in the 
      left hand entity is in the set. 

12.6      The con operator has one other property.  Zbex has five 
      special integer variables, trp, sze, rem, mpt, and sub, which 
      are set by various runtime operations.   Two of them, mpt (match 
      point) and sub (subscript), are given new values when the "con" 
      relation is true.  The value assigned to mpt is the position in 
      the left hand string or substring where the truthful result was 
      first detected.  The value assinged to sub is the subscript in 
      the left hand string variable where the truthful result was 
      first detected.  The difference between "position" (value of 
      mpt) and "subscript" (value of sub) is illustrated by the 
      following example:

          str test.5 

          test = "abcde" 
          if test{3..5} con "e" 
            putc mpt = ~mpt   sub = ~sub 
          end 
 
      In this example, the "con" relation is true and mpt is set to 
      3, the point in the sub-string "cde" where the "e" was found.  
      sub is set to 5, which is the subscript in the variable test 
      "abcde" where the "e" occurs.

12.7      In this section, reference was made to an entity called a 
      set.  A set is sub-set of the numbers 0 through 255, i.e., 
      the possible values of a byte.  For this reason, we also 
      refer to a set as a character set.  Below are some examples 
      of sets.  

         1.  ['a'..'z']                 the small alphabet 
         2.  ['A'..'Z','a'..'z']        the large and small alphabet 
         3.  ['0'..'9']                 the digits 
         4.  [48..57]                   the values 48 to 57, which 
                                        are the ASCII values for 
                                        the numbers 0 to 9.  Sets 
                                        3. and 4. are the same.  
         5.  [32]                       the value 32.   
         6.  [' ']                      the space character, ASCII 32.  
                                        Sets 5. and 6. are the same.  
         7.  ['A'..'z']                 all values between 'A' = 65 
                                        and 'z' = 122.  
 
      If you don't know or can't remember the ASCII values for the 
      various characters, don't worry about it.  Few people have 
      reason to remember these values.  When I want to be reminded 
      of the ASCII values for various characters, I just write and 
      run the following program: 
 
           str a.1 
           int i 
           loop 
             getc a 
             i = ors(a) 
             putc ~a  = ASCII ~i 
           repeat 
           run 

      The expression ors(a) is a function which converts the string 
      a to an integer.  We will discuss functions in the next section.  
 

      Bit strings, Integers, Real numbers 

12.8      For the non-string data types there are six relational 
      operators.  
 
           =     equals 
           >     greater than 
           <     less than 
           <>    not equal 
           >=    greater than or equal to 
           <=    less than or equal to 

      For bit strings, these operators behave in the same manner as 
      with strings.  For the numerical data types, these operators 
      behave in the usual manner.  The operators are sign sensitive, 
      i.e., a negative number closer to zero is larger than a 
      negative number farther from zero.  


    ────────────────────────────────────────────────────────────────── 

                      ┌───────────────────┐ 
                      │   13. Functions   │ 
                      └───────────────────┘ 

13.1      The Zbex language includes a number of functions.  The 
      purpose of functions is to facilitate the writing of Zbex 
      programs and to increase the speed of these programs.  Functions 
      can be used in assignment statements any place a normal variable 
      can be used.  Functions are classified by the data type of their 
      output.  Non-string and non-bit string functions can appear 
      in relations.  

      Functions whose output is an integer 

      Function        Output 
      ──────────      ──────────────────────────────── 
      rnd(int)        deliver a random number between 0 and <input> 
      not(int)        ones complement of <input> 
      abs(int)        absolute value of <input> 

      and(int,int)    for input (x,y), output = x & y 
      ior(int,int)    for input (x,y), output = x | y 
      xor(int,int)    for input (x,y), output = x exclusive or y 
      bit(int,int)    for input (x,y), output = bit x of y (0 is low order) 
      shr(int,int)    for input (x,y), output = shift x right y bits 
      shl(int,int)    for input (x,y), output = shift x left y bits 
 
      tst(table)      output = number of entries in the table 
                        tst also resets the sequential tget 
      tdx(table,str)  output = index number for key (second input) 
                        or 0 if the given key is not present 

      fix(real)       integer value of real number input 

      len(str)        length of input string 
      int(str)        integer value of string of digits, including 
                        the unitary plus or minus sign.  The function 
                        skips leading blanks and also sets the sub 
                        variable to subscript of terminating (non digit) 
                        byte or to len(str) + 1 
      ors(str)        integer value of bit pattern in first 4 (or less) 
                        bytes of the string input 
 
      len(bstr)       byte length of padded bit string input 
      bln(bstr)       bit length of bit string input 

 
      Functions whose output is a real number 
 
      Function        Output 
      ──────────      ──────────────────────────────── 
      flt(int)        convert integer value to real number 
      rnd(real)       deliver a random number between 0 and <input> 
      abs(real)       absolute value of <input> 
      dec(real)       decimal part of <input> 
      sin(real)       for input (x), output = sin(x) 
      cos(real)       for input (x), output = cos(x) 
      tan(real)       for input (x), output = tan(x) 
      ars(real)       for input (x), output = arcsin(x) 
      arc(real)       for input (x), output = arccos(x) 
      art(real)       for input (x), output = arctan(x) 
      exx(real)       for input (x), output = (e to the x power) 
      lnx(real)       for input (x), output = (log base e of x) 
      sqt(real)       for input (x), output = square root of x 
 
      pow(real,real)  for input (x,y), output = x to the y power 
                                (x,y > 0) 
      flt(str)        real value of string of digits, including the 
                        unitary plus or minus sign.  The function 
                        skips leading blanks and also sets the sub 
                        variable to subscript of terminating (non digit) 
                        byte or to len(str) + 1 
 

      Functions whose output is a string 

      Function        Output 
      ──────────      ──────────────────────────────── 
      pad(int)        add blanks to the end of the current right hand 
                        string, up to a length of <input> number.  
      zpd(int)        add nulls (the zero byte) to the end of the 
                        current right hand string, up to a length of 
                        <input> number.  
      chr(int)        single string character with a bit pattern 
                        that matches the lower eight bits of <input> 
      ch2(int)        two character string with a bit pattern that 
                        matches the lower sixteen bits of <input> 
      ch4(int)        four character string with a bit pattern that 
                        matches the <input> 
      oct(int)        ASCII octal representation of <input> 
      chs(int)        ASCII decimal representation of <input> 
                        including the unitary minus sign 
      hex(int)        ASCII hexidecimal representation of <input> 
      ch8(real)       eight character string with a bit pattern that 
                        matches the <input> 
      chs(real,int)   ASCII decimal representation of <real input> with 
                        <int input> digits to right of decimal.  Includes 
                        unitary minus sign 
      chx(real,int)   ASCII floating point representation of <real input> 
                        with <int input> digits to right of decimal.  
                        Includes unitary minus sign 

      trm(str)        input string without the trailing blanks
      mrt(str)        input string without the leading blanks 
      lcs(str)        input string with all letters converted to lower case 
      ucs(str)        input string with all letters converted to upper case 
      rev(str)        string with characters in the reverse order from 
                        the input string
 
      rpl(str,s(n,2)) create an output string from the first input string 
                        in following way:  Working from left to right in 
                        the input string, search the string array s(i,1) 
                        (i = 1,...,n) for a match in the input string.  
                        If a match is found (at i), place s(i,2) in the 
                        output string and advance the test point in the 
                        input string by len(s(i,1)).  If no match is found, 
                        copy the current byte to the output string and 
                        advance the test point one byte.  
 
      dup(str,int)    input string duplicated <int input> number of times 
 
      txt(s1,bs,int)  output string = s1{m..n-1} where m (>= int) is the 
                        subscript of first byte of the input string (at 
                        or beyond <int input>) which is not in the bit 
                        string set bs, and n (> m) is the subscript of 
                        the first byte of the input string (beyond m) 
                        which is in the bit string set bs.  <int input> 
                        is set to the value n by this function.  
                        Essentially, characters in the bit string set 
                        bs are used to sub-divide or parse the input 
                        string.  The function is designed for repeated 
                        calls until the input string is completely 
                        parsed.  

                        The format for specifying the bit string set bs 
                        is: set(bstr) where bstr is a bit string.  
 
      txt(s1,bs)      same as previous function, except the special       
                        variable mpt is used in place of the third input.  
                                                               
      txt(s1,[ ],int) same as the first txt function, except the set [ ] 
                        is used instead of the bit string set.  
 
      txt(s1,[ ])     same as the second txt function, except the set [ ] 
                        is used instead of the bit string set.  

      cby(bstr)       create output string with the same bit pattern as 
                        the input bit string (padded to the byte boundary 
                        with zeros) 

      upk(bstr,s2)    construct a string from the first <input> in the 
                        following way: 1 maps to s2{1} or 'x', 0 maps to 
                        s2{2} or ' '.  
      upk(bstr)       construct a string from the first <input> in the 
                        following way: 1 maps to 'x', 0 maps ' '.  

 
      Functions whose output is a bit string 
 
      Function        Output 
      ──────────      ──────────────────────────────── 
      npd(int)        add ones to the end of the current right hand 
                        bit-string, up to a length of <input> number.  
      zpd(int)        add zeros to the end of the current right hand 
                        bit-string, up to a length of <input> number.  
      cbi(str)        create output bit string with the same bit pattern 
                        as the input string

      pak(s1,s2)      construct a bit string from the first input 
                        string as follows: for each byte, if byte = s2{1} 
                        put in a one, otherwise put in a zero.  
      pak(str)        construct a bit string from the input string as 
                        follows: for each byte, if byte = 'x', put in 
                        a one, otherwise put in a zero.  
 
      trm(bstr)       input bit string without the trailing zeros  
      mrt(bstr)       input bit string without the leading zeros 
      rev(bstr)       bit string with bits in the reverse order from 
                        the input bit string 
      cmp(bstr)       1's complement of input bit string 
 
      dup(bstr,int)   input bit string duplicated <int input> number 
                        of times 

      bnd(bstr,bstr)  output = intersection of two inputs 
      bor(bstr,bstr)  output = union of two inputs 
 

      Function whose output is a set 

      Function        Output 
      ──────────      ──────────────────────────────── 
      set(bstr)       create a set in the following way:  Pad the 
                        input bit string with zeros up to a length 
                        of 256.  For each of the 256 bits in the 
                        bit string, if the bit is a one, add the 
                        number of the position to the set; if the 
                        bit is a zero, do not add the number of 
                        the position to the set.  


13.2      There are thirteen sets of ambiguous functions.  In each 
      case, there is a way for the compiler to distinguish which  
      function is being called.  

          1.   rnd(int)         --> int    distinguished by 
               rnd(real)        --> real   output type 
 
          2.   abs(int)         --> int    distinguished by 
               abs(real)        --> real   output type 

          3.   len(str)         --> int    distinguished by 
               len(bstr)        --> int    input variable type.  
 
          4.   flt(int)         --> real   distinguished by 
               flt(str)         --> real   input variable type.  

          5.   zpd(int)         --> str    distinguish by 
               zpd(int)         --> bstr   output type 
 
          6.   chs(int)         --> str    distinguish by number 
               chs(real,int)    --> str    of arguments 

          7.   trm(str)         --> str    distinguish by 
               trm(bstr)        --> bstr   output type 
 
          8.   mrt(str)         --> str    distinguish by 
               mrt(bstr)        --> bstr   output type 

          9.   rev(str)         --> str    distinguish by 
               rev(bstr)        --> bstr   output type 
 
          10.  dup(str)         --> str    distinguish by 
               dup(bstr)        --> bstr   output type 

          11.  txt(s1,bs,int)   --> str    distinguish by type 
               txt(s1,bs)       --> str    of second argument 
               txt(s1,[ ],int)  --> str    and by presence 
               txt(s1,[ ])      --> str    of third argument 
 
          12.  upk(bstr,s2)     --> str    distinguish by number 
               upk(bstr)        --> str    of arguments 

          13.  upk(s1,s2)       --> str    distinguish by number 
               upk(str)         --> str    of arguments 
 

13.3      We include some examples of how various functions work.  The 
      focus is mainly on those functions whose operation may not be       
      clear from the description.  

      1. The tst(table) function.  This function reports on the number 
         of enteries in the table, and resets the counter for the 
         sequentail version of the tget command.  To demonstrate this 
         function, we must fill a table with some items and use the 
         sequential version of tget to retrieve them.  

               Program 
         ═════════════════════ 
             str a.10,b.10 
             int i 
             table X(1000) 

             loop for i = 1 to 10      /* create 10 entries in table 
               a = "Key " // chs(i)    /* str a will contain the "key" 
               tput [X,a] Item ~i 
             repeat 
 
             loop for i = 1 to 3       /* get first 3 entries 
               tget [X] a b 
               putc ~a   ~b 
             repeat 
             i = tst(X) 
             putc Number of entries = ~i 
             loop for i = 1 to 3       /* check to see that sequential 
               tget [X] a b            /* counter has been reset 
               putc ~a   ~b 
             repeat 
             run 
 
                      Execution 
         ═════════════════════════════════════ 
             ** S=18, P=79, L=253, M=1418 ** 
             Key 1  Item 1 
             Key 2  Item 2 
             Key 3  Item 3 
             Number of entries = 10 
             Key 1  Item 1 
             Key 2  Item 2 
             Key 3  Item 3 
             Ready for program 
 

      2. The tdx(table,str) function.  This function provides the 
         index number in a table for a given "key".  The number is 
         0 if there is no such key in the table.  To demonstrate this 
         function, we must fill a table with some key/record pairs 
         and then use the tdx function to get their index numbers.  

               Program 
         ═════════════════════ 
             str a.10,b.10 
             int i,j 
             table X(1000) 

             loop for i = 1 to 10      /* create 10 entries in table 
               a = "Key " // chs(i)    /* str a will contain the "key" 
               tput [X,a] Item ~i 
             repeat 
 
             loop for i = 1 to 3       /* get index for first 3 entries 
               a = "Key " // chs(i) 
               j = tdx(X,a) 
               putc key = ~a   index = .w4 ~j   ...  
               tget [X,j] b            /* retrieve record via index 
               putc record = ~b 
             repeat 
             run 

                      Execution 
         ═════════════════════════════════════ 
         ** S=15, P=75, L=255, M=1418 ** 
         key = Key 1  index =  313  record = Item 1 
         key = Key 2  index =   58  record = Item 2 
         key = Key 3  index =  187  record = Item 3 
         Ready for program 

 
      3. The fix(real) function.  The reason we need this function 
         is that Zbex does not automatically convert real numbers 
         to integers.  
 
               Program 
         ═════════════════════ 
             int i 
             real x 

             x = 456.789 
             i = fix(x) 
             putc real input = ~x   integer output = ~i 
             run 
 
                      Execution 
         ═════════════════════════════════════ 
         ** S=6, P=28, L=236, M=410 ** 
         real input = 456.79  integer output = 456 
         Ready for program 
 

      4. The len(str) function.  When used on the right hand side 
         of an assignment statement, this function gives the length 
         of the string argument.  What makes this function unusual 
         is that it can also be used on the left hand side of an 
         assignment statement to set the length of a string.  This 
         is particularly valuable when we use a string as a buffer 
         for the read instruction, since the size of the block read 
         = the length of the string.  
 
               Program 
         ═════════════════════ 
             str a.80 
             int i 

             a = "For the last time, Mr. Smee, take the princess home!" 
             i = len(a) 
             putc Length of string a is ~i 
             len(a) = 28 
             i = len(a) 
             putc New length of string a is ~i 
             putc String a = "~a " 
             run 
 
                      Execution 
         ═════════════════════════════════════ 
         ** S=10, P=59, L=255, M=431 ** 
         Length of string a is 52 
         New length of string a is 28 
         String a = "For the last time, Mr. Smee," 
         Ready for program 
 

      5. The int(str) function.  This function reads a group of digits 
         in a string and returns their value as an integer.  The 
         setting of the special variable sub to the subscript of the 
         the terminating (non digit) byte in the string allows this 
         function to be called again to get the next number in the 
         string.  
 
               Program 
         ═════════════════════ 
             str a.80 
             int i 

             a = "1000  500      -39   465 678687" 
             putc Getting numbers from a string 
             putc a = "~a " 
             putc Numbers 
             putc ─────── 
             sub = 0 
             loop while sub < len(a) 
               i = int(a{sub+1..}) 
               putc .w7 ~i .w2t20  (terminating subscript = ~sub ) 
             repeat 
             run 

                      Execution 
         ═════════════════════════════════════ 
         ** S=13, P=72, L=248, M=431 ** 
         Getting numbers from a string 
         a = "1000  500      -39   465 678687" 
         Numbers 
         ─────── 
            1000             (terminating subscript =  5) 
             500             (terminating subscript = 10) 
             -39             (terminating subscript = 19) 
             465             (terminating subscript = 25) 
          678687             (terminating subscript = 32) 
         Ready for program 

 
      6. The ors(str) function.  This function reads the first four 
         bytes of the string argument (less if the string is shorter) 
         and creates an integer value from the bit pattern.  
 
               Program 
         ═════════════════════ 
             str a.4 
             int i 

             a = "." 
             i = ors(a) 
             putc a = "~a " .t13 i = ~i .t30x (hex ~i ) 
             a = ".1" 
             i = ors(a) 
             putc a = "~a " .t13 i = ~i .t30x (hex ~i ) 
             a = ".123" 
             i = ors(a) 
             putc a = "~a " .t13 i = ~i .t30x (hex ~i ) 
             run 
 
                      Execution 
         ═════════════════════════════════════ 
         ** S=12, P=95, L=249, M=412 ** 
         a = "."     i = 46           (hex 2e) 
         a = ".1"    i = 11825        (hex 2e31) 
         a = ".123"  i = 774976051    (hex 2e313233) 
         Ready for program 
 

      7. The pad(int) function.  This function adds blanks to the end 
         of the string on the right hand side of an assignment statement 
         up to the length specified by the integer argument.  If the 
         string is already longer than the specified length, nothing 
         is done.  

               Program 
         ═════════════════════ 
             str a.80,b.80 
             int i 
 
             a = "This is a short string" 
             b = "This is a slightly longgggerrrrrr string" 
             putc a = "~a " 
             a = a // pad(30) 
             putc a = "~a " 
             putc b = "~b " 
             b = b // pad(30) 
             putc b = "~b " 
             run 
 
                      Execution 
         ═════════════════════════════════════ 
         ** S=11, P=54, L=262, M=452 ** 
         a = "This is a short string" 
         a = "This is a short string        " 
         b = "This is a slightly longgggerrrrrr string" 
         b = "This is a slightly longgggerrrrrr string" 
         Ready for program 

 
      8. The ch4(int) function.  This function is the opposite of the 
         ors function.  It creates a four byte string from the bit 
         pattern of the integer argument.  
 
               Program 
         ═════════════════════ 
             str a.4 
             int i 

             i = 0x31323334        /* this is hexidecimal notation 
             a = ch4(i) 
             putc a = "~a " 
             run 
 
                      Execution 
         ═════════════════════════════════════ 
         ** S=6, P=19, L=237, M=412 ** 
         a = "1234" 
         Ready for program 
 

      9. The chs(int) function.  This function constructs a string 
         which is the ASCII decimal representation of the integer 
         argument.  We illustrate also the hex(int) function here.  

               Program 
         ═════════════════════ 
             str a.80,b.80 
             int i 
 
             a = "" 
             b = "" 
             loop for i = 500 to -300 step -100 
               a = a // chs(i) // "  " 
               b = b // hex(i) // "  " 
             repeat 
             putc a = "~a " 
             putc b = "~b " 
             run 
 
                      Execution 
         ═════════════════════════════════════ 
         ** S=11, P=55, L=247, M=452 ** 
         a = "500  400  300  200  100  0  -100  -200  -300  " 
         b = "1f4  190  12c  c8  64  0  ffffff9c  ffffff38  fffffed4  " 
         Ready for program 

 
     10. The rpl(str,s(n,2)) function.  This somewhat unwieldy function 
         is intended to facilitate the translation of certain string 
         patterns into alternate string patterns for large blocks 
         of text at a time.  rpl is short for replace.  The second 
         argument is an array of patterns and their replacements.  
 
               Program 
         ═════════════════════ 
             str a.500, b.500, c.10(1,2) 
 
            set up initial string for testing 
 
             a = "The purpose of this function is to facilitateCRthe" 
             a = a // " translation of certain string patterns intoCRother" 
             a = a // " string patterns.  In this example, we willCRchange" 
             a = a // " a code for carriage return into the escapeCR" 
             a = a // "sequences which will generate real carriageCR" 
             a = a // "returns.CR" 

            set up conversion matrix 

             c(1,1) = "CR" 
             c(1,2) = chr(27) // "S" // chr(27) // "Y" 
 
            do the translation 
 
             b = rpl(a,c) 
 
            display results 
 
             putc ~b 
             run 

                      Execution 
         ═════════════════════════════════════ 
         The purpose of this function is to facilitate 
         the translation of certain string patterns into 
         other string patterns.  In this example, we will 
         change a code for carriage return into the escape 
         sequences which will generate real carriage 
         returns.  
 
         Ready for program 
 

     11. The txt(s1,bs,int) function.  This is the most complicated of 
         the four forms of the txt function.  The purpose of this 
         function is to allow the rapid parsing of strings, based on 
         a set of bytes one might call "delimiter bytes."  In this 
         version of the function, the set is determined by a bit 
         string.  
 
               Program 
         ═════════════════════ 
             str a.100, b.100 
             bstr pset.256 
             int i 
 
             a = "Let us, please, ignor (i.e., disregard) all" 
             a = a // " non-alpha characters in our parse!!  O.K.?" 

             pset = npd(65) // zpd(91) // npd(97) // zpd(123) // npd(256) 

             i = 0 
             loop while i < len(a) 
               b = txt(a, set(pset), i) 
               b = txt(a, [0..64,91..96,122..255], i)  will also work 
               if b <> "" 
                 putc ~b 
               end 
             repeat 
             run 

                      Execution 
         ═════════════════════════════════════ 
         ** S=14, P=52, L=283, M=471 ** 
         Let 
         us 
         please 
         ignor 
         i 
         e 
         disregard 
         all 
         non 
         alpha 
         characters 
         in 
         our 
         parse 
         O 
         K 
         Ready for program 
 

     12. The upk(bstr,s2) function.  This function creates a string 
         with two types of characters.  The pattern matches that of 
         the bit string argument.  

               Program 
         ═════════════════════ 
             str a.50,b.2 
             bstr pattern.50(10) 
             int i 

             b = "▒█" 

             pattern(1) = "0001111111000" 
             pattern(2) = "0111111111110" 
             pattern(3) = "1110000000111" 
             pattern(4) = "1100110110011" 
             pattern(5) = "1100001000011" 
             pattern(6) = "1100100010011" 
             pattern(7) = "0110011100110" 
             pattern(8) = "0111000001110" 
             pattern(9) = "0001111111000" 

             loop for i = 1 to 9 
               a = upk(pattern(i), b) 
               putc ~a 
             repeat 
             run 

                      Execution 
         ═════════════════════════════════════ 
         ** S=18, P=90, L=267, M=456 ** 
         ███▒▒▒▒▒▒▒███ 
         █▒▒▒▒▒▒▒▒▒▒▒█ 
         ▒▒▒███████▒▒▒ 
         ▒▒██▒▒█▒▒██▒▒ 
         ▒▒████▒████▒▒ 
         ▒▒██▒███▒██▒▒ 
         █▒▒██▒▒▒██▒▒█ 
         █▒▒▒█████▒▒▒█ 
         ███▒▒▒▒▒▒▒███ 
         Ready for program 


    ────────────────────────────────────────────────────────────────── 

                      ┌───────────────────┐ 
                      │   14. Procedures  │ 
                      └───────────────────┘ 

14.1      A procedure is a section of code which can be entered 
      (called) from different points in a program and which, when 
      completed, will return control to the calling point.  
      Procedures may call other procedures, but Zbex is not designed 
      for recursive procedure calls (that is when a procedure calls 
      itself).  The codes for procedures must come after the main 
      program.  

14.2      A procedure is identified by the procedure statement.  
      A procedure is called using the perform statement.  Return 
      from a procedure is accomplished with the return statement.  
      Example:  
 
               Program 
         ═════════════════════ 
         int i,j   
 
         loop for i = 1 to 5 
           perform square 
           putc ~i  ~j 
         repeat 
         stop 
 
         procedure square 
           j = i * i 
         return 
         run 

                      Execution 
         ═════════════════════════════════════ 
         ** S=10, P=29, L=235, M=410 ** 
         1 1 
         2 4 
         3 9 
         4 16 
         5 25 
         Ready for program 

14.3      It is possible to declare variables inside a procedure.  
      Variables so declared can be referenced only within the 
      procedure.  If a variable of the same name was declared in 
      the main program, it will be masked (unaccessable) inside 
      the procedure.  Example, using the variable i: 

               Program 
         ═════════════════════ 
         int i,j,k 

         loop for i = 1 to 5 
           j = i 
           perform factorial 
           putc ~i  ~k 
         repeat 
         stop 
 
         procedure factorial 
           int i
           k = 1 
           loop for i = 1 to j 
             k = k * i 
           repeat 
         return 
         run 

                      Execution 
         ═════════════════════════════════════ 
         ** S=15, P=41, L=239, M=410 ** 
         1 1 
         2 2 
         3 6 
         4 24 
         5 120 
         Ready for program 

14.4      It is possible to pass data to a procedure and for a procedure 
      to pass data back to the calling point.  The buffers for this must 
      be included in the procedure statement, and the data types for the 
      buffers must be declared inside the procedure.  Values must be 
      loaded into the buffer at the time the procedure is called.  The 
      actual transfer of data is handled by the getvalue and passback 
      statements inside the procedure.  Example: 

               Program 
         ═════════════════════ 
         int i,j 

         loop for i = 6 to 10 
           perform factorial (i,j) 
           putc .w2 ~i  .t4w8 ~j 
         repeat 
         stop 

         procedure factorial (a,b) 
           int a,b,i 
           getvalue a 
           b = 1 
           loop for i = 1 to a 
             b = b * i 
           repeat 
           passback b 
         return 
         run 
 
                      Execution 
         ═════════════════════════════════════ 
         ** S=16, P=53, L=241, M=410 ** 
          6      720 
          7     5040 
          8    40320 
          9   362880 
         10  3628800 
         Ready for program 

14.5      There are times when you will want to return directly to a 
      point in the main program, irrespective of how many levels of 
      procedure calls you have made.  An example of this would be a 
      language compiler.  If you are processing a line of text and a 
      procedure within a procedure within a procedure identifies a 
      syntax error, you would not want to trace your way back to the 
      main program through these procedures; instead, you would want 
      to jump directly to the error handling portion of the program 
      with a flag telling that portion what went wrong.  Zbex 
      provides you a way to do this using the special trp variable 
      and the special trap label.  If you include an integer number 
      with a return statement, the record of procedure calls will 
      be erased and control will pass directly to the trap label 
      in the main program.  The special integer variable trp will 
      be assigned the value attached to the return statement.  
      Example: 
 
               Program 
         ═════════════════════ 
            int i 
 
            trp = 0 
         trap: 
            i = 0 
            perform pro1 
            stop 
 
            procedure pro1 
              putc pro1  ...  
              i = i + 1 
              if i > trp 
                putc 
                return 1 
              end 
              perform pro2 
              putc returning from pro1 
            return 

            procedure pro2 
              putc pro2  ...  
              i = i + 1 
              if i > trp 
                putc 
                return 2 
              end 
              perform pro3 
              putc returning from pro2 
            return 
 
            procedure pro3 
              putc pro3 
              putc returning from pro3 
            return 
            run 
 
                      Execution 
         ═════════════════════════════════════ 
         ** S=31, P=83, L=233, M=410 ** 
         pro1 
         pro1  pro2 
         pro1  pro2  pro3 
         returning from pro3 
         returning from pro2 
         returning from pro1 
         Ready for program 

14.6      There are only two ways to interrupt an Zbex program once it 
      has been started.  One of these is by pressing the <ctrl> Break 
      key (the other is by responding to a getc with two exclaimation 
      points (!!)).  On occasion you may want <ctrl> Break to actually 
      do something in your Zbex program.  For example, if your program 
      is writing to the screen, and you want it to stop, you must push 
      <ctrl> Break.  In some cases, you may not want your Zbex program 
      to halt, you may simply want to get control of it.  Zbex provides 
      you a means for doing this.  It is a special procedure called 
      break.  If you include a procedure with this name, control will 
      automatically pass to it when you press the <ctrl> Break key.  A 
      normal return from this procedure will put you back into your 
      Zbex program exactly at the point where <ctrl> Break was 
      pressed.  Example: 
 
               Program 
         ═════════════════════ 
         int i,j 
 
         loop for i = 1 to 100000 
           loop for j = 1 to 300000    /* (for a delay) 
           repeat 
           putc ~i 
         repeat 
         stop 

         procedure break 
           getc 
         return 
         run 
 
                      Execution 
         ═════════════════════════════════════ 
         ** S=11, P=26, L=235, M=410 ** 
         1 
         2 
         3 
                 (here, <ctrl> Break was pressed and a blank line entered) 
         4 
         5 
         !!      (here, <ctrl> Break was pressed and !! was entered) 
         Ready for program 

14.7      There is an important precaution you must observe with 
      the procedure break.  You need to provide some mechanism for 
      terminating the program inside the procedure, or else you 
      may not be able to terminate your program at all (except by 
      exiting Dmuse)!   The program below, once started, cannot 
      be stopped, except by exiting Dmuse.  
 
         int i,j 
 
         loop 
           i = i + 1 
           putc ~i 
         repeat 
         stop 
 
         procedure break 
         return 
         run 


    ────────────────────────────────────────────────────────────────── 

                      ┌───────────────────┐ 
                      │     15. Tables    │ 
                      └───────────────────┘ 

15.1      Zbex provides you with a special variable type called a 
      table.  A table is designed to hold records, much like a file.  
      You may put records into a table using an index number or 
      using string called a key.  You may retrieve records from the 
      table the same way.  There is also a way to access sequentially 
      all records and their keys.  Tables must be declared using the 
      table declaration statement.  All tables msut be declared as 
      one dimensional arrays.  The size should about 50% larger than 
      you think you need.  This is because records stored and 
      retrieve with a key use the hash addressing technique.  Records 
      stored in a table may be up to 1000 bytes long.  You may 
      retrieve a record, increase its size, and put it back in the 
      table without worrying about writing over some other record.  
      Memory for records in a table is allocated dynamically at
      run time.  

          Tables are useful in many applications.  If you want to 
      process records at random out of a file, the best way to do 
      this is to read the entire file into a table.  If you want 
      to count words from a large document to determine frequency 
      ratios, the best way to do this is to use the words as keys 
      and store the counts as records.  Any application that calls 
      for random access either by index or by key will be well served 
      by the use of tables.  

15.2      There are three instructions that address tables: tget, 
      tput, and treset.  
 
15.3  1. tget 

          There are three formats to the tget instruction.  The first 
      format has the table name in square brackets, followed by strings 
      for a key and a record.  This format is used for sequential 
      reading of the table.  The second and third formats differ only 
      in that the second parameter inside the square brackets is a 
      string (key) in one case and an integer expression (index) in the 
      other.  These instructions are used to retrieve records from the 
      table by key or by index.  The format for the input variable(s) 
      is the same as for getc.  

          In the first format, tget is used to sequentially read 
      key/record pairs from the table.  The purpose of this instruction 
      is to allow the table to be read as a whole.  At run time, the 
      sequential pointer is set to the first record.  It can be reset 
      to the first record by calling the tst function.  Each execution 
      of tget will retrieve the next key/record entry in the table.  An 
      attempt to retrieve beyond the end of the table will result in a 
      run-time error.  All key/record pairs will be retrieved, 
      including those which have null keys and null records.

          In the second format, tget is used to retrieve a record from 
      its key.  The key is the second parameter inside the square 
      brackets.  If the key does not exist in the table, the null 
      string is returned.
 
          In the third format, tget is used to retrieve a record from 
      its index in the table.  Any index between 1 and the maximum 
      size of the table is considered valid.  If the given index has 
      no corresponding pointer in the index table, i.e., no record has 
      been entered with this index, the null string will be returned.  
      An invalid index will result in a run-time error.

15.4  2. tput 

          There are two possible formats to the tput instruction.  
      These formats are roughly the equivalent of the tget formats two 
      and three.  tput is used to store records in a table by key or by 
      index.  The format of the output field of the tput instruction is 
      the same as for the putc and putf instructions.  You may put out 
      either variables or ASCII test.  Variables must be preceded by a 
      tilda sign (~) and followed with a space (see the section on 
      input and output).  
 
          In the first format, tput is used to store data in a table 
      record using a key string as the access mechanism.  If the key 
      does not already exist in the table, it is created and the 
      run-time size of the table is incremented.  If in so doing, the 
      run-time size exceeds 90% of the maximum size, a run-time error 
      will result.  This is because hash tables become very inefficient 
      when their size increases beyond 90% of the allocated space.  The 
      data to be stored may include the null string.  From the point of 
      view of accessing this record by a key, this will have the effect 
      of removing the key/record group from the table.  The key/record 
      group is still accessable using the sequential version of tget.  
 
          In the second format, tput is used to store data in a table 
      record using an index as the access mechanism.  If the index is 
      less than one or greater than the maximum size of the table, a 
      run-time error will result.  If a key does not exist for this 
      index, the null key will be written and the run-time size will 
      be incremented.

15.5  3. treset 

          The treset instruction needs only the name of the table 
      specified in square brackets.  This instruction allows a table 
      to be reset to empty, so that it may be reused by the program.  
      This is useful in the case where data is being collected 
      multiple times in the same program session.  


    ────────────────────────────────────────────────────────────────── 

                    ┌────────────────────────────┐ 
                    │  16. Handling directories  │ 
                    └────────────────────────────┘ 

16.1      Zbex gives you two commands which relate to directories: 
      getdir and createdir.  In addition, you can read the contents 
      of a directory by opening it as a file.  

16.2  getdir  =  get the path of the current directory.  

          The getdir statement will provide you with the path of 
      the current directory.  There is no way with Zbex to change 
      the current directory.  The following example will open the 
      current directory, read its contents, and display them on the 
      screen.  

               Program 
         ═════════════════════ 
         str curdir.80,rec.80 

         getdir curdir 
         open [1,1] curdir 
         loop 
           getf [1] rec 
           putc ~rec 
         repeat 
         run 

                      Execution 
         ═════════════════════════════════════ 
         ** S=8, P=25, L=239, M=452 ** 
         DISP         <DIR>     7-18-95 10:12p 
         FORMATS      <DIR>     8-10-95 11:01p 
         MKSLURS      <DIR>    11-01-95 10:08p 
         SPECS        <DIR>     7-18-95 10:11p 
         COMPRESS OLD    59427  7-05-95  3:12p 
         DECOMP   OLD    26037  7-05-95  3:12p 
         BUG      Z        197  7-05-95  3:12p 
         EXPAND   Z      28179 10-08-95 12:18a 
         MAKECFT  Z      66767 10-09-95  7:22a 
                9 Files(s) 
         Ready for program 
 

16.3  createdir  =  create a new directory.  

          The createdir statement lets you create a new directory.  
      The path to the new directory must already exist (you can 
      create it, one sub-directory at a time).  Zbex does not 
      provide you with a means of removing directories (or removing 
      files, either).  The following example will create a new 
      directory in the current directory.  
 
               Program 
         ═════════════════════ 
            str newdir.80,curdir.80 
            str rec.80,temp.80 

            getdir curdir 
         B: 
            putc Name of new directory?  
            getc newdir 
            temp = ucs(newdir)     /* for comparing with current names 

            Make sure that this directory doesn't already exist 

            open [1,1] curdir 
            loop 
              getf [1] rec 
              if rec{1} = " "      /* no more entries in directory 
                goto A 
              end 
              if rec{18} = ">"     /* looking for sub-directories 
                if rec con " " 
                  rec = rec{1,mpt-1}   /* keep only the name 
                end 
                if rec = temp 
                  putc This directory already exists 
                  close [1]        /* must close [1] so we can open it an 
                  goto B 
                end 
              end 
            repeat 
         A: 
            close [1] 

            Directory doesn't exist; try to create it.  

            newdir = curdir // "/" // newdir 
            createdir newdir 
            putc ~newdir  created 
            run 
 
                      Execution 
         ═════════════════════════════════════ 
         Name of new directory?  
         specs 
         This directory already exists 
         Name of new directory?  
         rivet 
         C:\RELEASE\INTERNET/rivet created 
         Ready for program 


    ────────────────────────────────────────────────────────────────── 

                ┌───────────────────────────────────┐ 
                │  17. Special variables and labels │ 
                └───────────────────────────────────┘ 

17.1      Some of the material covered in this section has also been 
      presented in other sections.  This is because all special 
      variables and special labels are related to some aspect of 
      the operation of Zbex.  

17.2      There are six special variables: err, rem, mpt, sub, sze,
      and trp.  These variables are integer type and are automatically 
      included in your program (with declaration). 

17.3  err This variable takes on meaning only when bit 12 of the 
          zoperation flag is set.  (see Section 18.3 on how to 
          do this using the setflag instruction).  

          Under default operation (bit 12 of zoperation flag is 
          clear = 0), when a Zbex open instruction fails for any 
          reason, Zbex sends a prompt to the user asking for a 
          new (different) file name.  The program is suspended 
          until this supplied.  Sometimes, however, the user 
          does not welcome this interruption; if the file or 
          directory can't be opened, the user wants the program 
          to ignor the open request and move on.  This will 
          happen if bit 12 of the zoperation flag is set = 1..  

          In this case, how does the user's program know that 
          open has failed, and why?  Answer, Zbex sets the 
          special variable err to one of the following values: 

                 0  =  open was successful 
                 1  =  cannot expand to full file name 
                 2  =  file already open somewhere else 
                 3  =  cannot open the file at all 
                 4  =  not a file or a directory 
                 5  =  unable to read directory 
                 6  =  read access on file is denied 
                 7  =  cannot open file for writing 
                 3  =  cannot open the file at all 
                 8  =  writing allowed only on regular files 
                 9  =  read/write access on file is denied 
                 10 =  write access on file is denied 
                 11 =  cannot create new file 

          Program control moves on to the next Zbex instruction, 
          which almost certainly would be to test the value of 
          err.  

17.4  rem This variable is assigned a value every time an integer 
          divide is performed.  If more than one integer divide is 
          performed in a statement, the value of rem will be set by 
          last integer divide to be performed by the statement.  
          The sign of rem is always the sign of the dividend.  
          Example: 
 
               Program 
         ═════════════════════ 
         int q 
 
         q = 13 / 9 
         putc for 13 / 9,   quotient = ~q      rem = ~rem 
         q = -13 / 9 
         putc for -13 / 9,  quotient = ~q     rem = ~rem 
         q = 13 / -9 
         putc for 13 / -9,  quotient = ~q     rem = ~rem 
         q = -13 / -9 
         putc for -13 / -9, quotient = ~q      rem = ~rem 
         run 
 
                      Execution 
         ═════════════════════════════════════ 
         ** S=11, P=98, L=237, M=410 ** 
         for 13 / 9,   quotient = 1     rem = 4 
         for -13 / 9,  quotient = -1    rem = -4 
         for 13 / -9,  quotient = -1    rem = 4 
         for -13 / -9, quotient = 1     rem = -4 
         Ready for program 

17.5  mpt This variable is affected by two types of statements.  
          (1) If the "con" operator in a relation is successful, 
          mpt is assigned the number which is the position in 
          the left hand string where the "con" operate found a 
          match.
          (2) When the txt function appears with only two of its 
          three required variables, mpt is assumed to be the third 
          variable.  If the txt function is successful in parsing 
          a sub-string from the source string, mpt will be set to the 
          subscript of the parsing byte (see section on functions).  
          Example: 

               Program 
         ═════════════════════ 
         str a.10,b.10 

         putc Testing mpt with con 
         putc ──────────────────── 
         a = "Elmer Fudd" 
         if a{3..5} con "er" 
           putc "er" occurs at position ~mpt  in "~a{3..5} " 
         end 
         putc 
         putc Testing mpt in txt 
         putc ────────────────── 
         mpt = 1 
         putc mpt = ~mpt 
         b = txt(a,[32]) 
         putc First word = ~b 
         putc mpt = ~mpt 
         run 

                      Execution 
         ═════════════════════════════════════ 
         ** S=11, P=66, L=255, M=418 ** 
         Testing mpt with con 
         ──────────────────── 
         "er" occurs at position 2 in "mer" 
 
         Testing mpt in txt 
         ────────────────── 
         mpt = 1 
         First word = Elmer 
         mpt = 6 
         Ready for program 

17.6  sub This variable is affected by two types of statements.  
          (1) If the "con" operator in a relation is successful, 
          sub is assigned the subscript in the left hand (string) 
          variable where the "con" operate found a match.  
          match.
          (2) The int(str) and flt(str) functions assign to sub 
          the subscript of the first byte which is not part of 
          the integer format (or real number format).  Examnple:
 
               Program 
         ═════════════════════ 
         str a.20 
         int i 
         real x 
 
         putc Testing sub with con 
         putc ──────────────────── 
         a = "Elmer Fudd" 
         if a{3..5} con "er" 
           putc "er" occurs at subscript ~sub  in "~a " 
         end 
         putc 
         putc Testing sub with int(str) and flt(str) 
         putc ────────────────────────────────────── 
         putc a = "  234.890;  " 
         a = "  234.890;  " 
         sub = 1 
         putc sub = ~sub 
         i = int(a)
         putc Integer value = ~i 
         putc sub = ~sub 
         putc 
         sub = 1 
         putc sub = ~sub 
         x = flt(a)
         putc Real value = ~x 
         putc sub = ~sub 
         run 
 
                      Execution 
         ═════════════════════════════════════ 
         ** S=26, P=152, L=255, M=416 ** 
         Testing sub with con 
         ──────────────────── 
         "er" occurs at subscript 4 in "Elmer Fudd" 

         Testing sub with int(str) and flt(str) 
         ────────────────────────────────────── 
         a = "  234.890;  " 
         sub = 1 
         Integer value = 234 
         sub = 6 
 
         sub = 1 
         Real value = 234.89 
         sub = 10 
         Ready for program 

17.7  sze This variable is assigned a value when you open a file 
          for type 5 access (random read/write).  The value is the 
          number of bytes in the file being opened.  You will need 
          this number in order to avoid reading beyond the end of 
          the file.  

17.8  trp This variable is assigned a value when a return statement 
          with a literal integer is encountered.  The value is the 
          value of the integer.


17.9      There are eleven special labels; trap, brk, and eof1 to 
      eof9.  The trap and brk labels should be used only in the 
      main part of your program, i.e., not in procedures.  

17.10 trap:  The trap label, if used, marks the location in the main 
          program where control is transferred when an abnormal 
          return is encountered in a procedure.  An abnormal return 
          from a procedure clears the entire procedure return stack 
          and assigns a value to the special integer variable trp.  


17.11 brk:  The brk label, if used, marks the location in the main 
          program where control is transferred when the break key 
          is pressed.  If the break procedure is present, this will 
          override the transfer to brk.   If neither the brk lable 
          nor the break procedure is present, pressing the break key 
          will terminate the program.  Examples: 
 
          (1) with brk only.  
 
                Program 
          ═════════════════════ 
                loop 
                repeat 
          brk: 
                putc Break pressed 
                run 
 
                       Execution 
          ═════════════════════════════════════ 
          ** S=5, P=11, L=231, M=410 ** 
          Break pressed 
          Ready for program 
 

          (2) with brk and the break procedure 

                Program 
          ═════════════════════ 
                loop 
                repeat 
          brk: 
                putc Break pressed 
                stop 

                procedure break 
                  putc Break pressed, what now?  
                  getc 
                return 
                run 

                       Execution 
          ═════════════════════════════════════ 
          ** S=10, P=23, L=231, M=410 ** 
          Break pressed, what now?  
                                       (blank line entered) 
          Break pressed, what now?  
          !!  
          Ready for program 

17.12 eof1:  The eof1 label, if used, may appear anywhere in a program, 
          including inside a procedure.  The eof1 location is the entry 
          point for program control following an attempt to getf a 
          record sequencially beyond the last record in a file opened 
          with a tag of [1].   The special labels, eof2 to eof9 work in 
          a similar fashion.  

17.13     Clearly, some caution must be exercised in the use of the 
      special labels.  Since these labels may be placed inside loops 
      with counters, it is possible to jump into such a loop without 
      having set up the counter variable or its limits.  This could 
      lead to unpredictable run time results.  More seriously, the eof 
      labels can be placed in procedures which may not be properly 
      called at run time.   The following program will bomb the 
      the interpreter, because the return from the procedure "death" 
      cannot be properly executed.  

                Program 
          ═════════════════════ 
              open [1,1] "<an existing file>" 
              loop 
                getf [1] 
              repeat 
              putc Congratulations!  You've done the impossible.  
              stop 
          * 
              procedure death 
          eof1: 
              return 


    ────────────────────────────────────────────────────────────────── 

                 ┌──────────────────────────────────┐ 
                 │  18. Instructions for debugging  │ 
                 └──────────────────────────────────┘ 

18.1      The Zbex language provides several tools for debugging 
      programs.  When you write a program, there are three levels 
      of success that you need to achieve.  The first is to have 
      your program compile.  This means following the proper 
      syntax of the language.  Zbex includes an extensive set of 
      compiler time error messages which not only identify errors 
      in your code but also try to explain what you need to do to 
      fix the code.  The second level of success is to have your 
      program run.  The most common run time error in Zbex programs 
      is a subscript error.  The Zbex interpreter knows the size of 
      all of your strings and arrays, and if you try to address 
      memory outside of this range, the interpreter will stop the 
      execution of your program and tell you the line number in your 
      program where the error occurred and what the offending        
      subscript values were.  The third level of success is to 
      get your program to do what you want it to do.  For large 
      programs, this is the most difficult and most time consuming 
      level to achieve.  Most of Zbex's debugging tools are 
      designed to help you solve this problem.  

18.2  1. dputc and dputp.  These instructions are identical to 
      putc and putp except that the line number where they sit in 
      your program is also displayed (printed out).  This way, you 
      can check on the value of certain variables at different points 
      in your program and always be able to tell where those points 
      are.  Example: 
 
                Program 
          ═════════════════════ 
       1  int i 
       2 
       3  loop for i = 1 to 3 
       4    i = i + 6 
       5  repeat 
       6  dputc i = ~i 
       7  run 
 
                       Execution 
          ═════════════════════════════════════ 
          ** S=6, P=20, L=233, M=410 ** 
          T0006 i = 7 
          Ready for program 
 

18.3  2. The behavior of Zbex in certain situations is determined by 
      a set of flags.  The state of these flags can be changed using 
      the setflag instruction.  This instruction has two formats.  

          Format 1:  setflag x[xxxxxxxxxxxxxxxx]  (x = 0 or 1) 

          Format 2:  setflag x,y    (x = bit number, y = 0 or 1) 

          In format 1, from 1 to 12 bits are specified in the second field 
          of the instruction.   Format 1 can be used to set the state 
          of all twelve flags at one time.  In format 2, you supply 
          a bit number and its new setting.  With format 2, you can 
          set the state on only one flag at a time.  

      The twelve flags control the following aspects of Zbex operation.  
      The default setting of all flags is 0 (no action).  
 
      Bit Num          Zbex behavior when bit is 1 
      ───────  ────────────────────────────────────────────────────── 
         1     Give warning on overflow on integer multiply 
 
         2     Give warning on overflow on conversion of ASCII string 
                 to integer 

         3     Give warning on attempt to divide by zero 

         4     Give warning on overflow on floating point number to 
                 integer conversion 
 
         5     I/O flag -- convert out-of-bounds fixed decimal floating 
                 point representation to floating decimal floating point 
                 representation 
 
         6     Give warning on attempt to read/write partially outside of 
                 bounds of a file (read/writing totally outside is cause 
                 for termination) 
 
         7     Give warning when LHS of string statement with subscripts 
                 has a different length than the RHS 

         8     Give warning when the argument of a real function 
                 is outside the proper domain 
 
         9     Give warning when string subscripts are out-of-bounds 
                 in relational statements 

        10     Suppress warnings for writing more than 100,000 records 
                 or more than 6 MB in a file 
 
        11     Give warning when the initial value of loop counter 
                 is set outside the loop limits 

        12     When open fails, don't prompt for a file name.  Instead, 
                 set the err variable to a positive number.  0 = normal.  


18.4  3. trace and untrace.  These commands take a single variable 
      as an argument.  The trace command is actually a command to 
      the compiler.  Whenever an instruction is compiled that could 
      possibly change the value of the given variable, a putc type 
      instruction is also compiled into the code, which will display 
      the value of the variable at run time.  The untrace command 
      will turn trace off for the given variable.  The purpose of 
      the trace command is to allow you to see where a particular 
      variable is being modified, and to track its value during the 
      execution of a program.  Example: 
 
                Program 
          ═════════════════════ 
          int i 
          trace i 
          loop for i = 1 to 5 
            i = i + 1 
          repeat 
          untrace i 
          i = 10 
          run 

                       Execution 
          ═════════════════════════════════════ 
          ** S=8, P=18, L=233, M=410 ** 
          Trace activated at line number 6 
          Integer counter: i = 1   Final value = 5 
          Trace activated at line number 8 
          Integer variable: i = 2 
          Trace activated at line number 6 
          Integer counter: i = 3   Final value = 5 
          Trace activated at line number 8 
          Integer variable: i = 4 
          Trace activated at line number 6 
          Integer counter: i = 5   Final value = 5 
          Trace activated at line number 8 
          Integer variable: i = 6 
          Ready for program 

18.5  4. examine.  When all else fails, there is always the examine 
      command.  examine puts your program into step mode.  From the 
      point where the examine occurs, you can step through your 
      program by pressing the  F9  key.  While in examine mode, you 
      can examine the value of any string, bit-string, integer or 
      real variable; and you can change the value of any such variable.  
      You can also exit examine mode and return to normal operation 
      of the program.  Examine statements can be put anywhere in 
      your program, and you can put in as many as you like.  examine 
      is a tedious way to find program bugs, but for some types of    
      problems, it has proven to be the fastest way to find the 
      source of trouble.  
 
      When you enter examine mode, you are presented with the 
      following menu: 

      ***  Entering examine mode at source line number <#>  *** 
          /x,<var>   = examine value of variable 
          /h,<var>   = examine value of variable in hex format 
          /c,<var>   = change value of variable 
          F9         = step one line in program 
          /e         = exit examine mode; continue with program 
          <var> = [<procedure name>]<variable name>(<subscripts>) 
             (a dot may be substituted for [<current procedure>]) 
 
      It is important understand how to specify a variable.  If 
      you want to know the value of the integer variable i, for 
      example, you need to specify whether this is a global 
      variable (a variable in the main program), or a variable 
      in a procudure.  If you precede the variable with a dot, 
      it means that you are specifying a local variable (within 
      the current procudure).  Example: 
 
                Program 
          ═════════════════════ 
          int i 
          loop for i = 1 to 1 
            perform pro1 
          repeat 
          stop 
 
          procedure pro1 
            int i 
            i = 20 
            examine 
            perform pro2 
          return 

          procedure pro2 
            int i 
            i = 10000 
          return 
          run 

                       Execution 
          ═════════════════════════════════════ 
          ** S=16, P=29, L=237, M=410 ** 
          ***  Entering examine mode at source line number 11  *** 
              /x,<var>   = examine value of variable 
              /h,<var>   = examine value of variable in hex format 
              /c,<var>   = change value of variable 
              F9         = step one line in program 
              /e         = exit examine mode; continue with program 
              <var> = [<procedure name>]<variable name>(<subscripts>) 
                 (a dot may be substituted for [<current procedure>]) 
          /x,i 
          Integer variable i = 1 
          /x,.i 
          Integer variable .i = 20 
          /x,[pro2]i 
          Integer variable [pro2]i = 0 
          /e 
          Ready for program 

      Examine mode will work only when your source program comes from 
      a file, not from a window.  This is because the interpreter 
      needs to have runtime access to the source code in order to 
      display the lines of your program at run time.  When in examine 
      mode, you will see 15 lines of your program at a time, with a 
      highlight on the fourth line (i.e., you can see three lines back 
      of where you currently are and eleven lines ahead of where you 
      currently are).   If you change to another window while in 
      examine mode, when you change back, the program lines are no 
      longer there.  Simply press the Enter key, and they will return.  


    ────────────────────────────────────────────────────────────────── 

                   ┌─────────────────────────────┐ 
                   │  19. Conditional compiles   │ 
                   │  and other features of the  │ 
                   │          Compiler           │ 
                   └─────────────────────────────┘ 

19.1      Zbex provides you with some compile time features that can 
      add flexibility and readability to your programs.  The following 
      commands must start in column one.  

19.2  #define   The #define command has two fields, separated by one 
      more blanks.  The second field is defined as a compile time 
      replacement for the first field.  The effect during compilation 
      is that whenever the compiler finds a string of characters 
      (outside a put statement) that matches the first field, it 
      substitutes the string of characters in the second field.  This 
      feature allows you to identify arbitrary constants (sometimes 
      called "magic numbers") with a name, so that these can later 
      be easily found and changed.  It also allows you to identify 
      program parameters by name (e.g., the size of arrays) so that 
      these can be easily changed.  The #define command is also used 
      to set the value of boolean variables used in conditional 
      compiles.  The variable is identified in the first field, and 
      its value is set by the second field.  The value is 1, if and 
      only if the second field is a "1".   Example: 

 
                Program 
          ═════════════════════ 
          #define LIMIT  40 
 
              int h,i,j(LIMIT) 
              j(1) = 1 
              j(2) = 1 
              loop for i = 3 to LIMIT 
                j(i) = j(i-1) + j(i-2) 
              repeat 
              loop for i = 1 to LIMIT 
                putc ~j(i)  ...  
                h = i / 5 
                if rem = 0 
                  putc 
                end 
              repeat 
              putc 
              run 
 
                       Execution 
          ═════════════════════════════════════ 
          ** S=15, P=57, L=243, M=450 ** 
          1 1 2 3 5 
          8 13 21 34 55 
          89 144 233 377 610 
          987 1597 2584 4181 6765 
          10946 17711 28657 46368 75025 
          121393 196418 317811 514229 832040 
          1346269 2178309 3524578 5702887 9227465 
          14930352 24157817 39088169 63245986 102334155 
 
          Ready for program 

19.3  #if, #else, #endif.   This group of commands works much like the 
      if/else/end group in Zbex, except that the effect is to tell 
      the compiler which lines of code to compile.  The #if command 
      requires a boolean variable, whose value must be set by a 
      #define command (see above).  If the value of the variable is 1, 
      the lines following the #if will be compiled, up to the #else 
      (which is optional) or the #endif of the group.  It is possible 
      to have nested #if-#else-#endif groups.  Example: 
 
                Program 
          ═════════════════════ 
          #define OPTION_1    1 
          #define OPTION_2    0 

               str name.30,subname.10(3) 
               int i 
 
               name = "Ronald Wilson Reagan" 
 
               mpt = 0 
               loop for i = 1 to 3 
                 subname(i) = txt(name,[' ']) 
               repeat 

               putc The candidate's name is ...  
          #if OPTION_1 
               putc ~subname(1)  ...  

          #if OPTION_2 
               putc ~subname(2)  ...  
          #else 
               putc ~subname(2){1} . ...  
          #endif 

          #endif 
               putc ~subname(3) 
               run 

                       Execution 
          ═════════════════════════════════════ 
          ** S=12, P=56, L=263, M=431 ** 
          The candidate's name is Ronald W. Reagan 
          Ready for program 

19.4  #process.  This command allows you to identify groups of lines 
      that you want included in a compile.  Groups of lines are 
      identified by letters, caps and small (52 groups total).  The 
      compiler will include all groups identied by letters found on 
      #process command line.  You may use the #process command more 
      than once.  Program lines belonging to a group are identified 
      by an ampersand "&" in column one, followed by a single letter 
      in column two and a blank space in column three.  Example: 
 
                Program 
          ═════════════════════ 
          #process A b D          
 
          &A  putc Option A 
          &b  putc Option B 
          &c  putc Option C 
          &D  putc Option D 
          &E  putc Option E 
          &F  putc Option F 
              run 
 
                       Execution 
          ═════════════════════════════════════ 
          ** S=4, P=17, L=231, M=410 ** 
          Option A 
          Option B 
          Option D 
          Ready for program 
 
      The #process command is very useful for removing debug statements 
      from the compile process after your program is running properly.  
      I have found it helpful to keep debug statements available in 
      case something goes wrong with one of my programs later on.  
      The advantage is that I don't have to rethink the debugging 
      process from scratch.  By using the %<letter> method for removing 
      these lines, I can turn them back on in an instant by simply 
      including the proper process statement at the top my program.  
      In the small example below, I can turn on the dputc statement 
      by replacing the "*" in front of process with a "#".  

                Program 
          ═════════════════════ 
          *process A 

              int i 
              loop for i = 1 to 10 
          &A    dputc ~i 
              repeat 
              run 

19.5  #autodef.  If you are writing a short program and you are 
      feeling too lazy to declare your variables, you may be able 
      to use the #autodef command.  #autodef automatically declares 
      the letters 'a'..'t' to be integers and the letters 'u'..'z' 
      to be real numbers.  All other symbols will be declared as 
      strings with maximum length of 160.   Example: 
 
                Program 
          ═════════════════════ 
          #autodef 
          A = "" 
          loop for i = 1 to 1000 
            A = A // "x" 
          repeat 
          run 

                       Execution 
          ═════════════════════════════════════ 
          ** S=5, P=16, L=237, M=451 ** 
          Run-time error: maximum length of string variable: A 
            has been exceeded at line = 4 
            attempted length = 161, maximum length = 160 
          Ready for program 

19.6      The Zbex compiler recognizes a few abbreviations that have 
      become standard in other programing languages.  These are listed 
      below, along with what they represent.  

            Abbreviation        Expanded to ...  
           ──────────────      ───────────────────────── 
               ++i              i = i + 1          (integer only) 
               --i              i = i - 1          (integer only) 
 
               x += 1.0         x = x + 1.0        (integer and real) 
               i -= 10          i = i - 10             "     "    " 
               y *= x + 3.0     y = y * (x + 3.0)      "     "    " 
               k /= 4           k = k / 4              "     "    " 

               i &= 0x01        i = i & 0x01       (integer only) 
               i |= 0x04        i = i | 0x04           "     " 
               i >>= 3          i = i >> 3             "     " 
               i <<= 4 + j      i = i << (4 + j)       "     " 

 
    ────────────────────────────────────────────────────────────────── 

    ┌───────────────────────────────────────────────────────────────┐ 
    │ 20. Controlling the (text) display with putc (Advanced Topic) │ 
    └───────────────────────────────────────────────────────────────┘ 
 
20.1      Zbex provides you with some escape sequences that allow 
      you to control certain parameters of the display from your 
      Zbex program.  Use putc to send these sequences to Dmuse.  

      1. Two characters escape sequences 

            Sequence   Effect 
            ────────   ────────────── 
            <esc> A    Cursor Up 
            <esc> B    Cursor Down 
            <esc> C    Cursor Right 
            <esc> D    Cursor Left 
            <esc> E    set Tab 
            <esc> F    Cursor End of Window (End) 
            <esc> G    (none)
            <esc> H    Cursor Top of Window (Home) 
            <esc> I    Tab 
            <esc> J    Clear Display 
            <esc> K    Clear Line 
            <esc> L    Insert Line 
            <esc> M    Delete Line 
            <esc> N    Copy Line to Buffer 
            <esc> O    Insert Line from Buffer 
            <esc> P    Delete Character 
            <esc> Q    Enter Insert Mode (if ins_mode = OFF) 
            <esc> R    Leave Insert Mode (if ins_mode = ON) 
            <esc> S    Scroll Up 
            <esc> T    Scroll Down 
            <esc> U    Page Up 
            <esc> V    Page Down 
            <esc> W    Scroll Right 
            <esc> X    Scroll Left 
            <esc> Y    Cursor to Column 0 
            <esc> Z    Cursor to End of line 
 
      2. Multiple character sequences 
 
         A. Cursor Addressing 
 
            1. Relative addressing: move cursor to <x,y> position in 
                 the currently displayed screen.  The <x> position in 
                 this case is actually absolute, i.e., same as <c> below.  
                 Numbering starts with 0, not 1.  Numbers connected to 
                 "Y" must have at least two digits.  (e.g., 09 = 9).  
 
               <esc>&a <column number> x <row number> Y 
               <esc>&a <column number> X 
               <esc>&a <row number> Y 
 
            2. Absolute addressing: move cursor to <c,r> position in 
                 the currently displayed window.  Scrolling works, but 
                 you cannot move to a row below the data at the bottom 
                 a window.  Numbering starts with 0.  Numbers connected 
                 to "R" must have at least two digits.  (e.g., 09 = 9).  
 
               <esc>&a <column number> c <row number> R 
               <esc>&a <column number> C 
               <esc>&a <row number> R 
 
            3. Cursor Relative Addressing: move cursor a relative distance 
                 from its present location <+or-c,+or-r> on the screen.  

               <esc>&a <+or-><column number> c <+or-><row number> R 
               <esc>&a <+or-><row number> r <+or-><column number> C 
               <esc>&a <+or-><column number> C 
               <esc>&a <+or-><row number> R 
 
            Note: Actually, it is possible to mix and match any of these 
                  commands.  The end of the sequence is signified by a 
                  capital command letter.  This means that if we want to 
                  back up the cursor 20 positions and goto row 0, we 
                  would send the command <esc>&a-20c00R 
 
         B. Display Enhancements 
 
               <esc>&d@   normal wind_txt 
               <esc>&dA   enhancement 1 
               <esc>&dB   enhancement 2 
               <esc>&dC   enhancement 3 
               <esc>&dD   enhancement 4 
               <esc>&dE   enhancement 5 
               <esc>&dF   enhancement 6 
               <esc>&dG   enhancement 7 
               <esc>&dH   enhancement 8 
               <esc>&dI   enhancement 9 
               <esc>&dJ   enhancement 10 
               <esc>&dK   enhancement 11 
               <esc>&dL   enhancement 12 
               <esc>&dM   enhancement 13 
               <esc>&dN   enhancement 14 
               <esc>&dO   enhancement 15 

 
    ────────────────────────────────────────────────────────────────── 

                 ┌─────────────────────────────────┐ 
                 │  21. Graphics (Advanced Topic)  │ 
                 └─────────────────────────────────┘ 

21.1      The normal mode of operation for an Zbex mode is text mode, 
      using the current Dmuse window for input and output.  It is 
      possible with Zbex to display data in graphics mode (music, 
      for example).  Zbex is not designed for constructing images in 
      graphics mode.  It is not good at drawing lines or circles.  
      It is good at displaying letters and other glyph like characters, 
      and it is reasonably efficient at displaying bitmap images that 
      have been constructed "off screen".  

21.2      There are eleven▂Zbex instructions that relate to graphics.  

      bitmode   =  change to graphics display 
      textmode  =  change to normal, character based, window display 
      setup     =  setup a string as graphics buffer 
      activate  =  identify a graphics buffer with a particular graphics 
                     plane 
      setb      =  turn on a group of bits in a graphics buffer 
      clearb    =  turn off a group of bits in a graphics buffer 
      getk      =  get a keystroke (spin until keystroke arrives) 
      getx      =  if a keystroke is pending, deliver it; otherwise return 0

      dscale2   =  scale a graphics buffer to 50% of its original size 
      dscale3   =  scale a graphics buffer to 33% of its original size 
      dscale5   =  scale a graphics buffer to 66% of its original size 

21.3      In order to use these instructions, you need to know how the 
      graphics mode is implemented in Zbex and how this relates to the 
      Linux X Diplay window.  Dmuse implements graphics as a set of 
      four single-plane Pixmaps, each one designed to display its 
      contents in a different color.  As a concept, think of these 
      planes as a stacked set, labled 1, 2, 3, and 4, with 4 on the 
      top.  Imagine that the background of the bottom plane is always 
      white, and the foreground is the base color, black.  Then think 
      of the remaining three planes as being like "tranperancies," 
      with one opaque color for writing and a background that is 
      "see-through." The default colors for planes 2 thorough 4 are 
      respectively, green, red, and blue.  The planes never change 
      their position, but things written to them can be erased and 
      re-written in a different places.  If the "blue" (top) image is 
      fixed and something on the "red" (3rd plane) is moved, this can 
      give the impression that the red image is changing under the 
      blue image.  Parts of the red image that were previously 
      "eclipsed" by the blue image would appear to move out from 
      behind the blue image.  Similarly, if the "red" plane is fixed 
      and something on the "blue" plane is moved (like a blue cursor 
      shape, for example), the blue cursor would appear to move around 
      on top of the red plane, but never destroying what was written 
      there (cursor like behavior).  

21.4       These Pixmap planes form the "hand-off" between what Zbex 
      wants to display and what Dmuse passes on to X Windows.  Dmuse 
      knows how to combine the information on the Pixmaps so that the 
      rules of eclipse are followed.  All the Zbex programmer needs to 
      concern himself/herself about is what is written to each of the 
      four Pixmap color planes.  Zbex does this through a mechanism 
      called a graphics buffer.  Zbex does not have a separate data 
      type called a graphics buffer; it uses strings for this.  The 
      setup instruction is provided to make it easy to format a string 
      as a graphics buffer.  A graphics buffer consists of a base plane 
      and zero to nine "scratch" planes.  When Zbex writes a graphics 
      buffer to one of the four Pixmap planes, it does this by first 
      clearing the base plane, then copying (OR-ing) all of the set 
      (on = 1) bits on each scratch plane to the base plane.  The base 
      plane is then copied to the specified Pixmap plane.  This system 
      has the advantage that features such as musical staff lines on 
      one scratch plane are uneffected by movements of notes and other 
      musical notation on another scratch plane.  
 

21.5      The format of a graphics buffer (string) is this: 

      bytes 1-5:  array paramteers 

        bytes 1-2:  m  =  number of BYTES in the horizontal dimension 
        bytes 3-4:  n  =  number of rows in the vertical dimension 
        byte 5:     p  =  number of "scratch" planes plus 1 

      bytes 6-10: activation parameters (dynamic at run time) 

        byte 6:        Pixmap plane to which buffer was last written 
                            0 = no plane designated 
                       1 to 4 = designated Pixmap plane; buffer is 
                                  said to be active 
        bytes 7-8:  x  =  x offset of last activation command 
        bytes 9-10: y  =  y offset of last activation command 
 
      bytes 11-18: left, top, right, and bottom exposure boundaries 
 
        bytes 11-12:   =  left boundary (x1) 
        bytes 13-14:   =  top  boundary (y1) 
        bytes 15-16:   =  right  boundary (x2) 
        bytes 17-18:   =  bottom boundary (y2) 

      bytes 19-20: unused 

      bytes 21...      m*n*p   bytes of data space 

         If m*n*p + 20 > declared length of string, a length error occurs 
         If m*n*p = 0, the string is invalid as a graphics buffer 

21.6      The setup instruction is designed to simplify the process of 
      formating a string as a graphics buffer.  The setup instruction 
      takes a string name, and three integer variables.  It is 
      functionally equivalent to the following Zbex code: 
 
          setup s,i,j,k      s = zpd(i*j*k+20)  /* check length */ 
                             s{1,2} = ch2(i) 
                             s{3,2} = ch2(j) 
                             s{5}   = ch(k) 

                             s{11,2} = ch2(0x3fff) 
                             s{13,2} = ch2(0x3fff) 
                             s{15,2} = ch2(0) 
                             s{15,7} = ch2(0) 
 
      The exposure boundaries are initially set to opposite 
      extremes.  When the first write to buffer s occurs, these limits 
      take on real-time values.  
 
      A graphics buffer need not have the same dimensions as a  
      Pixmap; it may be larger or smaller in either dimension.  
      Only that portion of the buffer lying within the Pixmap's 
      dimensions will be written to the Pixmap plane.  

21.7      The activate instruction provides a means of associating 
      a particular graphics buffer with a particular Pixmap plane, and 
      for actually writing that buffer to that plane.  The format for 
      the activate instruction is shown below.  
 
      activate S-str,int1,int2,int3
 
          S-str  =  name of a string that serves as a graphics buffer 
 
           int1  =  horizontal offset expressed in BYTES for mapping 
                      S-str onto its display plane 
           int2  =  vertical offset expressed in ROWS for mapping 
                      S-str onto its display plane 
           int3  =  action parameter 
                           -1 = flush buffer to Pixmap plane 
                                  if byte 6 of S-str is positive 
                            0 = deactive this buffer; 
                                  set byte 6 of S-str to 0 
                      1,2,3,4 = write buffer to Pixmap plane <#> 
                                  set byte 6 of S-str to <#> 
                            5 = erase area covered by S-str on 
                                  Pixmap plane designated by byte 6 
                                  of S-str           
                  11,12,13,14 = set byte 6 of S-str to <#-10> but 
                                  do not write buffer to Pixmap plane 

      The first two integer expressions supplied by the instruction 
      indicate the values of the <x,y> offsets for mapping S-str to 
      the display.  The <x> coordinate is expressed in BYTES, not 
      bits.  <0,0> is the upper left-hand corner; and positive offsets 
      move down and to the right.  

      The third integer expression, p, determines the action of the 
      activate instruction.  If p = 0, action is minimal.  Byte 6 
      of S-str is simply set to 0, thus "deactivating" the variable.  
      If p = 5, this means that the Pixmap plane designated by byte 6 
      (assuming its value is 1--4) is erased (set to 0) over the area 
      covered by S-str (as offset by the x and y coordinates in bytes 
      7--10).  If p = 1, 2, 3, or 4, then a write to Pixmap plane <p> 
      will occur.  If p = 11, 12, 13, or 14, S-str will be activated 
      on Pixmap plane (p-10), but a write to that plane will not occur.  

      Once the these actions are completed, and if p = 1, 2, 3, 4, or 5, 
      Dmuse combines all color Pixmaps in the appropriate way and sends 
      them to the Display screen.  

      If the offset <x,y> expressed in the first two integer 
      expressions of the instruction is different than the the previous 
      offsets stored in bytes 7--10 of S-str, AND the value of byte 6 
      and p are the same, then an erase of the former area will occur 
      before the write.  In the case where S-str covers an area smaller 
      than the graphics bitplane, this will have the effect of "moving 
      the image" from one location to another; in the case where S-str 
      covers an area larger than the graphics bitplane, this will be an 
      effective way to implement "panning" or "scrolling" of a larger 
      image.  
 
      If p = -1, this asks that the portion of S-str described by 
      the rectangle <x1,y1>, <x2,y2> be written to the Pixmap plane 
      referenced by byte 6 of S-str.  Of course, if byte-6 = 0, no 
      write will occur.  This use of the activate instruction is new 
      with the Linux version of Dmuse.  In the older DOS version, it 
      was quick and easy to write to the computer display screen, 
      and every call to setb/clearb (described below) did this 
      automatically.  In X Windows every write to a color Pixmap 
      requires a write from the X Client to the X Server, so we want 
      to keep the number of these writes to minimum.  The graphics 
      buffers are part of the Client application, so calls to setb 
      and clearb are fast and require no "X" communication.  But we 
      need a way to "flush" these buffers to their destination 
      Pixmaps (byte 6) at selected points in Zbex programs.  This 
      explains the purpose of the exposure boundaries, bytes 11-18 
      in S-str.  They outline the rectangle containing all 
      setb/clearb writes since the last flush command.  After the 
      flush write is completed the exposure boundaries are reset to 
      <0x3fff,0x3fff>, <0,0>.  
 
      Note that the flush version of activate (activate S-str,0,0,-1) 
      DOES NO WRITING to the Display screen; this can only be done 
      with activate in its regular form.  Also note that the x and y 
      values are ignored in this call.  The idea is that this 
      information is already stored in bytes 1-4 of S-str.  Flush 
      simply does all at once what use to be done piecemeal by setb 
      and clearb.  
 

21.8      The real work of building a graphics page is in turning 
      bits on and off bits in the graphics buffer.  Theoretically, this 
      could be done using conventional Zbex instructions, but this 
      would be time consuming to program and the code would be slow 
      to execute.  Instead, Zbex has two specially designed instructions, 
      setb and clearb, to do this in the fastest possible manner.  
 
      These instructions come in two formats: one using a bit-string 
      array as a source, and the other using an integer array as a 
      source.  The operation of the instruction under each of these 
      formats is slightly different.  The only difference between setb 
      and clearb is that setb turns bits on and clearb turns bits off.  
 
      Format 1 
      ──────── 
      setb/clearb S-str,A-bstr,int1,int2,int3,int4,int5,int6; 
 
          S-str  =  name of a string that serves as a graphics buffer 
 
         A-bstr  =  a array of bit strings containing a collection 
                      of bitmap images
           int1  =  horizontal offset expressed in BITS for the 
                      destination of a bitmap image in S-str 
           int2  =  vertical offset expressed in ROWS for the 
                      destination of bitmap image in S-str 
           int3  =  height of bitmap image to be transferred 
 
           int4  =  width of bitmap image to be transferred 
 
           int5  =  starting row in A-bstr for the bitmap image 
                      (number of rows to transfer is <int3>     
           int6  =  destination plane in S-str (1 = base plane, 
                      2 = scratch plane 1, etc.) 

      Format 2 
      ──────── 
      setb/clearb S-str,A-int,int1,int2,int3,int4: 

          S-str  =  name of a string that serves as a graphics buffer 

          A-int  =  an array of integers (treated as 32 bit wide bit 
                      strings) representing a collection of bitmap 
                      images.  
           int1  =  horizontal offset expressed in BITS for the 
                      destination of a bitmap image in S-str 
           int2  =  vertical offset expressed in ROWS for the 
                      destination of bitmap image in S-str 
           int3  =  array element (number) in A-int.  This element 
                      contains the offset (element number) in A-int 
                      for the bitmap data to be transferred.  At the 
                      offset address, the first integer (array element) 
                      contains four 8-bit data fields:  the height and 
                      width of the image, and the local horizonal and 
                      vertical offsets of image relative to the <x,y> 
                      placement parameters.  The second integer contains 
                      one (left justified) 8-bit field:  the increment 
                      to the <x> placement parameter after a set/clear 
                      operation.  
 
                         First integer contains: 
                         ┌──────┬──────┬───────────────┬───────────────┐ 
                         │height│width │local h. offset│local v. offset│ 
                         └──────┴──────┴───────────────┴───────────────┘ 
                         Second integer contains: 
                         ┌────────────────┬────────────────────────────┐ 
                         │increment to <x>│        |   zeros  |        │ 
                         └────────────────┴────────────────────────────┘ 
                         Third and following integers contain the 
                           bitmap image.  If the image is less than 
                           33 bits wide, each row of the image will 
                           require one integer; if the image is less 
                           than 65 bits wide, each row of the image 
                           will require two integers; etc.  

           int4 = destination plane in S-str 

      If byte 6 of S-str is 1,2,3 or 4 (S-str is activated), then 
      setb (clearb) will adjust the size of the exposure rectangle 
      (bytes 11-18 in S-str) to include the area changed by the 
      write.   This way, with one instruction you can look up a 
      bitmap image, transfer it to a graphics buffer, record this 
      action for later "flushing," and increment the variable which 
      specifies the <x> position for writing.  

 
      Operation 
      ───────── 
      The first variable is interpreted as a graphics buffer.  It acts 
      as <p> independent bitmap planes, where <p> is the dimension 
      specified in byte 5 of S-str (see diagram). The second variable 
      is a one dimensional bitstring array, A-bstr, or a one dimensional 
                                integer array, A-int(q).  This variable 
               S-str(m,n,p)     functions as a source for a set of 
             p┌──────────────┐  bitmats (see 2nd diagram).  
             ┌┴─────────────┐│ 
           .┌┴─────────────┐││  Now we can describe the action of the 
          .┌┴─────────────┐│││  command.  Let's talk about setb.  This 
         .┌┴─────────────┐││││  instruction is used to turn on bits 
        3┌┴─────────────┐│││││  in S-str.  The integers supplied with 
       2┌┴─────────────┐││││││  the instruction determine what pattern 
      1┌┴─────────────┐│││││├┘  of bits is turned on and where it is 
       │ ↑            │││││├┘   turned on.  The first two integers are 
       │◄┼─ m bytes ─►││││├┘   the <x,y> offset in S-str where the 
       │ │            │││├┘   pattern will start.  Notice that <x> is 
       │n rows        ││├┘   given as a BIT offset, not a byte offset.  
       │ │            │├┘   <0,0> is the top lefthand corner of S-str.  
       │ ↓            ├┘   The meaning of the remaining variables depends 
       └──────────────┘   on the format.   In the case of the first 
                         format type (using a bit-string source, the 
                        third integer is the height (vertical dimension) 
        A-bstr(.)      of the "box" image to be copied to S-str.  The 
        ┌───────┐     fourth integer is the width (horizontal dimension) 
        │       │    measured in BITs.  This is the maximum width of this 
        │       │   particular image in A-bstr.  The fifth integer is 
        ├───────┤   the array row number in A-bstr(.) of the first bit 
        ├───────┤   string to copy.  Notice that since this number is a 
        ├───────┤   subscript, it follows the rules of subscripts, 
        │       │   e.g., it cannot be less than one.  The sixth integer 
        │       │   is a value from 1 to <p> and is the plane to which 
        ├───────┤   the copy should be made.  
        │       │ 
        └───────┘   This technique will allow us to store entire fonts
                    in long, bitstring arrays.  To select a glyph for
      copying, we simply need to know the beginning array row number 
      in A-bstr(.) and the number of rows (height).  The fact that we 
      have <p> planes to write in means that we can build up complex 
      notations such as music from a series of glyphs.  It also means 
      that we can selectively turn on and off various glyphs, without 
      disturbing other glyphs.  For example, we could turn off a music 
      note and turn it back on in another location, without disturbing 
      the music staff lines in the background.  

      The operation of the second format (using an A-int source) is 
      slightly more complicated, but for certain kinds of tasks, it is 
      more streamline and faster.  The idea is that if we are using 
      these instructions to turn on and off letters of a font, much of 
      the information about the letters is known and can therefore be 
      put directly in the integer array.  In this case, the first and 
      second integers (represent the <x,y> position) must be integer 
                     variables, not literal numbers or expressions, 
        A-int(.)     as allowed in the first format.  It is possible 
       1┌───────┐    for this instruction to modify first variable 
        ├       ┤    <x> after the image is copied to the graphics 
        ├       ┤    buffer.  The third integer (expression) is an 
        ├       ┤    offset to an array element in A-int.  The fourth 
      97├───────┤    integer is a value from 1 to <p> and is the 
      ┌<│pointer│    plane in the graphics buffer to which the copy 
      │ ├───────┤    should be made.  
      │ │       │ 
      │ ├───────┤    One way of conceptualizing the role of the third 
      └►│param1 │    integer (expression) is to think of it as acting 
        │───────│    like a font number.  For example, the number 97 
        │param2 │    might point to a small 'a'.  This would mean that 
        │───────│    the 97-th entry in A-int would contain the offset 
        │bit    │    to the data for the 'a'.  In effect, this offset 
        │image  │    would be the equivalent of the fifth integer in 
        │       │    the first format.  The first two integers of the 
        ├───────┤    data block for the letter 'a' are reserved for 
        │       │    data about the letter.  The first integer contains 
                     the following information: 
 
        high order byte: height of the box (equivalent of third integer) 
                 byte 2: width of the box (equivalent of fourth integer) 
                 byte 1: horz offset (signed)  added to <x> 
         low order byte: vert offset (signed)  added to <y> 

      The high order byte of second integer contains the increment to 
      <x> following execution of instruction.  The third and following 
      integers contain the data for the letter 'a'.  Note that the 
      number of integers required per row will depend on the width of 
      the box (given in byte 2).  If the width is 32 or less, one 
      integer per row is required; 64 or less, two integers per row are 
      required; etc.  One can see that the integer array, A-int, must 
      be constructed rather carefully by the user programmer.  The 
      payoff, however, is much greater speed at run time for 
      applications that write fonts to the screen in a linear fashion.  
 
      setb is used to turn bits on; clearb is used to turn bits off.  
      The exact mechanism for writing will depend on whether bits are 
      being set or cleared.  In the first case, the specified plane 
      needs to be OR-ed to plane 1 (the base plane in the graphics 
      buffer); in the second case, all of the planes 2 and higher 
      need to be OR-ed together and the result placed in plane 1 
      (the base plane).  
 

21.9      When a Zbex program is compiled, a flag is set if 
      graphics instructions are used anywhere in the program.  When 
      the program is handed off to the Zbex interpreter, if this 
      flag is set, the interpreter allocates a set of four backup 
      color Pixmaps for storing the program's graphics.  The four 
      operational color Pixmaps are also cleared at this time, but 
      no writing is done to the user's X-Window display.  If the 
      Zbex program's operation is suspended (either by a switch to 
      text mode, or by a change to another window), the four 
      operational color Pixmaps are automatically copied to the 
      backup Pixmaps for this program.  If a Zbex program is 
      re-entered (by a window change), the backup color Pixmaps are 
      automatically copied back to the operational color Pixmaps, 
      and if the program is currently running in graphics mode, the 
      color Pixmaps are then written to the user's X-Window 
      display.  In this way, it is possible to have more than one 
      Zbex program running in graphics mode.  

21.10     In order to run in graphics mode, Zbex needs to have a 
      mechanism for clearing the screen and for determining the size 
      of the active screen.   This is the task of the bitmode 
      instruction.  The bitmode instruction has the following format: 
 
          bitmode int-exp,int,int 
 
      The format consists of the instruction, one integer expression 
      and two integer variables.  The value of the integer expression 
      must be 1 or 2.  This value determines the operation of the 
      instruction.  

      (1) Since Zbex programs should be able to run in any size X-window 
          there needs to be a way for a Zbex program to access the current 
          size of the Display Window in which it is running.  

      (2) Zbex also needs to have a way of clearing all four of the 
          color Pixmaps and asking that the Display be cleared as well.  
 
      If the value of int-exp is 1, then only task (1) will be done.  If 
      the value of int-exp is 2, both task (1) and task (2) will be done.  
      The values returned in the two integers are the current width and 
      height of the user's X-Window Display.  

      The bitmode instruction may be issued anywhere in a Zbex program.  
      If the display is not already using bitmap graphics, the window 
      display mechanism will be switched from text (glyph) based to 
      bitmap based.  
 

21.11     The Zbex textmode instruction is used to switch the display 
      from graphics mode to character mode.  The current window with 
      its current contents will reappear.  The contents of the four 
      operational color Pixmaps will be copied to the backup Pixmaps.  

21.12     The normal way to send Zbex imput from the keyboard is with 
      the getc command.  This is fine for sending the program lines 
      from a window, but it does not give the programmer control of 
      the keyboard.  In graphics mode, we often need to have control 
      of the keyboard so that we can program the effect of certain 
      keystrokes.  For example, we may want to scroll the graphics 
      screen using the cursor keys.  The way to get keystroke input 
      directly from the keyboard is with the getk instruction.  getk 
      takes one integer variable as an input argument.  

      Listed below are the keystrokes from the keyboard which will 
      return a value to getk (not all keystrokes will).  The values 
      are represented in HEX format.  

      ┌───────────────────────────────┬─────────────────────────────────┐ 
      │      SORTED BY NUMBER         │        SORTED BY KEYSTROKE      │ 
      │    ════════════════════       │      ═══════════════════════    │ 
      │Value                          │                          Value  │ 
      │Returned     Keystroke         │       Keystroke         Returned│ 
      │───────  ──────────────────    │ ────────────────────   ─────────│ 
      │0x10001  <ctrl-shft> a         │ <esc>                    0x1001b│ 
      │0x10002  <ctrl-shft> b         │ F1                       0x31000│ 
      │0x10003  <ctrl-shft> c         │ <shft> F1                0x31010│ 
      │0x10004  <ctrl-shft> d         │ <ctrl> F1                0x31020│ 
      │0x10005  <ctrl-shft> e         │ <ctrl-shft> F1           0x31040│ 
      │0x10006  <ctrl-shft> f         │ F2                       0x31001│ 
      │0x10007  <shft-alt> 9          │ <shft> F2                0x31011│ 
      │0x10009  <shft-alt> 0          │ <ctrl> F2                0x31021│ 
      │0x10008  <ctrl-shft> g         │ <ctrl-shft> F2           0x31041│ 
      │0x1000a  <shft-alt> -          │ F3                       0x31002│ 
      │0x1000b  <ctrl-shft> k         │ <shft> F3                0x31012│ 
      │0x1000c  <ctrl-shft> h         │ <ctrl> F3                0x31022│ 
      │0x1000d  <ctrl-shft> l         │ <ctrl-shft> F3           0x31042│ 
      │0x1000e  <ctrl-shft> m         │ F4                       0x31003│ 
      │0x1000f  <ctrl-shft> o         │ <shft> F4                0x31013│ 
      │0x10010  <ctrl-shft> p         │ <ctrl> F4                0x31023│ 
      │0x10011  <ctrl-shft> q         │ <ctrl-shft> F4           0x31043│ 
      │0x10012  <ctrl-shft> r         │ F5                       0x31004│ 
      │0x10013  <ctrl-shft> s         │ <shft> F5                0x31014│ 
      │0x10014  <ctrl-shft> t         │ <ctrl> F5                0x31024│ 
      │0x10015  <ctrl-shft> u         │ <ctrl-shft> F5           0x31044│ 
      │0x10016  <ctrl-shft> v         │ F6                       0x31005│ 
      │0x10017  <ctrl-shft> w         │ <shft> F6                0x31015│ 
      │0x10018  <ctrl-shft> x         │ <ctrl> F6                0x31025│ 
      │0x10019  <ctrl-shft> y         │ <ctrl-shft> F6           0x31045│ 
      │0x1001a  <shft-alt> =          │ F7                       0x31006│ 
      │0x1001b  <esc>                 │ <shft> F7                0x31016│ 
      │0x1001c  <ctrl-shft> n         │ <ctrl> F7                0x31026│ 
      │0x1001e  <ctrl-shft> i         │ <ctrl-shft> F7           0x31046│ 
      │0x1001f  <ctrl-shft> j         │ F8                       0x31007│ 
      │0x10020  SPACE                 │ <shft> F8                0x31017│ 
      │0x10021  <shft> 1              │ <ctrl> F8                0x31027│ 
      │0x10022  <shft> '              │ <ctrl-shft> F8           0x31047│ 
      │0x10023  <shft> 3              │ F9                       0x31008│ 
      │0x10024  <shft> 4              │ <shft> F9                0x31018│ 
      │0x10025  <shft> 5              │ <ctrl> F9                0x31028│ 
      │0x10026  <shft> 7              │ <ctrl-shft> F9           0x31048│ 
      │0x10027  '                     │ F10                      0x31009│ 
      │0x10028  <shft> 9              │ <shft> F10               0x31019│ 
      │0x10029  <shft> 0              │ <ctrl> F10               0x31029│ 
      │0x1002a  <shft> 8              │ <ctrl-shft> F10          0x31049│ 
      │0x1002b  <shft> =              │ F11                      0x3100a│ 
      │0x1002c  ,                     │ <shft> F11               0x3101a│ 
      │0x1002d  -                     │ <ctrl> F11               0x3102a│ 
      │0x1002e  .                     │ <ctrl-shft> F11          0x3104a│ 
      │0x1002f  /                     │ F12                      0x3100b│ 
      │0x10030  0                     │ <shft> F12               0x3101b│ 
      │0x10031  1                     │ <ctrl> F12               0x3102b│ 
      │0x10032  2                     │ <ctrl-shft> F12          0x3104b│ 
      │0x10033  3                     │ `                        0x10060│ 
      │0x10034  4                     │ <shft> `                 0x1007e│ 
      │0x10035  5                     │ 1                        0x10031│ 
      │0x10036  6                     │ <shft> 1                 0x10021│ 
      │0x10037  7                     │ <shft-alt> 1             0x100ad│ 
      │0x10038  8                     │ 2                        0x10032│ 
      │0x10039  9                     │ <shft> 2                 0x10040│ 
      │0x1003a  <shft> ;              │ <shft-alt> 2             0x1009d│ 
      │0x1003b  ;                     │ 3                        0x10033│ 
      │0x1003c  <shft> ,              │ <shft> 3                 0x10023│ 
      │0x1003d  =                     │ <shft-alt> 3             0x1009c│ 
      │0x1003e  <shft> .              │ 4                        0x10034│ 
      │0x1003f  <shft> /              │ <shft> 4                 0x10024│ 
      │0x10040  <shft> 2              │ <shft-alt> 4             0x1009b│ 
      │0x10041  <shft> a              │ 5                        0x10035│ 
      │0x10042  <shft> b              │ <shft> 5                 0x10025│ 
      │0x10043  <shft> c              │ <shft-alt> 5             0x1009f│ 
      │0x10044  <shft> d              │ 6                        0x10036│ 
      │0x10045  <shft> e              │ <shft> 6                 0x1005e│ 
      │0x10046  <shft> f              │ <shft-alt> 6             0x100fd│ 
      │0x10047  <shft> g              │ 7                        0x10037│ 
      │0x10048  <shft> h              │ <shft> 7                 0x10026│ 
      │0x10049  <shft> i              │ <shft-alt> 7             0x100ac│ 
      │0x1004a  <shft> j              │ 8                        0x10038│ 
      │0x1004b  <shft> k              │ <shft> 8                 0x1002a│ 
      │0x1004c  <shft> l              │ <shft-alt> 8             0x100ab│ 
      │0x1004d  <shft> m              │ 9                        0x10039│ 
      │0x1004e  <shft> n              │ <shft> 9                 0x10028│ 
      │0x1004f  <shft> o              │ <shft-alt> 9             0x10007│ 
      │0x10050  <shft> p              │ 0                        0x10030│ 
      │0x10051  <shft> q              │ <shft> 0                 0x10029│ 
      │0x10052  <shft> r              │ <shft-alt> 0             0x10009│ 
      │0x10053  <shft> s              │ -                        0x1002d│ 
      │0x10054  <shft> t              │ <shft> -                 0x1005f│ 
      │0x10055  <shft> u              │ <alt> -                  0x100aa│ 
      │0x10056  <shft> v              │ <shft-alt> -             0x1000a│ 
      │0x10057  <shft> w              │ =                        0x1003d│ 
      │0x10058  <shft> x              │ <shft> =                 0x1002b│ 
      │0x10059  <shft> y              │ <alt> =                  0x100a9│ 
      │0x1005a  <shft> z              │ <shft-alt> =             0x1001a│ 
      │0x1005b  [                     │ BackSpace                0x3040a│ 
      │0x1005c  \                     │ <shft> BackSpace         0x3040b│ 
      │0x1005d  ]                     │ Tab                      0x30810│ 
      │0x1005e  <shft> 6              │ <left-shft> Tab          0x30811│ 
      │0x1005f  <shft> -              │ <right-shft>  Tab        0x30812│ 
      │0x10060  `                     │ <ctrl> Tab               0x30814│ 
      │0x10061  a                     │ <left-ctrl-shft> Tab     0x30815│ 
      │0x10062  b                     │ <right-ctrl-shft> Tab    0x30813│ 
      │0x10063  c                     │ q                        0x10071│ 
      │0x10064  d                     │ <shft> q                 0x10051│ 
      │0x10065  e                     │ <ctrl> q                 0x30471│ 
      │0x10066  f                     │ <alt>  q                 0x100da│ 
      │0x10067  g                     │ <ctrl-shft> q            0x10011│ 
      │0x10068  h                     │ <shft-alt>  q            0x100d6│ 
      │0x10069  i                     │ w                        0x10077│ 
      │0x1006a  j                     │ <shft> w                 0x10057│ 
      │0x1006b  k                     │ <ctrl> w                 0x30477│ 
      │0x1006c  l                     │ <alt>  w                 0x100c2│ 
      │0x1006d  m                     │ <ctrl-shft> w            0x10017│ 
      │0x1006e  n                     │ <shft-alt>  w            0x100d2│ 
      │0x1006f  o                     │ e                        0x10065│ 
      │0x10070  p                     │ <shft> e                 0x10045│ 
      │0x10071  q                     │ <ctrl> e                 0x30465│ 
      │0x10072  r                     │ <alt>  e                 0x100bf│ 
      │0x10073  s                     │ <ctrl-shft> e            0x10005│ 
      │0x10074  t                     │ <shft-alt>  e            0x100b7│ 
      │0x10075  u                     │ r                        0x10072│ 
      │0x10076  v                     │ <shft> r                 0x10052│ 
      │0x10077  w                     │ <ctrl> r                 0x30472│ 
      │0x10078  x                     │ <alt>  r                 0x100c4│ 
      │0x10079  y                     │ <ctrl-shft> r            0x10012│ 
      │0x1007a  z                     │ t                        0x10074│ 
      │0x1007b  <shft> [              │ <shft> t                 0x10054│ 
      │0x1007c  <shft> \              │ <ctrl> t                 0x30474│ 
      │0x1007d  <shft> ]              │ <alt>  t                 0x100c9│ 
      │0x1007e  <shft> `              │ <ctrl-shft> t            0x10014│ 
      │0x1007f  <ctrl-shft> z         │ <shft-alt>  t            0x100d5│ 
      │0x10091  <shft-alt> i          │ y                        0x10079│ 
      │0x10092  <shft-alt> o          │ <shft> y                 0x10059│ 
      │0x1009b  <shft-alt> 4          │ <ctrl> y                 0x30479│ 
      │0x1009c  <shft-alt> 3          │ <alt>  y                 0x100cb│ 
      │0x1009d  <shft-alt> 2          │ <ctrl-shft> y            0x10019│ 
      │0x1009e  <shft-alt> p          │ <shft-alt>  y            0x100d1│ 
      │0x1009f  <shft-alt> 5          │ u                        0x10075│ 
      │0x100a8  <shft-alt>  /         │ <shft> u                 0x10055│ 
      │0x100a9  <alt> =               │ <ctrl> u                 0x30475│ 
      │0x100aa  <alt> -               │ <alt>  u                 0x100bb│ 
      │0x100ab  <shft-alt> 8          │ <ctrl-shft> u            0x10015│ 
      │0x100ac  <shft-alt> 7          │ <shft-alt>  u            0x100b8│ 
      │0x100ad  <shft-alt> 1          │ i                        0x10069│ 
      │0x100ae  <shft-alt>  ,         │ <shft> i                 0x10049│ 
      │0x100af  <shft-alt>  .         │ <ctrl> i                 0x30469│ 
      │0x100b0  <alt>  o              │ <alt>  i                 0x100cd│ 
      │0x100b1  <alt>  p              │ <ctrl-shft> i            0x1001e│ 
      │0x100b2  <alt>  [              │ <shft-alt>  i            0x10091│ 
      │0x100b3  <alt>  f              │ o                        0x1006f│ 
      │0x100b4  <alt>  d              │ <shft> o                 0x1004f│ 
      │0x100b5  <shft-alt>  j         │ <ctrl> o                 0x3046f│ 
      │0x100b6  <shft-alt>  d         │ <alt>  o                 0x100b0│ 
      │0x100b7  <shft-alt>  e         │ <ctrl-shft> o            0x1000f│ 
      │0x100b8  <shft-alt>  u         │ <shft-alt>  o            0x10092│ 
      │0x100b9  <alt>  j              │ p                        0x10070│ 
      │0x100ba  <alt>  k              │ <shft> p                 0x10050│ 
      │0x100bb  <alt>  u              │ <ctrl> p                 0x30470│ 
      │0x100bc  <alt>  m              │ <alt>  p                 0x100b1│ 
      │0x100bd  <shft-alt>  c         │ <ctrl-shft> p            0x10010│ 
      │0x100be  <shft-alt>  m         │ <shft-alt>  p            0x1009e│ 
      │0x100bf  <alt>  e              │ [                        0x1005b│ 
      │0x100c0  <alt>  z              │ <shft> [                 0x1007b│ 
      │0x100c1  <alt>  x              │ <alt>  [                 0x100b2│ 
      │0x100c2  <alt>  w              │ <shft-alt> [             0x100fe│ 
      │0x100c3  <alt>  a              │ ]                        0x1005d│ 
      │0x100c4  <alt>  r              │ <shft> ]                 0x1007d│ 
      │0x100c5  <alt>  s              │ <alt>  ]                 0x100db│ 
      │0x100c6  <shft-alt>  g         │ <shft-alt> ]             0x100fa│ 
      │0x100c7  <shft-alt>  a         │ \                        0x1005c│ 
      │0x100c8  <alt>  b              │ <shft> \                 0x1007c│ 
      │0x100c9  <alt>  t              │ a                        0x10061│ 
      │0x100ca  <alt>  n              │ <shft> a                 0x10041│ 
      │0x100cb  <alt>  y              │ <ctrl> a                 0x30461│ 
      │0x100cc  <alt>  g              │ <alt>  a                 0x100c3│ 
      │0x100cd  <alt>  i              │ <ctrl-shft> a            0x10001│ 
      │0x100ce  <alt>  h              │ <shft-alt>  a            0x100c7│ 
      │0x100cf  <shft-alt>  n         │ s                        0x10073│ 
      │0x100d0  <shft-alt>  x         │ <shft> s                 0x10053│ 
      │0x100d1  <shft-alt>  y         │ <ctrl> s                 0x30473│ 
      │0x100d2  <shft-alt>  w         │ <alt>  s                 0x100c5│ 
      │0x100d3  <shft-alt>  z         │ <ctrl-shft> s            0x10013│ 
      │0x100d4  <shft-alt>  b         │ <shft-alt>  s            0x100d7│ 
      │0x100d5  <shft-alt>  t         │ d                        0x10064│ 
      │0x100d6  <shft-alt>  q         │ <shft> d                 0x10044│ 
      │0x100d7  <shft-alt>  s         │ <ctrl> d                 0x30464│ 
      │0x100d8  <shft-alt>  h         │ <alt>  d                 0x100b4│ 
      │0x100d9  <alt>  c              │ <ctrl-shft> d            0x10004│ 
      │0x100da  <alt>  q              │ <shft-alt>  d            0x100b6│ 
      │0x100db  <alt>  ]              │ f                        0x10066│ 
      │0x100dc  <alt>  ;              │ <shft> f                 0x10046│ 
      │0x100dd  <alt>  ,              │ <ctrl> f                 0x30466│ 
      │0x100de  <alt>  .              │ <alt>  f                 0x100b3│ 
      │0x100df  <alt>  l              │ <ctrl-shft> f            0x10006│ 
      │0x100fa  <shft-alt> ]          │ g                        0x10067│ 
      │0x100fd  <shft-alt> 6          │ <shft> g                 0x10047│ 
      │0x100fe  <shft-alt> [          │ <ctrl> g                 0x30467│ 
      │0x30101  ◄-                    │ <alt>  g                 0x100cc│ 
      │0x30102  ↑                     │ <ctrl-shft> g            0x10008│ 
      │0x30103  -►                    │ <shft-alt>  g            0x100c6│ 
      │0x30104  ↓                     │ h                        0x10068│ 
      │0x30105  <left-shft>  ◄-       │ <shft> h                 0x10048│ 
      │0x30106  <left-shft>  ↑        │ <ctrl> h                 0x30468│ 
      │0x30107  <left-shft>  -►       │ <alt>  h                 0x100ce│ 
      │0x30108  <left-shft>  ↓        │ <ctrl-shft> h            0x1000c│ 
      │0x30109  <ctrl> ◄-             │ <shft-alt>  h            0x100d8│ 
      │0x30109  <right-shft> ◄-       │ j                        0x1006a│ 
      │0x3010a  <ctrl> ↑              │ <shft> j                 0x1004a│ 
      │0x3010a  <right-shft> ↑        │ <ctrl> j                 0x3046a│ 
      │0x3010b  <ctrl> -►             │ <alt>  j                 0x100b9│ 
      │0x3010b  <right-shft> -►       │ <ctrl-shft> j            0x1001f│ 
      │0x3010c  <ctrl> ↓              │ <shft-alt>  j            0x100b5│ 
      │0x3010c  <right-shft> ↓        │ k                        0x1006b│ 
      │0x3010d  <alt>  ◄-             │ <shft> k                 0x1004b│ 
      │0x3010e  <alt>  ↑              │ <ctrl> k                 0x3046b│ 
      │0x3010f  <alt>  -►             │ <alt>  k                 0x100ba│ 
      │0x30110  <alt>  ↓              │ <ctrl-shft> k            0x1000b│ 
      │0x30111  <left-shft-alt>  ◄-   │ l                        0x1006c│ 
      │0x30112  <left-shft-alt>  -►   │ <shft> l                 0x1004c│ 
      │0x30113  <right-shft-alt> ◄-   │ <ctrl> l                 0x3046c│ 
      │0x30114  <right-shft-alt> -►   │ <alt>  l                 0x100df│ 
      │0x30115  <ctrl-shft>  ◄-       │ <ctrl-shft> l            0x1000d│ 
      │0x30116  <ctrl-shft>  ↑        │ ;                        0x1003b│ 
      │0x30117  <ctrl-shft>  -►       │ <shft> ;                 0x1003a│ 
      │0x30118  <ctrl-shft>  ↓        │ <alt>  ;                 0x100dc│ 
      │0x30119  <left-shft-alt>  ↑    │ '                        0x10027│ 
      │0x3011a  <left-shft-alt>  ↓    │ <shft> '                 0x10022│ 
      │0x3011b  <right-shft-alt> ↑    │ Enter                    0x3080c│ 
      │0x3011c  <right-shft-alt> ↓    │ <shft> Enter             0x3080d│ 
      │0x30120  PageUP                │ <ctrl> Enter             0x3080e│ 
      │0x30121  PageDOWN              │ <alt>  Enter             0x3080f│ 
      │0x30122  Home                  │ z                        0x1007a│ 
      │0x30123  End                   │ <shft> z                 0x1005a│ 
      │0x30124  <shft> PageUP         │ <ctrl> z                 0x3047a│ 
      │0x30125  <shft> PageDOWN       │ <alt>  z                 0x100c0│ 
      │0x30126  <shft> Home           │ <ctrl-shft> z            0x1007f│ 
      │0x30127  <shft> End            │ <shft-alt>  z            0x100d3│ 
      │0x30130  <ctrl> PageUP         │ x                        0x10078│ 
      │0x30131  <ctrl> PageDOWN       │ <shft> x                 0x10058│ 
      │0x30132  <ctrl> Home           │ <ctrl> x                 0x30478│ 
      │0x30133  <ctrl> End            │ <alt>  x                 0x100c1│ 
      │0x30134  <ctrl-shft> PageUP    │ <ctrl-shft> x            0x10018│ 
      │0x30135  <ctrl-shft> PageDOWN  │ <shft-alt>  x            0x100d0│ 
      │0x30136  <ctrl-shft> Home      │ c                        0x10063│ 
      │0x30137  <ctrl-shft> End       │ <shft> c                 0x10043│ 
      │0x30400  Insert                │ <ctrl> c                 0x30463│ 
      │0x30401  <shft> Insert         │ <alt>  c                 0x100d9│ 
      │0x30402  <ctrl> Insert         │ <ctrl-shft> c            0x10003│ 
      │0x30403  <alt>  Insert         │ <shft-alt>  c            0x100bd│ 
      │0x30404  <shft-alt>  Insert    │ v                        0x10076│ 
      │0x30405  Delete                │ <shft> v                 0x10056│ 
      │0x30406  <shft> Delete         │ <ctrl> v                 0x30476│ 
      │0x30407  <ctrl> Delete         │ <ctrl-shft> v            0x10016│ 
      │0x30408  <alt>  Delete         │ b                        0x10062│ 
      │0x30409  <shft-alt>  Delete    │ <shft> b                 0x10042│ 
      │0x3040a  BackSpace             │ <ctrl> b                 0x30462│ 
      │0x3040b  <shft> BackSpace      │ <alt>  b                 0x100c8│ 
      │0x3040c  <ctrl-shft> Insert    │ <ctrl-shft> b            0x10002│ 
      │0x30461  <ctrl> a              │ <shft-alt>  b            0x100d4│ 
      │0x30462  <ctrl> b              │ n                        0x1006e│ 
      │0x30463  <ctrl> c              │ <shft> n                 0x1004e│ 
      │0x30464  <ctrl> d              │ <ctrl> n                 0x3046e│ 
      │0x30465  <ctrl> e              │ <alt>  n                 0x100ca│ 
      │0x30466  <ctrl> f              │ <ctrl-shft> n            0x1001c│ 
      │0x30467  <ctrl> g              │ <shft-alt>  n            0x100cf│ 
      │0x30468  <ctrl> h              │ m                        0x1006d│ 
      │0x30469  <ctrl> i              │ <shft> m                 0x1004d│ 
      │0x3046a  <ctrl> j              │ <ctrl> m                 0x3046d│ 
      │0x3046b  <ctrl> k              │ <alt>  m                 0x100bc│ 
      │0x3046c  <ctrl> l              │ <ctrl-shft> m            0x1000e│ 
      │0x3046d  <ctrl> m              │ <shft-alt>  m            0x100be│ 
      │0x3046e  <ctrl> n              │ ,                        0x1002c│ 
      │0x3046f  <ctrl> o              │ <shft> ,                 0x1003c│ 
      │0x30470  <ctrl> p              │ <alt>  ,                 0x100dd│ 
      │0x30471  <ctrl> q              │ <shft-alt>  ,            0x100ae│ 
      │0x30472  <ctrl> r              │ .                        0x1002e│ 
      │0x30473  <ctrl> s              │ <shft> .                 0x1003e│ 
      │0x30474  <ctrl> t              │ <alt>  .                 0x100de│ 
      │0x30475  <ctrl> u              │ <shft-alt>  .            0x100af│ 
      │0x30476  <ctrl> v              │ /                        0x1002f│ 
      │0x30477  <ctrl> w              │ <shft> /                 0x1003f│ 
      │0x30478  <ctrl> x              │ <shft-alt>  /            0x100a8│ 
      │0x30479  <ctrl> y              │ SPACE                    0x10020│ 
      │0x3047a  <ctrl> z              │ Insert                   0x30400│ 
      │0x30800  Pad *                 │ <shft> Insert            0x30401│ 
      │0x30801  <shft> Pad *          │ <ctrl> Insert            0x30402│ 
      │0x30802  Pad +                 │ <alt>  Insert            0x30403│ 
      │0x30803  <shft> Pad +          │ <ctrl-shft> Insert       0x3040c│ 
      │0x30804  Pad -                 │ <shft-alt>  Insert       0x30404│ 
      │0x30805  <shft> Pad -          │ Delete                   0x30405│ 
      │0x30806  Pad /                 │ <shft> Delete            0x30406│ 
      │0x30807  <ctrl> Pad +          │ <ctrl> Delete            0x30407│ 
      │0x3080b  <ctrl-shft> Pad +     │ <alt>  Delete            0x30408│ 
      │0x3080c  Enter                 │ <shft-alt>  Delete       0x30409│ 
      │0x3080d  <shft> Enter          │ Home                     0x30122│ 
      │0x3080e  <ctrl> Enter          │ <shft> Home              0x30126│ 
      │0x3080f  <alt>  Enter          │ <ctrl> Home              0x30132│ 
      │0x30810  Tab                   │ <ctrl-shft> Home         0x30136│ 
      │0x30811  <left-shft> Tab       │ End                      0x30123│ 
      │0x30812  <right-shft> Tab      │ <shft> End               0x30127│ 
      │0x30813  <right-ctrl-shft> Tab │ <ctrl> End               0x30133│ 
      │0x30814  <ctrl> Tab            │ <ctrl-shft> End          0x30137│ 
      │0x30815  <left-ctrl-shft> Tab  │ PageUP                   0x30120│ 
      │0x30820  Pad Enter             │ <shft> PageUP            0x30124│ 
      │0x30821  <shft> Pad Enter      │ <ctrl> PageUP            0x30130│ 
      │0x30822  <ctrl> Pad Enter      │ <ctrl-shft> PageUP       0x30134│ 
      │0x30823  <ctrl-shft> Pad Enter │ PageDOWN                 0x30121│ 
      │0x30824  <alt>  Pad Enter      │ <shft> PageDOWN          0x30125│ 
      │0x30825  <shft-alt>  Pad Enter │ <ctrl> PageDOWN          0x30131│ 
      │0x30826  <ctrl-alt>  Pad Enter │ <ctrl-shft> PageDOWN     0x30135│ 
      │0x30827  <shft-ctrl-alt> Pad En│ ◄─                       0x30101│ 
      │0x31000  F1                    │ <left-shft>  ◄─          0x30105│ 
      │0x31001  F2                    │ <right-shft> ◄─          0x30109│ 
      │0x31002  F3                    │ <ctrl> ◄─                0x30109│ 
      │0x31003  F4                    │ <alt>  ◄─                0x3010d│ 
      │0x31004  F5                    │ <ctrl-shft>  ◄─          0x30115│ 
      │0x31005  F6                    │ <left-shft-alt>  ◄─      0x30111│ 
      │0x31006  F7                    │ <right-shft-alt> ◄─      0x30113│ 
      │0x31007  F8                    │ ↑                        0x30102│ 
      │0x31008  F9                    │ <left-shft>  ↑           0x30106│ 
      │0x31009  F10                   │ <right-shft> ↑           0x3010a│ 
      │0x3100a  F11                   │ <ctrl> ↑                 0x3010a│ 
      │0x3100b  F12                   │ <alt>  ↑                 0x3010e│ 
      │0x31010  <shft> F1             │ <ctrl-shft>  ↑           0x30116│ 
      │0x31011  <shft> F2             │ <left-shft-alt>  ↑       0x30119│ 
      │0x31012  <shft> F3             │ <right-shft-alt> ↑       0x3011b│ 
      │0x31013  <shft> F4             │ ─►                       0x30103│ 
      │0x31014  <shft> F5             │ <left-shft>  ─►          0x30107│ 
      │0x31015  <shft> F6             │ <right-shft> ─►          0x3010b│ 
      │0x31016  <shft> F7             │ <ctrl> ─►                0x3010b│ 
      │0x31017  <shft> F8             │ <alt>  ─►                0x3010f│ 
      │0x31018  <shft> F9             │ <ctrl-shft>  -►          0x30117│ 
      │0x31019  <shft> F10            │ <left-shft-alt>  ─►      0x30112│ 
      │0x3101a  <shft> F11            │ <right-shft-alt> ─►      0x30114│ 
      │0x3101b  <shft> F12            │ ↓                        0x30104│ 
      │0x31020  <ctrl> F1             │ <left-shft>  ↓           0x30108│ 
      │0x31021  <ctrl> F2             │ <right-shft> ↓           0x3010c│ 
      │0x31022  <ctrl> F3             │ <ctrl> ↓                 0x3010c│ 
      │0x31023  <ctrl> F4             │ <alt>  ↓                 0x30110│ 
      │0x31024  <ctrl> F5             │ <ctrl-shft>  ↓           0x30118│ 
      │0x31025  <ctrl> F6             │ <left-shft-alt>  ↓       0x3011a│ 
      │0x31026  <ctrl> F7             │ <right-shft-alt> ↓       0x3011c│ 
      │0x31027  <ctrl> F8             │ Pad /                    0x30806│ 
      │0x31028  <ctrl> F9             │ Pad *                    0x30800│ 
      │0x31029  <ctrl> F10            │ <shft> Pad *             0x30801│ 
      │0x3102a  <ctrl> F11            │ Pad -                    0x30804│ 
      │0x3102b  <ctrl> F12            │ <shft> Pad -             0x30805│ 
      │0x31040  <ctrl-shft> F1        │ Pad +                    0x30802│ 
      │0x31041  <ctrl-shft> F2        │ <shft> Pad +             0x30803│ 
      │0x31042  <ctrl-shft> F3        │ <ctrl> Pad +             0x30807│ 
      │0x31043  <ctrl-shft> F4        │ <ctrl-shft> Pad +        0x3080b│ 
      │0x31044  <ctrl-shft> F5        │ Pad Enter                0x30820│ 
      │0x31045  <ctrl-shft> F6        │ <shft> Pad Enter         0x30821│ 
      │0x31046  <ctrl-shft> F7        │ <ctrl> Pad Enter         0x30822│ 
      │0x31047  <ctrl-shft> F8        │ <alt>  Pad Enter         0x30824│ 
      │0x31048  <ctrl-shft> F9        │ <ctrl-shft> Pad Enter    0x30823│ 
      │0x31049  <ctrl-shft> F10       │ <shft-alt>  Pad Enter    0x30825│ 
      │0x3104a  <ctrl-shft> F11       │ <ctrl-alt>  Pad Enter    0x30826│ 
      │0x3104b  <ctrl-shft> F12       │ <shft-ctrl-alt> Pad Ent  0x30827│ 
      └───────────────────────────────┴─────────────────────────────────┘ 

      Notice that Pad 0 through Pad 9 (also <shft> Pad 0 to 9, and 
      <alt> Pad 0 to 9) are not included in this list.   The reason is 
      that these keystrokes are still needed by Dmuse for changing 
      windows.  Consequently, they are not available to Zbex.  
 

21.13     There is also a getx instruction in Zbex.  This instruction 
      is different from getk in that it does not block the execution 
      of the Zbex program.  If there has been one or more keystrokes 
      (from the set above) that has not yet been processed, and the  
      Zbex interpreter encounters a getx instruction, then the value 
      of the most recent keystroke will be delivered, and the keystroke 
      queue will be cleared.  If the keystroke queue is empty, a getx 
      instruction will simply return 0x0000 in the output variable.  
      The Zbex program will continue to execute.  

21.14     Zbex provides three commands for scaling a graphics image: 
      dscale2, which produces a 1/2 size image, dscale3, which produces 
      a 1/3 size image, and dscale5, which produces a 2/3 size image.  
      A common use of these instructions is scaling an image, 
      constructed at 300 dots/inch (for printing), down to a size that 
      can viewed on a screen (e.g., 75 dots/inch).  
 
      There are two forms for these instructions: (1) complete scaling 
      and (2) partial scaling.  Both forms require two graphic string 
      variables, a source and a destination.  At compile time, any 
      simple strings will suffice.  It is at run time that the program 
      must check values to see that the strings are properly formatted 
      (setup) as graphics buffers.  In particular, the following 
      conditions for the string variables must hold: 
 
          For both strings, 0 < m*n*p <= len(S-str) - 20, where 
            m = value represented in bytes 1-2  (BYTES in row) 
            n = value represented in bytes 3-4  (number of rows) 
            p = value represented in bytes 5    (number of planes) 

      It is possible to specify four additional integer parameters 
      to the dscale instructions.  These are respectively the 
      left boundary, the top boundary, the right boundary, and the 
      bottom boundary of the space that is to be scaled. Left and 
      right boundaries are measured in COLUMNS; top and bottom 
      boundaries are measured in ROWS.  If these variables are not 
      specified, the values <0,0> and <90000,90000> are supplied by 
      the compiler.  
 
      The format for the dscale instructions is then: 
 
          dscale3 S-str1, S-str2      or 
          dscale3 S-str1, S-str2, int,int,int,int 

      Operation (for the instruction dscale3): 

      Let m1 = value for m for S-str1; and m2 = value of m for S-str2 
      Let n1 = value for n for S-str1; and n2 = value of n for S-str2 
 
      If m1 = 3 * m2, rows will be mapped directly from S-str1 to 
                      S-str2 (three rows to one).  
      If m1 > 3 * m2, scaled rows from S-str1 will be concatinated 
                      to fit in S-str2.  
      If m1 < 3 * m2, scaled rows from S-str1 will be padded with 
                      zero bytes to fill up S-str2.  

      If n1 = 3 * n2, all rows will be scaled directly from S-str1 
                      to S-str2 (three rows to one).  
      If n1 > 3 * n2, only a sufficent number of rows from S-str1 
                      will be scaled to S-str2 to fill it up.  
      If n1 < 3 * n2, all rows from S-str1 will be scaled directly 
                      to S-str2.  The remaining rows will be set to 
                      zero bytes.  

      In dealing with the space to be scaled, the COLUMN values are converted
      (expanded outward) to the nearest 3-BYTE boundaries, and the row 
      values are expanded outward to the nearest 3-ROW boundaries.  The 
      minimum values for <BTYES,ROWS> are adjusted to be no less than 
      <0,0> and the maximum values for <BTYES,ROWS> are adjusted to no 
      greater than <m1,n1>.  Then, only those rows and bytes between the 
      minimum and maximum values are scaled.  

      In describing the operation for dscale2, all values of 3 should be 
      replaced by 2.  For the dscale5 instruction, all values of 3 
      would be replaced by 3/2.  

21.15     In a Zbex program that uses graphics mode, certain run-time 
      situations can occur whose behavior needs to be specified.  
 
      1. If graphics instructions, activate, setb, or clearb, are 
         encountered before the first execution of the bitmode 
         instruction, this is not a problem.  The graphics screen 
         is initialized, but the user's Zbex program will not know 
         the size of the user's X-Window display.  If one of these 
         instructions is encountered while Zbex is running in 
         textmode, then the mode will be changed to graphics mode, 
         and the graphics screen restored.  Remember, using the 
         bitmode instruction to change to graphics mode will clear 
         the graphics screen.  

      2. textmode may be executed any time.  If Zbex is in graphics 
         mode at the time, then textmode will cause the graphics 
         screen to be temporarily stored.  

      3. If a Zbex program is running in graphics mode, it may be 
         temporarily suspended by changing to another window.  It 
         will be terminated if "disconnected" (<shft> KeyPad *) in 
         the same window.  If a program with graphics is suspended, 
         it will not execute in the background.  Also, when a 
         program with graphics applications is running, all other 
         Zbex programs are suspended.  This is because the getk 
         instruction preempts the event loop.  
 
      4. If getc or putc is encountered while Zbex is running in 
         graphics mode, the effect will be the same as if a textmode 
         instruction were executed and then the getc or putc.  This 
         allows the seamless (although probably not instantaneous) 
         transition from graphics to text and back (see 1 above).  


    ────────────────────────────────────────────────────────────────── 

             ┌──────────────────────────────────────────┐ 
             │  22. MIDI instructions (Advanced Topic)  │ 
             └──────────────────────────────────────────┘ 

22.1      MIDI stands for Musical Instrument Digital Interface.  MIDI 
      is the dominant method for sending data to and receiving data 
      from a music keyboard.  In order for Dmuse running on Linux to 
      interface with a MIDI keyboard, your system needs two things.  

22.2  (1) You need to have the ALSA (Advanced Linux Sound Architecture) 
      Driver installed on your system.  You can determine whether the 
      ALSA system is installed on your computer by typing the command: 
      cat /proc/asound/version 
      which should return something like: 
      Advanced Linux Sound Architecture Driver Version 1.0.17 
 
      If you do not get positive response to the command above, 
      then you will need to go on line and download the Alsa system.  
      The web site for the ALSA project is www.alsa-project.org 
      You will need to download the alsa-lib and the alsa-utils 
      and you will need a makefile, which looks something like this: 
 
          all: unpack compile 
 
          download: 
                   ┊wget -i source.txt 

          unpack: 
                   ┊tar xvjf alsa-lib-1.0.20.tar.bz2 
                   ┊tar xvjf alsa-utils-1.0.20.tar.bz2 

          compile: 
                   ┊(cd alsa-lib-1.0.20; ./configure) 
                   ┊(cd alsa-lib-1.0.20; make install) 
          -------------------------------------------------- 
 
          Note: ┊ = Tab character 
 

      (2) You need to have hardware and drivers that connect your 
      computer (probably via a usb port) to a keyboard.  I am using 
      a MidiMan 1x1 usb MIDI device.  

      To get and install the drivers, following the instructions on 
         http://usb-midi-fw.sourceforge.net 
 
         (1) Download fxload RPM package: 
             wget ftp://rpmfind.net/linux/fedora/updates/7/i386/fxload-2002_04_11-6.fc7.i386.rpm

         (2) Install fxload rpm: 
    -->      rpm --install fxload-2002_04_11-6.fc7.i386.rpm 
 
         (3) Download midisport firmware package: 
            wget http://softlayer.dl.sourceforge.net/sourceforge/usb-midi-fw/midisport-firmware-1.2.tar.gz

    -->  (4) tar xvzf midisport-firmware-1.2.tar.gz 

    -->  (5) cd midisport-firmware-1.2 

    -->  (6) ./configure 

    -->  (7) make 

         (8) As root, type: 
    -->      make install 
 
         (9) Now, when you plug in a MidiSport 1x1 USB device on the computer
             the green LED near the USB output should start pulsing, which 
             indicates that it is activated properly and ready to use with 
             the ALSA system.  You can check that you have ALSA installed 
             on your system by running this command: 
                 cat /proc/alsa/version 
             Which will reply something like this: 
                 Advanced Linux Sound Architecture Driver Version 1.0.14 (Thu May 31 09:03:25 2007 UTC).

22.3  When you have done these things, you need to give Dmuse the information
      it needs to talk to the MIDI port.  This information is transmitted in
      your INIT file in Record 48.  When Dmuse is first installed, this record
      has an "N" in column 17.  This must be changed to a "Y", indicating that
      MIDI has been installed.  There must be a space in column 18.  This is
      followed by the port name and the name of the hardware you are using.  
      The length of the hardware name need only be long enough to identify 
      the hardware uniquely.  The port name and the hardware name must be 
      separated by a semi-colon, and must be in double quotes.  Dmuse also 
      needs to know the path location of the "midi_in" program.  On my system,
      Record 48 of the INIT file looks like this: 

      MIDI Installed: Y "hw:1,0,0;MidiSport"  "/usr/local/apps/disp" 

      Craig Stuart Sapp has written a program called alsarawportlist 
      which should be in the same path location as the program midi_in.  
      If you run this program, and if the ALSA system is installed on 
      your computer, You will get a list of MIDI devices and hardware 
      names on your computer, from which you can construct Record 48 
      in your INIT file.  
 

22.3  If your MIDI interface has been installed properly, you can 
      communicate with a music keyboard using the following five 
      Zbex MIDI instructions: 

          midistart
          midistop 
          midiget     str 
          midiput     str-exp
          miditime    int 

      Before explaining these instructions, it should be pointed 
      out that Dmuse can communicate with MIDI though only one Zbex 
      program at a time.  If another Zbex program (running in another 
      window) has any midi instructions whatsoever, executing or not, 
      a subsequent Zbex program containing any midi instructions 
      will compile, but will not run.  Instead, it will exit with 
      the message: 

      Unable to run program.  Midi in use by another zbex program.  

22.4  A. midistart 
                   
        Format: 

          midistart stands alone and takes no arguments.  

        Operation: 

          Midistart is the command that sets up the operation of the 
          MIDI interface.  No other midi commands will execute unless 
          and until the midistart command has been issued.  Midistart 
          starts the MIDI timer and clears the MIDI I/O buffers and 
          opens the snd_rawmidi write interface.  

          Midistart checks to see if midi is "plugged in."  If 
          you are using a usb MidiSport, for example, you might have 
          everything installed on your system, but the MidiSport might 
          be unplugged.  In this case, midistart will exit with the 
          message: 

          Unable to open the midi interface at line = xxx.  Check to see if the
            midi interface is connected.  Wait 10 seconds after connecting.    
 
          Midistart actually has two modes of execution depending on 
          whether or not the Zbex program contains the midiget 
          instruction.  Without the midiget instruction, MIDI operation 
          is relatively simple.  MIDI data is sent out to the interface 
          whenever your program executes a midiput instruction, and you 
          can select when to do this by checking the MIDI timer with the 
          miditime instruction.  With midiget, however, things get more 
          complicated.  Dmuse needs to fork, creating a child process, 
          which executes a separate program called "midi_in."  When 
          started, midi_in simply "listens" for input from the music 
          keyboard (or other MIDI device) and passes time-stamped data 
          back to Dmuse through a FIFO pipe.  If your MIDI interface 
          becomes disconnected, or midi-in from the keyboard stops working 
          for any reason, it becomes impossible for Dmuse to talk to the 
          child process (actually, the child process stops listening for 
          commands from Dmuse).  Under these conditions, if Dmuse tries to 
          stop the child (with a midistop instruction), this will freeze 
          the entire Dmuse environment (as presently coded).  This is why 
          we check first to see that the MIDI interface is plugged in 
          before we startmidi.  

          Note:  If you are an experienced Linux user, you can unfreeze 
          Dmuse by using the ps command to identify the process number of 
          the child process running "midi_in."  You can then use the kill 
          command to terminate "midi_in," which will cause a "pipe-closed" 
          signal to be sent to Dmuse and allow Dmuse to complete the 
          midistop procedure.  
 

22.5  B. midistop 

        Format and Operation: 

          The midistop command takes no argument.  Operation depends 
          on the current running state of the Zbex program.  If the 
          MIDI interface is currently active, the midistop command 
          begins by closing the snd_rawmidi write interface.  If the 
          Zbex program does not contain the midiget instruction (i.e., 
          was not opened for midiget), this completes the stop operation.  
          If Zbex was open for midiget, then these addtional steps must 
          be completed.  
 
          (1) Close the FIFO pipe  Dmuse -> midi_in.  This tells the 
              midi_in program (running in the child process) that 
              MIDI operations are to be stopped.  
 
          (2) Wait for midi_in to acknowledge this message.  (Midi_in 
              does this by closing its end of the  midi_in -> Dmuse pipe.) 
              This step also insures that the child process (midi-in) is 
              terminated.  

          (3) Close the FIFO pipe (read end of)  midi_in -> Dmuse.  

          (4) Remove the process id for the child process.  

          Note: this sequence can be fouled up if the MIDI_IN connection 
          from the MIDI device to the computer is broken.  A broken 
          connection will cause the midi_in program (running in the child 
          process) to get stuck in the loop which looks for input from 
          the MIDI device.  Since it is stuck there, it cannot check for a 
          close-pipe signal from Dmuse.  Consequently, Dmuse will hang on 
          step (2) of the above sequence.  If Dmuse simply skips step (2) 
          after a timeout, then the child process is left spinning and is 
          never terminated, and step (4) creates a "zombie." 

          midistop does not generate error messages.  

22.6  C. midiget 

        Format: 

          midiget takes one argument:  an string variable (without 
          subscripts)
 
        Operation: 
 
          midiget is used to read MIDI data that has come in from the 
          MIDI interface.  The data is actually read by the independent 
          program midi_in, running as a child process.  Midi_in reads 
          MIDI data as it comes in and appends a time stamp to each 
          midi packet (which consists of a midi command and the proper 
          number of data bytes).  

          If the MIDI interface is not currently activated for midiget 
          (with the midistart instruction), the Zbex program is terminated 
          with an error message.  Otherwise Zbex attempts to read "atomic" 
          events from the (circular) pipe buffer.  An atomic event consists 
          of a four byte time stamp, plus a complete midi packet.  Since 
          midi_in "write events" to the FIFO pipe are asynchronous with the 
          Dmuse Zbex interpreter, we cannot be certain that the data present
          in the pipe buffer at any particular time is complete.  Therefore 
          a read (and removal) from the pipe buffer is done only when a 
          complete "atomic" event is found.  
 
          If no complete atomic events are found, midiget returns the 
          null string.  Otherwise, midiget will return one or more 
          complete atomic events in the output string, provided there 
          is space available in the string.  If adding an atomic event 
          to the output string would cause its maximum length to be 
          exceeded, the atomic event is not read (not removed from the 
          circular buffer).  
 
          An atomic event should always start with an 0xff byte.  If this 
          is not the case, a flag is set, and all further data is assumed 
          to be corrupted.  In this event, all further calls to midiget will
          simply return the null string.  The data_integrety_flag is reset 
          when MIDI is stopped.  
 
          The time stamp consists of four bytes:  0xff + a 3 byte number.  
          (maximum value = 16,777,215)  The midi_in "timer" is started when 
          the midistart command is executed.  Timer units are in milliseconds.
          (16,777,215 milliseconds is approximately 4 and 2/3 hours).  
          From the standpoint of collecting data, it is the difference in 
          time stamps that matters, not the absolute value.  The 4 hour 
          upper limit on time stamp should not present a logical problem 
          to any running Zbex program.  

          A midi packet consists of a midi command (one byte) and from 
          0 to 2 data bytes, depending on the command.  Midiget will read 
          as many atomic events as permitted by the size of the input 
          string, and if possible all atomic events in the (circular) pipe 
          buffer.  

22.7  D. midiput 

        Format: 

          midiput takes one argument:  a string expression (subscripts allowed)
          
        Operation: 

          midiput is used to send MIDI data directly to the MIDI interface 
          with no time delay.  
 
          If the MIDI interface is not currently activated (with the 
          midistart instruction), the Zbex program is terminated with 
          an error message.  Otherwise, contents of the string is 
          transferred to the (non-circular) MIDI-out buffer, space 
          permitting.  Cycling of the Zbex program is then stopped 
          and control is returned to the Dmuse main module, which 
          "writes" the contents of the MIDI-out buffer to the MIDI 
          interface and resets the buffer pointers.  

22.8  E. miditime 

        Format: 

          miditime takes one integer variable as vehicle for input.  

        Operation: 

          One of the things midistart does when it is first called is 
          to read the time-of-day.  A call to miditime also reads the 
          time-of-day.  A difference is calculated and the number of 
          transpired milliseconds is computed.  This number is returned 
          in the integer variable.  

          If miditime is called without first calling midistart, the 
          Zbex program is terminated with an error message.  
 
        Use of miditime: 
 
          Miditime is used in conjunction with midiput to send music 
          (MIDI commands) out to the midi interface.  

          A MIDI file consists of a series of MIDI commands, each one 
          preceded by a time delay (which can be zero).  A the top of 
          a MIDI file are two numbers: the number of divisions per 
          quarter note (e.g. 240), and the tempo number (measured as the 
          number of u-secs per quarter note).  The time delay numbers 
          are measured in divisions.  Typically, the number of divisions 
          per quarter note does not change in a MIDI file.  The tempo 
          number, however, can be changed.  This is how tempos are 
          changed in a MIDI performance.  

          In order for a Zbex program to send out MIDI commands at the 
          proper time, there needs to be a delay procedure which can 
          "spin" for a specified length of time (i.e., a specified 
          interval called a division or a "tick."  And what is a "tick" 
          in terms of milliseconds?  Well this depends on the tempo.  

          If the tempo is 60 quarters per minute, which is one quarter 
          per 1,000 milli-secs, and there are 240 divisions in a quarter, 
          then one division is 1,000 / 240 = 4.16667 milli-secs.  Since 
          this unit contains a significant rounding error (to the nearest 
          integer), we cannot use it directly, but must work with a 
          cumulative counter.  We define the procedure tick as follows: 
 
              procedure tick 
                int i,j,k 

                ++midi_ticks 

                k = midi_ticks * 2000 / tempo_mult 
                loop 
                  miditime i 
                  if i > k 
                    return 
                  end 
                repeat 
              return 
 
          where tempo_mult is set as follows, assuming the string 
          temp{1,6} contains the tempo setting MIDI command, and 
          divspq = number of divisions per quarter note: 
 
              if temp{1} = chr(255) and temp{2} = chr(0x51) 
                s4 = temp{3,4}          <-- n-sec. per quarter 
                usec_pq = ors(s4) 
                h = 2000000 * divspq / usec_pq
                putc ~h  divisions per (2 seconds) 
                tempo_mult = h 
                temp = temp{7..} 
              end 

          Note that this code resolves the tempo_mult variable to the number
          of divisions per (2 second), which gives us some extra resolution 
          in terms of tempo variances.  

          In this scheme, the midi_ticks "clock" keeps increasing, so 
          fractions parts of the fundamental unit (division) also 
          accumulate.  And since most time intervals that we can detect 
          are in the range of 10 or more divisions, the resolution error 
          goes undetected.  The "spin" portion of the procedure simply 
          makes repeated calls to miditime until the "timer" exceeds the 
          target.   And the tick procedure is called in the main program 
          for the exact number divisions that are requested as a time delay.
 
          Note:  This code only works when the tempo is constant.  
          A change in the tempo number will change the tempo_mult 
          variable and completely screw-up the value of k in the 
          procedure tick.  In order to fix this problem, the value 
          of k would need to be the sum of a "total elapsed time at 
          the point where a new tempo is set" plus "the increment 
          since that setting."  You write the code!  
 


    ──────────────────────────────────────────────────────────────────