╔════════════════════════════════════════╗
                                                       
                     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!



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