MITSVTL2.WS4
------------

- "MITS VTL-2 -- A Very Tiny Language for the Altair 8800"
   Frank McCoy,  1976

(Retyped by Emmanuel ROCHE.)


Introduction
------------

The statements that may be entered as input to the VTL-2 interpreter are of  2 
types:

     1. Direct statements
        which have no line number, and are executed immediately after they are 
        entered.

     2. Program statements
        which  are  used to build a program, and are not  executed  until  the 
        program is run. Program statements must have line numbers  identifying 
        their location in the program.

VTL-2 is simple enough for the beginner to use easily, and yet powerful enough 
to  serve  the  needs  of the most  advanced  users.  The  subscripted  memory 
reference  commands,  and  full  input/output format  control,  make  VTL-2  a 
versatile language suitable for solving a wide range of computer problems.


1. Preliminary concepts
-----------------------

Line  numbers must precede each program statement, and must be separated  from 
that statement by a single blank space. These numbers must be in the range  of 
1-65535.  (ROCHE> Contrary to most small "integer interpreters"  (like  PATB), 
VTL-2 uses 16-bit numbers WITHOUT sign bit... That's why it is able to address 
the  full 64K of addressing space of the Intel 8080 CPU.) Line number zero  is 
not permitted. Each line ends with a Carriage Return, and must be less than 73 
characters  long.  (ROCHE>  The width of the ASR-33 Teletype,  which  was  the 
standard terminal used by the first microcomputers. Later, screens allowed  16 
lines of 64 characters, then 25 lines of 80 characters.)

It  is recommended that lines be numbered in steps of ten (10, 20, 30,  etc.), 
so that new statements may be inserted, if necessary.

Variables  may  be represented by any single alphabetic or  special  character 
(e.g., punctuation mark: !"#$%&'()=-+*:;?/>.<,[]). Most of these are available 
for  the  user to define as he wishes. A few of the variable  names,  however, 
have  been set aside for special purposes. These so-called "system  variables" 
will be discussed in detail below.

The value assigned to a variable may be either a numeric value in the range 0-
65535, or a single ASCII character (including control characters). Numeric and 
string  values  may be freely interchanged, in which case the  characters  are 
equivalent  to the decimal value of their ASCII code representation. Thus,  it 
becomes  possible  to add 1 to the letter "A", giving as a result  the  letter 
"B".

The arithmetic operations permitted for use in expressions are:

      + (Addition)
      - (Subtraction)
      * (Multiplication)
      / (Division)
      = (Test for equality)
      > (Test for greater than or equal to)
      < (Test for less than)

The  test operations (equal to, greater than or equal to, and less  than)  all 
return  a value of zero if the test fails, and a value of one if the  test  is 
successful.  (ROCHE> Note that this is non-standard. Commonly, FALSE =  0  and 
TRUE  =  NOT  FALSE = -1 (that is to say: -32,768 for  a  16-bit  word).  This 
unusual  value  (TRUE  = 1) was chosen to implement  computed  GOTO/GOSUB,  as 
explained below.)

Expressions  in  VTL-2 may contain any number of variables or  numeric  values 
(literals) connected by any of the above operation symbols. Parentheses may be 
used to alter the order of execution of the operations. If no parentheses  are 
included, the operations proceed in strictly left-to-right order.

The  value  resulting from the expression must be assigned  to  some  variable 
name.  This is done with the equal sign. Note that the symbol has 2  meanings, 
depending  on where it occurs in the expression. The expression "A=B=C"  means 
test  B  and C for equality. If they are equal, put a one in A;  if  they  are 
unequal, put a zero in A.

Some examples of valid arithmetic expressions would be:

        Y=A*(X*X)+B*X+C         With left-to-right execution, this
                                is equivalent to: Y=(A*X*X+B)*X+C
        Y=(A*X*X)+(B*X)+C       Which is equivalent to: AX2+BX+C

Notice  how  the absence of parentheses around the quantity B*X in  the  first 
expression has completely altered its meaning. Keep the left-to-right order in 
mind and, when in doubt, use parentheses to control the order of evaluation.


2. System variables
-------------------

In  order  to conserve space, and to provide a more consistent  syntax,  VTL-2 
uses "system variables" to accomplish functions usually done with special  key 
words  in  other  languages.  This convention  is  probably  the  single  most 
important reason for its tiny size.

These special variables are used for such functions as the BASIC "PRINT, GOTO, 
GOSUB, RETURN, IF, and RANDOM" functions.


2.1 "number" ("#")
------------

The system variable "number" or "pound sign" ("#") represents the line  number 
of  the line being executed. Until the statement has been completed,  it  will 
contain the current line number. So that the statement:

        100 A=#

is equivalent to simply writing "100 A=100". After completion of a line,  this 
variable  will contain the number of the next line to be executed. If  nothing 
is done to the variable, this will be the next line in the program text. If  a 
statement changes #, however, the next line executed will be the line with the 
number  that  matches  the value of #. Thus, the variable #  may  be  used  to 
transfer control to a different part of the program.

This is the VTL-2 equivalent of the BASIC "GOTO" statement. For example:

        #=300
means
        "GOTO 300"

If  the  # variable should ever be set to zero by some statement,  this  value 
will be ignored, and the program will proceed as if no change had taken place. 
This  fact allow us to write "IF" statements in VTL-2. Consider the  following 
example:

        10 X=1          Set X equal to 1
        20 #=(X=25)*50  If X=25 goto 50
        30 X=X+1        Add 1 to X
        40 #=20         Goto 20
        50 ...          Continue

Notice  that the quantity (X=25) will have the value one if it is TRUE that  X 
is equal to 25, and the value zero if it is FALSE. When this logical value  is 
multiplied  times 50, the result will be either zero, or 50. If it is 50,  the 
statement causes a "GOTO 50" to occur. If the value is zero, a "GOTO 0" (which 
is  a  dummy  operation)  causes the next statement down  (number  30)  to  be 
executed.

Taking advantage of left-to-right evaluation, 2 bytes of memory could be saved 
by writing:

        20 #=X=25*50


2.2 "exclamation point" ("!")
-----------------------

Each and every time the value of # is changed by a program statement, the old-
value+1  is saved in the system variable "exclamation point" ("!").  In  other 
words,  after executing a GOTO, the line number of the line that  follows  the 
GOTO  is saved, so that a subroutine will know which program statement  called 
it, and will know where to return when finished. Thus, the # variable is  used 
for both GOTO and GOSUB operations. For example:

        10 X=1
        20 #=100
        30 X=2
        40 #=100
        50 X=3
        60 #=100
       (...)
        100 X=X*X
        110 #=!         (Goto back where you came from) (=BASIC "RETURN")

In  this example, control proceeds from line 20 to line 100. After that,  line 
110  causes  control  to  return to line 30. When line  40  is  executed,  the 
subroutine at 100 will return to line 50.

The  actual value stored in the ! variable is (old line number+1),  but  VTL-2 
(if it does not find the exact line number it is searching for) will take  the 
next  higher  line number. Therefore, if a program statement says  "#=52"  and 
there  are lines numbered 50 and 60 (with nothing in between), control  passes 
to the next higher line number, 60.


2.3 "question mark" ("?")
-------------------

The  system variable "question mark" ("?") represents the user's terminal.  It 
can  be  either an input, or an output, depending on which side of  the  equal 
sign it appears.

The  statement "?=A" is interpreted as "PRINT A", and the statement  "X=?"  is 
interpreted  as "INPUT X". Note that the "?" may be included anywhere  in  the 
expression. For example, the program:

        10 ?="Enter three values"
        20 A=(?+?+?)/3
        30 ?="The average is"
        40 ?=A

will request 3 inputs while executing line 20.

When  typing  in a reply to a request for input, the user may enter any  of  3 
different types of data:

     1. A decimal number
     2. A variable name
     3. Any valid VTL-2 expression

Thus,  for  example,  the  user  may reply  with  such  things  as  "1004"  or 
"A+B*(9/X)".  In each case, the expression is completely evaluated before  the 
result  is passed to the input statement. The only exception is that  you  are 
not  allowed to respond with another question mark, as this will mess  up  the 
line pointer in the interpreter, causing it to return an improper value.

If  a  Carriage Return, with no value, is typed in response to a  request  for 
input,  the interpreter will return some undefined value. Therefore,  this  is 
not recommended.

When  the  question  mark  is on the left side of the  first  equal  sign,  it 
represents  a PRINT statement. When this occurs, either of 2 different  things 
may be on the right side of the equal sign:

     1. Any valid VTL-2 expression (as defined above).
     2. A string of characters enclosed in quote marks (").

When  the  expression is a numeric one, the value is computed and  printed  as 
left-adjusted, unsigned, decimal integer, with no leading or trailing  blanks. 
A Carriage Return never follows the printing of a decimal value.

When  the  expression  is  a quoted character string,  the  actual  string  of 
characters is printed with no leading or trailing blanks. A Carriage Return  - 
Line  Feed sequence will follow the printing of a string, unless  a  semicolon 
follows the closing quote.

The  omission  of  leading  and trailing blanks  allows  complete  control  of 
formatting printed output. For example, the program:

        10 ?=50/2
        20 ?=",";
        30 ?=265+3
        40 ?=".";
        50 ?=16

will  print  the  line: "25,268.16" with no spaces between  the  pieces.  This 
feature   is  most  often  used  in  floating-point   and   multiple-precision 
subroutines (see "FACTORIALS" in the sample programs section).

If, at any time, it is desired to have a Carriage Return - Line Feed  printed, 
the statement ?="" will accomplish this.


2.4 "per cent" ("%")
--------------

The  system variable "per cent" ("%") contains the value of the  remainder  of 
the  last  divide operation. This value will remain the same  until  the  next 
divide operation.


2.5 "apostrophe" (')
----------------

The  system variable "apostrophe" (') represents a random number. This  number 
will have an unpredictable value in the range 0-65535. If called twice on  the 
same  line,  the  same value will be returned both times.  The  value  of  the 
variable is scrambled each time any statement is executed. Therefore, for best 
results,  it  is  highly recommended that at least one  other  computation  be 
performed  before  the value is again called for. This may even  be  a  simple 
dummy statement, such as "Z=Z+7". For an example of this, see "DON'T LOSE YOUR 
AT" in the sample programs section.


2.6 "dollar sign" ("$")
-----------------

In  addition to decimal numeric input and output, the system variable  "dollar 
sign"  ("$")  is  used  to input and output single  characters.  As  with  the 
question mark variable, "A=$" means "Input a single ASCII character, and place 
its  numeric  value  in A". Similarly, "$=X" means  "Print  the  single  ASCII 
character whose value is stored in X". For example, the program:

        10 A=65
        20 $=A
        30 A=A+1
        40 #=A<91*20
        50 ?=""

will  print  out, as one continuous string, all the letters of  the  alphabet: 
ABCDEFGHIJKLMNOPQRSTUVWXYZ.  If  you  wish to find  out  what  decimal  values 
correspond to which characters, these can be found in the "MITS 8800 Reference 
Manual",  or  simply computed by typing the direct statement: "?=$"  and  then 
entering the character whose decimal value is to be found.


2.7 "asterisk" ("*")
--------------

The  system  variable  "asterisk" ("*") represents the  memory  size  of  your 
computer. For a 1-K system, this would be 1024; for a 17-K system, this  would 
be  17*1024. When the machine is first turned on, and VTL-2 is called for  the 
first  time,  the user must type in the value of the * variable  as  a  direct 
statement. "*=1024", for example.

If  a  person  wishes  to  allot  space  for  user-defined  machine   language 
subroutines, then the variable * is set equal to the bottom of the first  byte 
required by the user-defined routine.


2.8 "period" (".")
------------

In  the MITS 8800 version only, the system variable "period" (".") is used  to 
control  the  terminal. If the number is odd (bit 0 high), then  the  computer 
will  "echo"  or print. If the number is even, the computer will not  echo  or 
print. If the number divided by 2 is odd (bit 1 high), then the computer  will 
accept  input  from  the cassette. If the number divided by 4 is  odd  (bit  2 
high),  then  the computer will output to the cassette in  parallel  with  the 
terminal.


2.9 "comma" (",")
-----------

Also  in  the  MITS  8800 version only,  the  system  variable  "comma"  (",") 
represents the number of nulls that the computer will put out to the  terminal 
after every Carriage Return - Line Feed.

In  general,  on the MITS 8800 version, in normal operation, "comma"  is  zero 
(",=0"), and period is one (".=1"). However, to read from cassette, you  would 
set "period" equal to 2 and "comma" equal to 0 (".=2" and ",=0"). This is done 
so  that  the  terminal does not slow up the computer  in  reading  the  tape. 
However, if the terminal's speed is greater than 300 bauds, then "period"  may 
be set to 3, so that you can see the program as it comes in.

To  store a program on cassette, set the nulls to 4 (",=4"), set the echo  OFF 
and the cassette output ON (".=4"), then type "0" but do not hit the  Carriage 
Return. Start the cassette tape running on record, and then hit RETURN (ROCHE> 
The  ASR-33  Teletype  key  labeled "RETURN". "ENTER"  on  IBM  Clowns.).  The 
computer will then save its current program on tape.

To read a program in from cassette, set the nulls to 0 (",=0"), turn the  echo 
OFF  and the cassette input ON (".=2"), type "1" with no Carriage Return,  and 
start the tape recorder.

To save a program on (the ASR-33 Teletype) paper tape, set ".=1", ",=3",  type 
"0", punch 20 or more nulls, and then hit the Carriage Return. 

To  read in from paper tape, set ".=1", ",=0", type "1", start the  reader  on 
the nulls of the paper tape, and start the reader to run.


2.10 "up arrow" ("^")
---------------

Finally,  on the MITS 8800 version only, the system variable "up arrow"  ("^") 
is  the number of the terminal option currently running. If the number is  odd 
(bit  0 high), then the terminal will be set for a MITS 2SIO board at port  18 
else,  if the number divided by 8 is odd (bit 3 high), the terminal will be  a 
MITS  2SIO board at port 16. Finally, if neither situation is TRUE (bit 3  and 
bit 0 low), then the terminal will be set for a MITS single-SIO board at  port 
0.


2.11 "ampersand" ("&")
----------------

The  system variable "ampersand" ("&") represents the next available  byte  of 
memory in the program buffer. When first calling VTL-2, or when it is  desired 
to  erase  the  present program, this must be initialized  to  the  value  320 
("&=320") as a direct statement.

At any given time, the user may find out how much of his memory still  remains 
unused  by  typing  "?=*-&". This will cause the system to  respond  with  the 
number  of bytes remaining. A minimum of at least 3 bytes are needed  for  any 
line  of  VTL-2.  The line numbers are saved in binary, and  require  2  bytes 
regardless  of  their decimal values. The lines "1 X=Y" and "65000  X=Y"  both 
take up an identical 7 bytes of memory, and are examples of the normal minimum 
valid VTL-2 line.

Any  memory remaining past the end of a program may be used as array  storage. 
This array storage may be used for saving numeric or string values. The  array 
does  not have a name, since there is only one, but it can be divided up  into 
several  pieces, and used for different groups of data. (See "CIPHER"  in  the 
sample programs section.)

A  subscript expression is identified by a colon and a right parenthesis.  The 
colon  marks the beginning of the expression, and the right parenthesis  marks 
the  end.  Thus, for example, ":1)=0" places a zero in the first  2-byte  word 
past the end of program, and ":2+7)=A" places the value of A in the 9th 2-byte 
word past the end of the program.

Subscripts  should not be allowed to be less than one (1) as this  will  point 
the subscript into the program, and could cause it to be wiped out. (ROCHE> In 
BASIC: "OPTION BASE 1" only.)

Subscript  expressions may be any valid VTL-2 numeric expressions. This  valid 
example should clarify the use of subscript expressions:

        10 I=1          Set pointer
        20 :I)=$        Input a character to next array word
        30 #=:I)=13*60  Goto 60 if it is a Carriage Return character
        40 I=I+1        Point to next array word
        50 #=20         Go get another character
        60 ?=""         Print Carriage Return - Line Feed
        70 I=1          Reset pointer
        80 $=:I)        Print Ith character
        90 #=:I)=13*120 If Carriage Return, then goto 120
        100 I=I+1       Point to next character
        110 #=80        Go get next character
        120 ?=""        Print Carriage Return - Line Feed

The  above  example will read in any string of characters typed by  the  user, 
such  as a sentence, or paragraph, until a Carriage Return is typed.  It  will 
then echo back the complete string as it was typed.

For  further examples, study the game programs which use character input,  and 
those that have arrays representing the playing board. These will be found  in 
the sample programs section.

Since subscripts refer to 2-byte words, and since values as large as 65535 are 
allowed  as  subscripts,  it is possible that large values  in  the  subscript 
expression may "wrap around" the end of memory, and reach locations within the 
program text. Therefore, there is a danger that VTL-2 programs using  computed 
subscripts may "clobber" themselves. On the other hand, this also means that a 
VTL-2 program may modify itself, although this practice is not recommended.


2.12 "greater than" (">")
-------------------

The system variable "greater than" (">") is used to pass a value to a  machine 
language  subroutine. When encountered on the left side of the equal sign  (10 
> >=expr), the expression is evaluated, the value placed as a 16-bits integer in 
the (Intel 8080) B and C registers, and a "call zero" command generated.  (See 
MITS 8800 manual on subroutine handling and restarts.)

At  the  conclusion  of the machine language subroutine, a  (Intel  8080)  RET 
instruction returns control to VTL-2, and places the value found in the B &  C 
registers into the system variable >.


2.13 END statement
------------------

There  is  no  "end"  statement in VTL-2.  The  interpreter  simply  continues 
sequentially  through the program, until it runs out of lines to  execute,  or 
until a statement is encountered which will try to transfer control to a  line 
that is greater in number than any in the program.


3. MITS Operational characteristics
-----------------------------------

When  the  MITS  8800 is first turned ON, the following things  must  be  done 
before  any VTL-2 program may be entered. First, lift the STOP and  the  RESET 
switches simultaneously, then release the RESET switch, then the STOP  switch. 
Next,  set the address switches to address F800 hex. A15 through A11  up,  A10 
through A0 down. Then, lift the EXAMINE switch.

Finally,  set  the terminal option on the sense switches. If you have  a  MITS 
single-SIO board at port 0, then set A11 and A3 down. If you have a MITS  2SIO 
board, then set A11 high, and A8 high if you want port 18, or low if you  want 
port 16.

After setting the port option, press "RUN".

Once  VTL-2 is in control, the message "OK" will be printed. The next step  is 
to  set  your memory size. This is done by typing "*=1024" for a  1-K  system, 
"*=1024*17" for a 17-K system, and so on.

Finally,  the  user  must set the "end of program" pointer. This  is  done  by 
typing  "&=320".  This  number will be the same for  all  MITS  8800  systems, 
regardless of memory size. (ROCHE> 320 = 0140H = Beginning of user program.)

VTL-2 is now ready to begin accepting programs and commands. If, at any  time, 
it  is desired to erase the program in memory, repeat the last 2  steps  given 
above. The will reinitialize the VTL-2 program buffer space.

When  a program line is entered, it will be inserted into its proper place  in 
the program text. The line number indicates where it will be inserted. If  the 
line  just  typed has the same line number as a line already  in  the  program 
text,  the old line will be replaced by the new line. If the line number  only 
is typed, followed immediately by a Carriage Return, the line with that number 
will be deleted.

While  typing  in program lines, the system should single space, and  make  no 
replies  to  line entered. If, after typing a line, the system  double  spaces 
down  and  prints  "OK",  that indicates that  there  was  not  enough  memory 
available to insert the new line just typed.

The  user  may check to see how much memory is still available by  typing  the 
direct  statement (no line number) "?=*-&". The system will respond  with  the 
number of unused bytes remaining.

While typing in a line, the back-arrow key ("<--", SHIFT-O on some  terminals, 
or  "underline"  ("_") on others) will cause the last character  typed  to  be 
deleted  from the input buffer. The character will still appear on the  screen 
or printout, but will no longer be in memory. Thus, the line "A=B*C__+N"  goes 
in  as  "A=B+N",  where  the "*C" was erased in memory  by  the  2  back-arrow 
characters (here represented by 2 "underlines").

At  any time before hitting RETURN, the entire line may be erased from  memory 
by typing the at-sign character ("@") (SHIFT-O or "CANCEL" on some terminals.)

Typing the single character zero ("0"), followed by a Carriage Return,  causes 
the system to print out a listing of the program. VTL-2 programs may be  saved 
on  cassette  or  paper  tape. For paper tape, simply type  "0"  and  punch  a 
listing.  (ROCHE>  There  are no instructions on how to  save/load  a  program 
to/from a cassette tape.)

While printing is taking place, whether as a program listing or as output from 
a  program, the operation may be cancelled, and control returned to VTL-2,  by 
pressing Control-C. When this is done, the system completes its current  print 
statement, and then prints "OK" to acknowledge the interruption.

In  addition  to  this,  any other  key  (preferably  a  non-printing  control 
character,  such as Control-A) may be pressed. This will cause the  system  to 
temporarily suspend operation, and wait for another key to be pressed. (Again, 
preferably a non-printing character.)

This  feature  allows  users with video terminals to  list  their  programs  a 
section at a time, hitting Control-A to stop the listing, and hitting it again 
to resume listing.

Note  that these characters also affect printing being done by a program.  You 
may temporarily halt your program with a Control-A, and start it up again with 
another  Control-A.  These  keys  work only during  printing  which  uses  the 
question  mark  system variable. String printing with the dollar  sign  system 
variable will not interrupt in this manner. This allows the user the option of 
making his program interruptable or non-interruptable.

Should  an uninterruptable program become "locked up" in a loop, the only  way 
to  break out is with the front panel "RESET" switch. When this is  done,  the 
"J  F800"  must be typed to return to VTL-2, but the  remaining  steps  listed 
above (to clear the program) must not be performed!

To  run a program, the user simply types the direct command "#=1" ("GOTO  1"). 
This  causes the system to find the lowest numbered line, and begin  executing 
there.  If  it is desired to begin executing at some other  line  number,  say 
1000, simply type "#=1000", or whatever line number is desired.

Comments  may  be  inserted  on  any line  by  preceding  them  with  a  right 
parenthesis.  This symbol must follow the expression on the line  immediately, 
with no blanks in between. This causes the system to stop evaluating the line, 
and go on to the next line. If a line is to contain only a comment, the  first 
character on the line must be a right parenthesis. Examples:

        10 ) Frank McCoy was here...
        350 #=*-&/2<L*380) End program when memory becomes low.

There  are no error messages in VTL-2. If an expression is wrong, the  results 
of  evaluating  that  expression will also be wrong.  In  other  words,  VTL-2 
assumes that you know what you are doing, and will do its best to execute  any 
statement  that  you  give it. This leaves wide latitude  for  trying  various 
programming "tricks", but also leaves the responsibility of verifying  program 
accuracy with the programmer.


3.1 Sample of programming
-------------------------

When starting:

1) Lift the "RUN/HALT" switch
2) Turn the computer ON
3) Lift the "RESET" switch
4) Turn the "RUN/HALT" switch to "RUN"
5) The monitor should reply with a period (".")
6) Type in J F800
7) VTL-2 should reply "OK"

OK              VTL-2 prompt
*=1024          Set memory size to 1024 bytes

OK              VTL-2 prompt
&=320           Reset program pointer

OK              VTL-2 prompt
10 A=0          Set A equal to zero
20 B=1          Set B equal to one
30 ?=A          Print the value of A
40 ?="! = ";    Print "factorial equals"
50 ?=B          Print the value of B
60 ?=""         Print a Carriage Return - Line Feed
70 A=A+1        Increment A
80 B=A*B        Multiply B times A
90 #=A<9*30     If A is less than 9 then goto line # 30
#=10            Execute program
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320

OK              VTL-2 prompt, saying it is done.


4. List of features
-------------------

4.1 Variables
-------------

Variables       Meaning
---------       -------
   A-Z          Common variables. Use freely for storing values.

System vars     Meaning
-----------     -------
     #          Line number
     !          "RETURN" address
                (Points to the line # after the last #= statement.)
     ?          "PRINT" statement when on left of equal sign
                ("INPUT" statement when on right of equal sign.)
     %          Remainder after the last divide operation
     '          Random number
     $          Single character string (input or output)
     *          Points to end of memory
     .          Terminal/cassette echo switch
     ,          Number of nulls after cr/lf on TTY
     ^          Number of MITS terminal option
     &          Points to the last byte of program
     >          Machine language subroutine
     "          Pointer for literal print statements
     (          Sets start of parenthesized expression
     )          End
                (Sets end of line)
                (Sets end of parenthesized expression)
                (Sets end of array description)
                (Also used for "REMARK" statement)
     :          Defines start of array description
     ;          When following a literal print statement,
                  says: "Do not print Carriage Return - Line Feed".

  -=;+</][      May be used freely as standard variables, but
                  use is not recommended for legibility reasons.


4.2 Operators
-------------

Operators       Meaning
---------       -------
    +           Add to previous value
    -           Subtract from previous value
    *           Multiply times previous value
    /           Divide previous value by
    =           Is previous value equal to (yes=1, no=0)
    <           Is previous value less than (yes=1, no=0)
    >           Is previous value equal to or greater than (yes=1, no=0)
                (The default operator is the "less than" test.)


5. Sample programs
------------------

5.1 Hurkle
----------

100 ?=""
110 ?="A Hurkle is hiding on a"
120 ?="10 by 10 grid. Homebase"
130 ?="on the grid is point 00"
140 ?="and a gridpoint is any"
150 ?="pair of whole numbers."
160 ?="Try to guess the Hurkle's"
170 ?="gridpoint. You get 5 guesses."
180 ?=""
190 R='/100*0+%
200 A=R/10
210 B=%
220 K=1
230 ?="Guess #";
240 ?=K
250 ?="  ?";
260 X=?/10
270 Y=%
275 #=X>9*580)Terminate program
280 ?=""
290 #=X*10+Y=R*540
300 K=K+1
310 #=K=6*440
320 ?="Go ";
330 #=Y=B*370+(Y<B*360)
340 ?="South";
350 #=370
360 ?="North";
370 #=X=A*410+(X<A*400)
380 ?="West";
390 #=410
400 ?="East";
410 ?=""
420 ?=""
430 #=230
440 ?=""
450 ?="Sorry, that's 5 guesses..."
460 ?="The Hurkle is at ";
470 ?=A
480 ?=B
490 ?=""
500 ?=""
510 ?="Let's play again."
520 ?="Hurkle is hiding"
530 #=180
540 ?="You found him in ";
550 ?=K
560 ?=" guesses."
570 #=490


5.2 Time of day digital clock
-----------------------------

For 300-baud terminals
----------------------

10 ?="Hour ?";
20 H=?
30 ?="Minute ?";
40 M=?
50 ?="Second ?";
60 S=?
70 ?="Ready"
80 A=$
90 S=S+1
100 M=S/60+M
110 S=%
120 H=M/60+H
130 M=%
140 H=H/24*0+%
150 ?="Time: ";
160 ?=H/10
170 ?=%
180 ?=":";
190 ?=M/10
200 ?=%
210 ?=":";
220 ?=S/10
230 ?=%
240 $=13
250 A=B
260 T=31
270 T=T-1
280 #=T=0*90
290 #=270


For 110-baud terminals
----------------------

10 ?="Hour ?";
20 H=?
30 ?="Minute ?";
40 M=?
50 ?="Second ?";
60 S=?
70 ?="Ready"
80 A=$
90 S=S+1
100 M=S/60+M
110 S=%
120 H=M/60+H
130 M=%
140 H=H/24*0+%
150 ?=H/10
160 ?=%
170 ?=":";
180 ?=M/10
190 ?=%
200 ?=":";
210 ?=S/10
220 ?=%
230 $=13
240 A=B
250 A=B
260 A=B
270 A=B+B
280 T=14
290 T=T-1
300 #=T=0*90
310 #=290


5.3 Factorials
--------------

Calculates factorials until it runs out of memory.
For 1-K of memory, this is about 208!

10 A=1
20 L=2
30 :1)=1
40 I=2
50 :I)=0
60 I=I+1
70 #=L>I*50
80 ?=""
90 ?=""
100 ?=A
110 ?="! ="
120 ?=""
130 I=L+1
140 I=I-1
150 #=:I)=0*140
160 ?=:I)
170 I=I-1
180 #=I=0*220
190 ?=:I)/10
200 ?=%
210 #=170
220 A=A+1
230 I=1
240 C=0
250 X=:I)
260 :I)=A*X
270 #=:I)<X*320
280 :I)=:I)+C
290 C=:I)/100
300 :I)=%
310 I=I+1
320 #=L>I*250
330 #=C=0*80
340 L=L+1
350 #=*-&/2<L*380)End program when memory becomes low.
360 :I)=C
370 #=290


5.4 Weekday
-----------

10 #=440
20 ?="Day of the week"
30 ?=""
40 ?="Month?  ";
50 M=?
60 #=M>13*40
70 #=M=0*40
80 ?="Day of month?  ";
90 D=?
100 ?="Year?  "
110 Y=?
120 #=Y>1800*230
130 #=Y<100*150
140 #=70
150 ?=""
160 ?="Is that 19";
170 ?=Y
180 ?="?  ";
190 K=$
200 #=K=89=0*70
210 ?="es"
220 Y=Y+1900
230 C=Y/100
240 Y=%
250 #=Y/4*0+%=0*280
260 :1)=6
270 :2)=2
280 W=Y/4+Y+D+:M)+(2*(C=18))/7*0+%
290 #=300+(20*W)
300 ?="Sun";
310 #=430
320 ?="Mon";
330 #=430
340 ?="Tues";
350 #=430
360 ?="Wednes";
370 #=430
380 ?="Thurs";
390 #=430
400 ?="Fri";
410 #=430
420 ?="Satur";
430 ?="day"
440 :1)=0
450 :2)=3
460 :3)=3
470 :4)=6
480 :5)=1
490 :6)=4
500 :7)=6
510 :8)=2
520 :9)=5
530 :10)=0
540 :11)=3
550 :12)=5
560 #=20


5.5 Starshooter
---------------

Object of the game is to change this:

A - . . . . .
B - . . . . .
C - . . * . .
D - . . . . .
E - . . . . .
    1 2 3 4 5

to this:

A - * * * * *
B - * . . . *
C - * . . . *
D - * . . . *
E - * * * * *
    1 2 3 4 5

10 I=0
20 I=I+1
30 :I)=46
40 #=I<41*20
50 :25)=42
60 I=8
70 J=1
80 $=I-1/7+64
90 ?=" - ";
100 S=I+J
110 $=:S)
120 J=J+1
130 #=J=6*160
140 ?="  ";
150 #=100
160 I=I+7
170 ?=""
180 ?=""
190 #=I<43*70
200 ?=""
210 ?="    1 2 3 4 5"
220 ?=""
230 ?="Your move --";
240 I=42
250 I=I+1
260 :I)=$
270 #=:I)=13*320
280 #=:I)=3*580)End program if user types Control C.
290 #=:I)=65=0*250)65 was 95
300 I=I-1
310 #=260
320 A=:43)-64
330 ?=""
340 #=A>6*230
350 B=:44)-48
360 #=B>6*230
370 S=A*7+1+B
380 ?=""
390 #=:S)=42*420
400 ?="That's not a Star!"
410 #=230
420 :S)=46
430 C=S-7
440 #=520
450 C=S-1
460 #=520
470 C=S+1
480 #=520
490 C=S+7
500 #=520
510 #=60
520 ^=!
530 #=:C)=42*560
540 :C)=42
550 #=^
560 :C)=46
570 #=^


5.6 Factors of a number
-----------------------

10 ?="Number?  ";
20 N=?
30 X=N
40 ?=N
50 #=N=0*420)Stop program if N=0
60 ?=" is ";
70 #=N/2*0+%=0*140
80 D=3
90 Q=N/D
100 #=%=0*160
110 #=D>Q*300
120 D=D+2
130 #=90
140 ?="even."
150 #=10
160 ?=""
170 ?=D
180 N=Q
190 Q=N/D
200 #=%=0*220
210 #=120
220 ?="^";
230 P=1
240 N=Q
250 Q=N/D
260 P=P+1
270 #=%=0*240
280 ?=P
290 #=120
300 #=N=1*340
310 #=N=X*390
320 ?=""
330 ?=N
340 ?=""
350 ?=""
360 ?="Done."
370 ?=""
380 #=10
390 ?="prime."
400 ?=""
410 #=340


5.7 Don't lose your at!
-----------------------

Don't lose your at! by Ed Verner
Adapted to VTL-2 by Gary Shannon
(A game similar to "Bagles".)

The  object of the game is to guess the secret number picked by the  computer. 
The  number has 3 digits, no zeroes, and no digit is repeated. After you  type 
your  guess,  the computer will print an "IT" for every correct digit  in  the 
wrong position, and an "AT" for every correct digit in the right position. You 
win when you get 3 "AT"s.

Each time that you guess incorrectly, you lose 5% of the points you have left.

************** Have Fun! ************

10 T=0
20 L=0
30 ?="Don't lose your 'at'"
40 X='/9*0+%+1
50 Y='/9*0+%+1
60 #=X=Y*40
70 Z='/9*0+%+1
80 #=X=Z*40
90 #=Y=Z*40
100 ?="I've got a number."
105 L=L+1
110 P=10000
120 ?=""
130 ?="You have ";
140 ?=P/100
150 ?=".";
160 ?=%/10
170 ?=%
180 ?=" points left."
190 ?=""
200 ?="What's your guess? -- ";
210 G=?
220 A=G/100
230 B=%/10
240 C=%
250 S=600
270 #=A=Y*S
280 #=A=Z*S
290 #=B=X*S
300 #=B=Z*S
310 #=C=X*S
320 #=C=Y*S
330 K=0
340 S=620
350 #=A=X*S
360 #=B=Y*S
370 #=C=Z*S
380 #=K<3*580
390 ?=""
400 ?="You win ";
410 ?=P/100
420 ?=".";
430 ?=%/10
440 ?=%
450 ?=" points for a total of ";
460 T=T+P
490 ?=T/100
500 ?=".";
510 ?=%/10
520 ?=%
540 ?=" points in ";
550 ?=L
560 ?=" games."
570 #=30
580 P=P/20*19
590 #=120
600 ?="IT ";
610 #=!
620 ?="AT ";
630 K=K+1
640 #=!


5.8 Craps!
----------

10 T=100
20 $=22
30 ?="Craps!"
40 ?=""
50 ?="How much do you bet? - ";
60 B=?
70 #=B=0*90
80 ?="Good luck!"
90 #=B=0*480
100 #=T>B*160
110 ?="Too much!"
120 ?="You have $";
130 ?=T
140 ?=" left."
150 #=40
160 ?=""
170 ?="Roll-";
180 A=?
190 $=22
200 ?="First roll: ";
210 #=500
220 #=R=7*360
230 #=R=11*360
240 #=R<4*390
250 #=R=12*390
260 ?=""
270 ?=R
280 ?=" is your point."
290 P=R
300 ?="Roll-";
310 A=$
320 #=500
330 #=R=7*390
340 #=R=P*360
350 #=300
360 ?="You win!"
370 T=T+B
380 #=120
390 T=T-B
400 ?="You lose..."
410 #=T=0*430
420 #=120
430 ?="You are busted!"
440 ?="Move over, and let"
450 ?="the next sucker try."
460 ?=""
470 #=10
480 ?="Be serious!"
490 #=40
500 R='/6*0+%+1
510 ?=R
520 X=X+11213
530 ?=" and ";
540 S='/6*0+%+1
550 X=X*56001
560 ?=S
570 ?="  (";
580 R=R+S
590 ?=R
600 ?=")"
610 #=!


5.9 Cipher game
---------------

10 I=0
20 I=I+1
30 :I)=I+64
40 #=I<26*20
50 I=1
60 ?=""
70 M='/26*0+%+1
80 H=:M)
90 :M)=:I)
100 :I)=H
110 I=I+1
120 #=I<27*70
130 ?="Text?"
140 ?=""
150 I=27
160 :I)=$
170 #=:I)=13*220
180 #=:I)=95=0*200
190 I=I-2
200 I=I+1
210 #=160
220 ?=""
230 I=27
240 #=:I)<64*270
250 T=:I)-64
260 :I)=:T)
270 I=I+1
280 #=:I)>14*240
290 ?=""
300 ?="Code: "
310 ?=""
320 I=27
330 $=:I)
340 #=:I)=13*370
350 I=I+1
360 #=330
370 ?=""
380 ?="Switch? - ";
390 A=$
400 B=$
410 #=B=64*370
420 I=27
430 #=:I)=A*490
440 #=:I)=B=0*460
450 :I)=A
460 I=I+1
470 #=:I)=13*290
480 #=430
490 :I)=B
500 #=460


5.10 Phrase sort
----------------

10 $=22
20 I=0
30 I=I+1
40 :I)=$
50 L=:I)=95*2
60 I=I-L
70 #=:I)>14*30
80 ?=""
90 I=1
100 K=I
110 J=K
120 #=:K)=32*160
130 #=:J)=32*150
140 #=:K)>:J)*160
150 J=K
160 K=K+1
170 #=:K)>14*120
180 H=:I)
190 :I)=:J)
200 :J)=H
210 I=I+1
220 #=:I)>14*100
230 I=0
240 I=I+1
250 $=:I)
260 #=:I)>14*240
270 ?=""


5.11 Life
---------

Needs 2-K of memory for operation.

10 #=710
20 Z=!
30 #=X*Y=0*(X=20)+1/2*Z
40 #=Y=20*Z
50 L=Y-1*19+X
60 #=:L)=10+(:L)=32)*Z
70 S=S+1
80 #=Z
90 I=1
100 J=1
110 S=0
120 X=I-1
130 Y=J-1
140 #=T
150 X=I
160 #=T
170 X=I+1
180 #=T
190 Y=J
200 #=T
210 X=I-1
220 #=T
230 Y=J+1
240 #=T
250 X=I
260 #=T
270 X=I+1
280 #=T
290 K=J-1*19+I
300 #=S>3*340
310 #=:K)=32*400
320 :K)=S=2*42
330 #=400
340 #=S>4*380
350 #=:K)=42*400
360 :K)=10
370 #=400
380 #=:K)=32*400
390 :K)=0
400 J=J+1
410 #=J<20*110
420 I=I+1
430 #=I<20*100
440 ?=""
450 ?=""
460 P=0
470 ?=" ";
480 J=1
490 I=1
500 K=J-1*19+I
510 :K)=:K)+(:K)<30*32)
520 #=:K)=32*560
530 ?="<>";
540 P=P+1
550 #=570
560 ?="  ";
570 I=I+1
580 #=I<20*500
590 ?=""
600 ?=" ";
610 J=J+1
620 #=J<20*490
630 ?=""
640 ?="Gen= ";
650 ?=G
660 ?="    Pop= ";
670 ?=P
680 ?="------------"
690 G=G+1
700 #=90
710 I=1
720 T=20
730 J=1
740 :I-1*19+J)=32
750 J=J+1
760 #=J<20*740
770 I=I+1
780 #=I<20*730
790 G=0
800 J=1
810 ?=""
820 I=1
830 $=10
840 #=J>10*860
850 ?=" ";
860 ?=J
870 ?=" ";
880 K=J-1*19+I
890 :K)=$
900 #=:K)=13*940
910 #=:K)=64*810
920 I=I+1
930 #=I<20*880
940 :K)=32
950 J=J+1
960 #=J<20*820
970 #=440


5.12 Renumber
-------------

64000 A=#
64010 B=&
64020 C=#
64030 &=B
64040 ?="Starting #? ";
64050 D=?
64060 ?="Step size?  ";
64070 E=?
64080 &=1
64090 G=159
64100 J=0
64110 H=#+1
64120 G=&+1/2+G
64130 &=%
64140 #=:G)>A*5*(C-1)+#
64150 #=D-1>(A-1)+(J>D)>1*C
64160 :G)=D
64170 &=&+1
64180 J=D
64190 D=D+E
64200 K=#+1
64210 &=&+1
64220 #=:G)*256>1*K
64230 #=H
64240 &=B
64250 ?="Done"

Note:  This  program is relocatable, i.e., it can be renumbered  and  it  will 
still  run.  However, the step size between program steps (10 in  BASIC)  must 
remain  constant, or line 64140 will not work right. Also, the largest  number 
of  the  program to be renumbered must be less than the first  number  of  the 
RENUMBER program.


6. ROCHE> Comparison with BASIC
-------------------------------

BASIC           VTL-2
--------        -----
GOTO 100        #=100   (or GOSUB 100)
RUN             #=1
RETURN          #=!
END             #=65432 (or any big line number not used in program)
PRINT           ?=""
PRINT A         ?=A
PRINT "Hello"   ?="Hello"
PRINT CHR$(A)   $=A
INPUT$ A$       A=$
INPUT A         A=?     (The TTY was upper case only.)
Remainder       %
RND             '       (16-bit value)
MEMORY=addr     *=1024
LIST            0
NEW             &=320   (= 0140H = Start of user program)
PRINT FRE       ?=*-&
REM             ) This is a remark.
DEEK            var=:addr)
DOKE            :addr)=16-bit value


7. ROCHE> I/O ports used on the MITS Altair 8800
------------------------------------------------

0 = Single-SIO board Status
1 = Single-SIO board Data

6 = ACR-88 board Status (=cassette)
7 = ACR-88 board Data   (=cassette)

16 = 2SIO board Status
17 = 2SIO board Data
18 = 2SIO board Status
19 = 2SIO board Data

255  = The Altair "Sense Switch" is used during boot to specify  the  terminal 
port to the program.


8. ROCHE> "Period" bit fields
-----------------------------

Period (".") = xxxx$xxxxB
                    ||||
                    |||+--> 0 = No echo/print on terminal
                    |||     1 =    Echo/print on terminal (default)
                    ||+---> 1 = Input from cassette WITHOUT echo to terminal
                    |+----> 1 = Output to  cassette WITH    echo to terminal
                    +-----> 1 = Output to  cassette WITHOUT echo to terminal


9. Listing of VTL-2 interpreter
-------------------------------

; MITSVTL2.ASM
; ------------
;
; MITS VTL-2 -- A Very Tiny Language for the Altair 8800
; by Frank McCoy, Copyright 1976
;
; (Retyped by Emmanuel ROCHE.)
;
;--------------------------------
; MITS Altair 8800 I/O ports used.
;
; Input
; -----
;  0 = Single-SIO board Status
;  1 = Single-SIO board Data
;  6 = ACR-88 board Status  (=cassette)
;  7 = ACR-88 board Data    (=cassette)
; 16 = 2SIO board Status
; 17 = 2SIO board Data
; 18 = 2SIO board Status
; 19 = 2SIO board Data
; 255 = Sense Switch
;
; Output
; ------
;  1 = Single-SIO board Data
;  7 = ACR-88 board Data  (=cassette)
; 16 = 2SIO board Status
; 17 = 2SIO board Data
; 18 = 2SIO board Status
; 19 = 2SIO board Data
;
;--------------------------------
; ASCII characters used (ASR-33 Teletype).
;
ctrlC   EQU     03H             ; Abort key
backs   EQU     08H             ; Backspace
lf      EQU     0AH             ; Line Feed
cr      EQU     0DH             ; Carriage Return
quote   EQU     22H             ; Surrounds strings
dolar   EQU     24H             ; Dollar
Lparen  EQU     28H             ; Left  parenthesis
Rparen  EQU     29H             ; Right parenthesis
star    EQU     2AH             ; Multiplication
plus    EQU     2BH             ; Addition
minus   EQU     2DH             ; Subtraction
slash   EQU     2FH             ; Division
zero    EQU     30H             ; Number zero
colon   EQU     3AH             ; Start of array statement
semicol EQU     3BH             ; Semicolon after PRINT
equal   EQU     3DH             ; Equal sign
greatR  EQU     3EH             ; Greater sign
quest   EQU     3FH             ; Question mark
atsign  EQU     40H             ; Discard line
;
;--------------------------------
;       Buffer area
;-------------------------------- (SV = System Variable)
;
brkpnt  EQU     0000H           ; Address of RST 0
uparrow EQU     007CH           ; SV: Number of terminal option running
number  EQU     007EH           ; SV: Line number
exclam  EQU     0082H           ; SV: RETURN address
dollar  EQU     0086H           ; SV: Character pointer
percent EQU     008AH           ; SV: Remainder
ampers  EQU     008CH           ; SV: Next byte of user program
greater EQU     008FH           ; SV: Greater than
asterix EQU     0094H           ; SV: Memory size (in bytes)
comma   EQU     0098H           ; SV: Number of nulls after each cr/lf
period  EQU     009CH           ; SV: Terminal echo switch
evalptr EQU     00A0H           ; Eval pointer
result  EQU     00ACH           ; Result of expression evaluation
linumb  EQU     00AEH           ; Line number
curent  EQU     00B0H           ; Current line number
valvar  EQU     00B2H           ; Value of variable?
decbuf  EQU     00BEH           ; Decimal buffer ("54321")
delim   EQU     00C3H           ; Delimiter
linbuf  EQU     00C4H           ; Line buffer (TTY = 72 columns)
;
;--------------------------------
;       User program
;--------------------------------
;
        ORG     0140H           ; = 320 in decimal
;
stack   EQU     $
prgm    EQU     $
;
;--------------------------------
;       Main operating system
;--------------------------------
;
        ORG     0F800H          ; Probably a ROM
;
; Cold-Start ("Boot").
;
rstrt:  LXI     H,0003H         ; = 3
        SHLD    period          ; To see the program as it comes in
        SHLD    comma           ; Number of nulls after each cr/lf
;
; By default, VTL-2 expects that the MITS Altair 8800 system
; it is running on has a terminal via one 2SIO board, either
; at I/O port 16, or at port 18. Since it has no way to know
; which one will be used, it simply initialize the 2SIO board
; at both I/O ports...
;
        MOV     A,L             ; = 3 Resets the chip of the 2SIO board
        OUT     10H             ; I/O port 16 = 2SIO board Status
        OUT     12H             ; I/O port 18 = 2SIO board Status
        MVI     A,11H           ; = 17 Sets the clock and interrupts
        OUT     10H             ; I/O port 16 = 2SIO board Status
        OUT     12H             ; I/O port 18 = 2SIO board Status
        IN      0FFH            ; I/O port 255 = Sense Switch
        ANI     0000$1001B      ; = 9
;                    |  |
;                    |  +--> Bit 0 = 2SIO board at I/O port 18
;                    +-----> Bit 3 = 2SIO board at I/O port 16
;
; That is to say: check if there is another
; I/O board than a 2SIO at port 16 or 18,
; and store the result in UPARROW.
;
        MOV     L,A             ;
        SHLD    uparrow         ; Number of terminal option running
;
; Warm-Start (Ctrl-C).
;
; BC: ?, DE: ?, HL: ?
;
start:  LXI     SP,stack        ; Set up local stack (before user program)
        XRA     A               ; Initialize delimiter
        LXI     H,okm           ; Point to "OK" prompt
        CALL    strng           ; Display it on terminal
;
; Main loop of VTL-2 interpreter.
;
; BC: ?, DE: ?, HL: ?
;
loop:   LXI     H,0000H         ; = 0
        SHLD    dollar          ; Initialize char ptr
        CALL    cvtln           ; Was a line number inputted?
;
        JNC     stmnt           ; 0: List (display) user program statements
;
        CALL    exec            ; No: Execute direct statement
;
        JZ      start           ; Done: Return to interpreter
;
loop2:  CALL    find            ; No: Find what remains in line?
;
; Start processing "=" statements.
;
; BC: ?, DE: ?, HL: ?
;
eqstrt: JNC     start           ; No: Return to interpreter
;
        CALL    lxhn            ; Get line number, and compare with ?
;
        SHLD    dollar          ; Save char ptr
        LHLD    number          ; Get lin ptr
        INX     H               ;
        INX     H               ; 3 bytes = line number + space?
        INX     H               ; 
        CALL    exec            ; Execute direct statement
;
        XCHG                    ;
        JZ      loop3           ; Zero: ?
;
        LHLD    number          ; Get lin ptr
        CALL    lxhn            ; Get line number, and compare with ?
;
        JZ      loop3           ; Zero: ?
;
        INX     H               ; 
        SHLD    exclam          ; RETURN value
        JMP     loop2           ; Unconditional jump to loop2
;
loop3:  PUSH    H               ; Save line number
        LHLD    ampers          ; Next byte of user program
        MOV     B,H             ;
        MOV     C,L             ; Copy it into BC
        POP     H               ; Restore line number
        CALL    fnd3            ; Find what?
;
        JMP     eqstrt          ; Process "=" statement
;
;--------------------------------
; Get line number from user program.
;
lxhn:   MOV     A,M             ;
        INX     H               ;
        MOV     H,M             ;
        MOV     L,A             ;
;
; ... and falls through
;
; Compare HL with DE.
;
cphd:   MOV     A,L             ;
        CMP     E               ;
        RNZ                     ;
;
        MOV     A,H             ;
        CMP     D               ;
        RET                     ;
;
;--------------------------------
; Execute direct statements.
;
exec:   SHLD    linumb          ; Save line number?
        CALL    var2            ; ?
;
        INX     H               ;
        MOV     A,M             ;
        CALL    evil            ; 
;
        LHLD    dollar          ; Get char ptr
        MOV     A,H             ; 16-bits test if it is zero
        ORA     L               ; (Sets Zero flag)
        RET                     ;
;
;--------------------------------
; List (display) user program statements.
;
; BC: ?, DE: ?, HL: ?
;
stmnt:  SHLD    curent          ; Save current line number
        MOV     L,C             ; Copy BC (?) to HL
        MOV     H,B             ;
        SHLD    dollar          ; And save it in char ptr
        MOV     A,B             ; 16-bits test if BC is zero
        ORA     C               ; 
        JNZ     skp2            ; No: Skip what?
;
        LHLD    ampers          ; Next byte of user program
        XCHG                    ; Put it in DE
        LXI     H,prgm          ; Start of user program
;
; List (display) program on terminal.
;
lst2:   CALL    cphd            ; Check if next = start (= no program)
;
        JZ      start           ; Yes: Return to interpreter
;
        PUSH    D               ; No:
        MOV     C,M             ;
        INX     H               ; Get line number from memory
        MOV     B,M             ;   and put it into BC.
        PUSH    H               ;
        CALL    prnt2           ; Print 16-bit line number in decimal
;
        POP     H               ; 
        INX     H               ; Skip the space
        CALL    pntmsg          ; Print contents of line
;
        CALL    crlf            ; End of line
;
        POP     D               ;
        JMP     lst2            ; Loop until end of program
;
;--------------------------------
; Next text?
;
nxtxt:  LHLD    number          ; Lin ptr
        INX     H               ;
lookag: INX     H               ;
        MOV     A,M             ;
        ANA     A               ;
        JNZ     lookag          ; Look again...
;
; "Output a char" (at the end) uses those 2 instructions...
;
outd:   INX     H               ;
        RET                     ;
;
;--------------------------------
; ?
;
find:   LHLD    ampers          ; Next byte of user program
        MOV     C,L             ;
        MOV     B,H             ; Copy it to BC
        LHLD    dollar          ; Char ptr
        XCHG                    ; Copy it to DE
        LXI     H,prgm          ; PC of user program
fnd2:   SHLD    number          ; Set it equal to lin ptr
        MOV     A,C             ;
        CMP     L               ; Compare low byte of user program
        JNZ     nxtuu           ;
;
        MOV     A,B             ; Compare high byte of user program
        CMP     H               ;
        RZ                      ; Zero: Return
;
; Not zero: ?
;
nxtuu:  MOV     A,M             ; Get low byte of user program
        SUB     E               ; Subtract low byte of char ptr
        INX     H               ; Point to next byte
        MOV     A,M             ; Get high byte of user program
        SBB     D               ; Subtract low byte of char ptr
        DCX     H               ; Point to previous byte
        CMC                     ; Complement Carry flag
        RC                      ; Return if DE > M(HL)
;
; Else, call ?
;
fnd3:   CALL    nxtxt           ;
;
        JMP     fnd2            ;
;
;--------------------------------
;
;         Subroutine __________________________
;
evil:   CPI     quote           ; Is it a quote surrounding strings?
        JZ      quote2          ; Yes: go process it
;
        CALL    eval            ; No: Evaluate expression
;
        PUSH    B               ;
        LHLD    linumb          ; ?
        CALL    convp           ; ?
;
        POP     B               ;
        CPI     dolar           ; Is it a single character?
        JNZ     andt            ; No: Check if it is a "?" char
;
        MOV     A,C             ; Yes:
        JMP     outch           ;   Output it to terminal
;
; It is not a single char. Is it followed by a "?" char?
;
andt:   SUI     greatR+1        ;
        JZ      prnt2           ; Yes: Display the line number in decimal 
;
; System Variable "Greater Than" subroutine.
;
; Pass a 16-bits value in BC to a machine language subroutine.
;
        INR     A               ; Is it a ">" char?
        CZ      brkpnt          ; Call RST 0 at 0000H
;
; "At the conclusion of the machine language subroutine,
; a Intel 8080 RET instruction returns control to VTL-2,
; and places the value found in the BC register pair into
; the question variable ">".
;
        MOV     M,C             ; Copy BC to M(HL)
        INX     H               ;
        MOV     M,B             ;
        LXI     H,greater       ; Point to ">" data storage
        MOV     A,M             ; Get 1st byte from memory
        ADD     C               ; Add C-reg
        MOV     C,A             ; Move it to C-reg
        DCX     H               ; Point to previous byte
        MOV     A,M             ; Get 2nd byte from memory
        ADC     B               ; Add B-reg
        MOV     M,C             ; Move C-reg to ">" data storage
        INX     H               ; Go back to 1st byte
        MOV     M,A             ; Move B-reg to ">" data storage
;
; Compare BC with DE.
;
cpbd:   MOV     A,C             ; BC = ">" data storage
        CMP     E               ;
        RNZ                     ;
;
        MOV     A,B             ; DE = ?
        CMP     D               ;
        RET                     ;
;
;--------------------------------
; Skip what?
;
; BC: ?, DE: ?, HL: ?
;
skp2:   CALL    find            ;
;
        JNC     insrt           ;
;
        CALL    lxhn            ; Get line number from user program,
;                                   and compare with ?
;
        JNZ     insrt           ; Not zero: Insert
;
        CALL    nxtxt           ;
;
        XCHG                    ;
        LHLD    number          ; Lin ptr
delt:   CALL    cpbd            ; Compare BC with DE
;
        JZ      fitit           ; Equal: ?
;
; Not equal: ?
;
        LDAX    D               ; Move byte at M(DE)
        MOV     M,A             ;   to M(HL).
        INX     H               ; Increment HL
        INX     D               ; Increment DE
        JMP     delt            ; Loop
;
; Insert a program line in program "text".
;
fitit:  SHLD    ampers          ; Save address of next byte of user program
        MOV     B,H             ; Copy HL to BC
        MOV     C,L             ;
insrt:  LHLD    curent          ; ?
        LXI     D,0003H         ; 3 = line number + space?
        MOV     A,M             ; Get byte
        ANA     A               ; Is it zero?
        JZ      loop            ; Yes: Back to main loop of interpreter
;
cntln:  INX     D               ; No:
        INX     H               ;
        MOV     A,M             ; Get next byte
        ANA     A               ;
        JNZ     cntln           ; Not zero: Back to main loop of interpreter
;
; Zero: ?
;
        XCHG                    ; 
        DAD     B               ; Add BC
        XCHG                    ; Put result in DE
        LXI     H,asterix       ; Memory size
        MOV     A,E             ; Subtract memory size from program length?
        SUB     M               ;
        INX     H               ;
        MOV     A,D             ;
        SBB     M               ;
        JNC     start           ; No memory: Return to interpreter
;
; Some memory is present.
;
        XCHG                    ;
        SHLD    ampers          ; Then, this must be next byte of user PGM?
        INX     B               ;
        INX     H               ;
        PUSH    H               ;
        LHLD    number          ; Lin ptr
        XCHG                    ;
        POP     H               ;
;
; BC: ?, DE: ?, HL: ?
;
slide:  DCX     B               ;
        DCX     H               ;
        LDAX    B               ; A = M(BC)
        MOV     M,A             ;
        CALL    cpbd            ; Compare ? with ?
;
        JNZ     slide           ; Move program "text"
;
        LHLD    dollar          ; Char ptr
        MOV     A,L             ; Get M(HL)
        STAX    B               ; M(BC) = A
        INX     B               ; Increment BC
        MOV     A,H             ; Get M(HL)
        STAX    B               ; M(BC) = A
        LHLD    curent          ; ?
        DCX     H               ; Decrement it?
;
; Move one line in memory?
;
movl:   INX     H               ;
        INX     B               ;
        MOV     A,M             ;
        STAX    B               ; M(BC) = A
        ANA     A               ;
        JNZ     movl            ; Not zero: Loop
;
        JMP     loop            ; Zero: Back to main loop of interpreter
;
;--------------------------------
; Print 16-bit line number in decimal.
;
; BC: ?, DE: ?, HL: ?
;
prnt2:  LXI     D,decbuf        ; Decimal buffer (5 chars long)
        LXI     H,pwrs10        ; Table of decimal values
cvd1:   PUSH    D               ; Save address of DECBUF on Stack
        MOV     D,B             ; Copy BC to DE
        MOV     E,C             ;
        MOV     B,M             ; Get high byte of dec value
        INX     H               ;
        MOV     C,M             ; Get low byte of dec value
        INX     H               ;
        PUSH    H               ; Save address of next word
        XCHG                    ;
        CALL    div             ; 16-bits division
;
        XCHG                    ;
        MOV     A,L             ; Get low byte
        MOV     B,D             ; Copy DE to BC
        MOV     C,E             ;
        POP     H               ; Restore address of next dec value
        POP     D               ; Restore address of DECBUF
        ADI     zero            ; Make it an ASCII number
        STAX    D               ; M(DE) = A
        INX     D               ;
        MOV     A,M             ; Get high byte
        CPI     7EH             ; "~" ?
        JNZ     cvd1            ; No: Loop
;
; Yes: ?
;
        LXI     H,decbuf-1      ; Point before decimal buffer
        DCX     D               ;
        LDAX    D               ; A = M(DE)
        ORI     1000$0000B      ; Why?
        STAX    D               ; M(DE) = A
;
; Suppress zeroes?
;
zrsup:  INX     H               ; Point to next char
        MOV     A,M             ; Get it
        CPI     zero            ; Is it an ASCII zero?
        JZ      zrsup           ; Yes: Loop, not showing zeroes
;
; Print message -- Init delimiter.
;
pntmsg: XRA     A               ; Init delimiter
;
; Start displaying a message.
;
strtmsg:
        STA     delim           ; Save delimiter
        MOV     B,A             ;    and put it in B-reg.
outmsg: MOV     A,M             ; Get char from memory
        INX     H               ; Update memory pointer
        CMP     B               ; Is it the delimiter?
        JZ      contC           ; Yes: Check if it is Ctrl-C
;
        CALL    ascii           ; No: Display the char on the terminal
;
        JMP     outmsg          ;    and loop until delimiter found.
;
; Check for Control-C.
;
contC:  CALL    polcat          ; Poll character at terminal?
;
        RNC                     ; None available: Return
;
        CALL    inch            ; One char present: Get it
;
        CPI     ctrlC           ; Is it Control-C?
        JZ      start           ; Yes: Return to interpreter
;
        JMP     inch            ; No: Get char
;
;--------------------------------
; Evaluate expressions between parentheses.
;
; BC: ?, DE: ?, HL: ?
;
eval:   CALL    getval          ; Get value of a number
;
; Next term of an expression?
;
nxtrm:  MOV     A,M             ; 
        ANA     A               ; Number = zero?
        RZ                      ; Yes: Return
;
        CPI     Rparen          ; No: Is it a closing parenthesis?
        JZ      outd            ; Yes: INC HL and return
;
        CALL    term            ; No: Process term of expression
;
        MOV     B,H             ; Copy HL to BC
        MOV     C,L             ;
        LHLD    evalptr         ; Load eval pointer
        JMP     nxtrm           ;    and loop until 00H found.
;
;--------------------------------
; Process one term of an arithmetic expression.
;
; BC: ?, DE: ?, HL: ?
;
term:   PUSH    B               ; Put BC on Stack
        MOV     A,M             ; Get char
        PUSH    PSW             ; Save it on Stack
        INX     H               ; Point to next char
        CALL    getval          ; Get value of a decimal number
;
        SHLD    evalptr         ; Save eval pointer?
        POP     PSW             ; Restore char
        POP     H               ; Place BC into HL
;
; Is it followed by an addition?
;
        CPI     plus            ;
        JNZ     eval2           ; No: Check next arithmetic operation
;
; Yes: 16-bit addition.
;
        DAD     B               ; Add BC to HL
        RET                     ; Result in HL
;
;--------------------------------
; Is it followed by a subtraction?
;
eval2:  CPI     minus           ;
        JNZ     eval3           ; No: Check next arithmetic operation
;
; Yes: 16-bit subtraction subroutine.
;
hsubb:  MOV     A,L             ; Subtract BC from HL
        SUB     C               ;
        MOV     L,A             ;
        MOV     A,H             ;
        SBB     B               ;
        MOV     H,A             ;
        RET                     ; Result in HL
;
;--------------------------------
; Is it followed by a multiplication?
;
eval3:  CPI     star            ;
        JNZ     eval4           ; No: Check next arithmetic operation
;
; Yes: 16-bit multiplication subroutine.
;
        XCHG                    ; Put multiplicand in DE
        LXI     H,0000H         ; Clear partial product
        MVI     A,10H           ; Set loop count to 16 bits
mult1:  PUSH    PSW             ; Save it on Stack
        DAD     H               ; Add to product
        XCHG                    ; Put it into DE
        DAD     H               ; Add to product
        XCHG                    ; Put it into HL
        JNC     mult2           ; If the result has 17 bits,
;
        DAD     B               ;   add multiplier to product.
mult2:  POP     PSW             ; Restore count
        DCR     A               ; Decrement it
        JNZ     mult1           ; Not end: Loop
;
        RET                     ; Result in HL
;
;--------------------------------
; Is it followed by a division?
;
eval4:  CPI     slash           ;
        JNZ     eval5           ; No: Now, check relational operators
;
        CALL    div             ; Yes: Use a subroutine
;
        SHLD    percent         ; Remainder of divide operation
        XCHG                    ;
        RET                     ; Result in HL
;
;--------------------------------
; 16-bit division subroutine.
;
; BC: divisor, DE: dividend, HL: remainder
;
div:    XCHG                    ; Put dividend in DE
        LXI     H,0000H         ; Initialize remainder in HL
        MOV     A,B             ; Checks if 16-bits divisor = zero
        ORA     C               ;
        RZ                      ; Yes: Return (division by zero is forbidden)
;
        MVI     A,10H           ; Set loop count to 16 bits
div1:   PUSH    PSW             ; Save it on Stack
        DAD     H               ;
        XCHG                    ; Dividend
        DAD     H               ;   bit
        XCHG                    ;   to Carry.
        JNC     div2            ; It was zero
;
        INX     H               ; It was one, duplicate in HL
div2:   CALL    hsubb           ; Subtract BC from HL
;
        INX     D               ;
        JNC     div3            ; It was zero
;
        DAD     B               ; Add divisor to remainder
        DCX     D               ;
div3:   POP     PSW             ; Restore loop count
        DCR     A               ; Decrement it
        JNZ     div1            ; Not end: Loop
;
        RET                     ;
;
;--------------------------------
; Now, check the relational operators.
;
eval5:  LXI     D,0000H         ; Init DE with FALSE value
        CALL    evil5           ; Check the relational operators
;
        XCHG                    ; HL will contain the TRUE/FALSE value
        RET                     ;
;
;--------------------------------
; Is it followed by an equality?
;
evil5:  SUI     equal           ;
        JNZ     evil6           ; No: Check next relational operators
;
        CALL    hsubb           ; Yes: Use a subroutine
;
        RNZ                     ; Return if not zero
;
        ORA     L               ; Is it zero?
        RNZ                     ; No: Return if not zero (FALSE)
;
        INX     D               ; Yes: = 1 (TRUE value)
        RET                     ;
;
;--------------------------------
; Is it followed by a greater than?
;
evil6:  DCR     A               ; "=" - 1 = "<"
        JZ      evil7           ; Yes: Check next relational operator
;
        CALL    hsubb           ; No: Use a subroutine
;
        RNC                     ; Return if FALSE
;
        INX     D               ; = 1 (TRUE value)
        RET                     ;
;
;--------------------------------
; Is it followed by a less than?
;
evil7:  CALL    hsubb           ; Use a subroutine
;
        RC                      ; Return if FALSE
;
        INX     D               ; = 1 (TRUE value)
        RET                     ;
;
;--------------------------------
; Get value of a decimal number.
;
; BC: ?, DE: ?, HL: ?
;
getval: CALL    cvbin           ; Convert to binary number
;
        RNC                     ; It was a number: Return
;
        CPI     quest           ; Is it a PRINT statement?
        INX     H               ; Increment mem ptr
        JNZ     var             ; No: Check if it is a variable name
;
; Yes: ?
;
        SHLD    valvar          ; Save value of variable?
        CALL    inln            ; ?
;
        CALL    eval            ; Evaluate expression
;
        LHLD    valvar          ; Value of expression?
        RET                     ;
;
;--------------------------------
; Is it a variable name?
;
; BC: ?, DE: ?, HL: ?
;
var:    CPI     dolar           ; Is it an INPUT statement?
        JNZ     var1            ; No: Check next possibility
;
        CALL    inch            ; Yes: Input next char
;
        MOV     C,A             ;
        MVI     B,00H           ; Make sure a char is a byte value
        RET                     ;
;
; Is it the start of an expression?
;
var1:   CPI     Lparen          ;
        JZ      eval            ; Yes: Evaluate expression
;
        DCX     H               ; No: Re-point to character
;
;         Subroutine __________________________
;
var2:   CALL    convp           ; ?
;
        MOV     C,M             ;
        INX     H               ;
        MOV     B,M             ;
        LHLD    result          ; ?
        RET                     ;
;
;--------------------------------
; Obviously, deal with array DEEK/DOKE...
;
array:  CALL    eval            ; Evaluate expression
;
        SHLD    result          ; Save result of evaluation?
        LHLD    ampers          ; Get address of next byte of user program
        DAD     B               ;
        DAD     B               ;
        POP     PSW             ;
        RET                     ;
;
;--------------------------------
; Convert p...?
;
; BC: ?, DE: ?, HL: ?
;
convp:  MOV     A,M             ;
        INX     H               ;
        PUSH    PSW             ;
        CPI     colon           ; Is it the start of an array statement?
        JZ      array           ; Yes: Go to ARRAY subroutine
;
; No: ?
;
        SHLD    result          ; Save value of expression
        LXI     H,0020H         ; = 32 ?
        ANI     0011$1111B      ; = 3FH
        ADD     L               ; Add 32 ?
        MOV     L,A             ;
        DAD     H               ; 2 times HL
;
; "Output a char" (at the end) uses those 2 instructions...
;
outb:   POP     PSW             ;
        RET                     ;
;
;--------------------------------
; Table of powers of 10.
;
pwrs10: DB      27H, 10H        ; = 10000
        DB      03H,0E8H        ; =  1000
        DB      00H, 64H        ; =   100
        DB      00H, 0AH        ; =    10
        DB      00H, 01H        ; =     1
;
; (Note that this is the REVERSE of the Intel word format...)
;--------------------------------
; Test if it is an ASCII decimal number character.
;
tstn:   MOV     A,M             ; Get char
        CPI     '9'+1           ; Is it > "9" ?
        CMC                     ;
        RC                      ;
        CPI     '0'             ; Is it < "0" ?
        RET                     ;
;
;--------------------------------
; Convert to line number.
;
cvtln:  CALL    inln            ;
;
; Convert to binary number.
;
cvbin:  CALL    tstn            ; Is it a number?
;
        RC                      ; No: Return
;
; Yes: Convert the ASCII char to binary number.
;
        LXI     B,0000H         ;
cbloop: MOV     A,M             ; Get char
        SUI     zero            ; Convert from ASCII to binary
        ADD     C               ; Add C-reg
        MOV     C,A             ; Put result in C-reg
        MVI     A,00H           ; Re-init A-reg
        ADC     B               ; Add B-reg
        MOV     B,A             ; Put result in B-reg
        INX     H               ; Point to next char
        CALL    tstn            ; Is it a number?
;
        CMC                     ;
        RNC                     ; No: Return
;
; Yes: ?
;
        PUSH    H               ; Save pointer
        MOV     H,B             ; Copy BC to HL
        MOV     L,C             ;
        DAD     H               ; 2 times HL
        DAD     H               ; 4 times HL
        DAD     B               ; 2 times BC
        DAD     H               ; 8 times HL
        MOV     B,H             ; Move HL to BC ?
        MOV     C,L             ;
        POP     H               ; Restore pointer
        JMP     cbloop          ; Loop until not a number
;
;--------------------------------
; Process line input from terminal.
;
inln6:  CPI     atsign          ; Discard line inputted?
        JZ      newlin          ; Yes: Start a new line
;
; No: We are in a line. Are we at the end?
;
        INX     H               ; Increment line pointer
        MOV     A,L             ; Get its low byte
        CPI     LOW linbuf+73   ; Are we at the end of the line?
        JNZ     inln2           ; No: Get next char
;
; Yes: Start a new line on the terminal.
;
newlin: CALL    crlf            ; Return "cursor" at beginning of next line
;
;         Subroutine __________________________
;
inln:   LXI     H,linbuf+1      ; Point to first char in line buffer
;
; Check if we reached the beginning of the line,
; that is to say: erased all the line, backspacing chars.
;
inln5:  DCX     H               ; Point before the first char
        MOV     A,L             ;
        CPI     LOW linbuf-1    ; Is it the end of the line?
        JZ      newlin          ; Yes: Start a new line
;
; Not at end of line: Get another char, and check it.
;
inln2:  CALL    inch            ; Input another char
;
        MOV     M,A             ;
        CPI     backs           ; Backspace to erase a char?
        JZ      inln5           ; Yes: Check if we erased everything
;
; No: Check if the user had ended inputting a line.
;
        CPI     cr              ;
        JC      inln2           ; No: Get another char
;
        JNZ     inln6           ; Yes: Go proceed another line
;
        XRA     A               ; Mark end of line with a 00H
        MOV     M,A             ; 
        LXI     H,linbuf        ; Why?
        JMP     prlf            ; It was CR: add LF
;
;--------------------------------
;
quote2: INX     H               ; Skip beginning quote
;
;         Subroutine __________________________
;
strng:  CALL    strtmsg         ; Proceed start of string
;
        MOV     A,M             ; Get char
        CPI     semicol         ; Is it a semicolon?
        RZ                      ; Yes: Then, no cr/lf after PRINT
;
; No: End of line reached, output CR and LF.
;
crlf:   MVI     A,cr            ; Carriage Return
        CALL    outch           ;
;
prlf:   MVI     A,lf            ; Line Feed
        CALL    outch
;
; System Variable COMMA subroutine.
;
; Outputs the number of null bytes (00H) stored in COMMA,
; while the printhead of the ASR-33 Teletype returns to
; the left side of the roll of punched paper.
;
        LDA     comma           ; Number of null bytes after cr/lf
        INR     A               ;
null:   DCR     A               ;
        RZ                      ; Return if zero reached
;
        PUSH    PSW             ;
        XRA     A               ; = Null byte in A-reg
        CALL    outch           ;
;
        POP     PSW             ;
        JMP     null            ; Loop the number of null bytes
;
;--------------------------------
;
okm:    DB      cr, lf, 'OK', 00H
;
;--------------------------------
;       I/O subroutines
;--------------------------------
; The following code is repeated 3 times,
; the first time to check if any char is available,
; the second time to input a char, and
; the third time (which is also executed if a char
; was inputted) to display the char on the terminal.
; Each time, the code deals with 3 I/O ports (5 when
; the cassette tape player is involved).
;--------------------------------
; Poll character at terminal?
;
polcat:
;
        LDA     uparrow         ; Number of terminal option currently running
        RRC                     ; Rotate A-reg right, putting bit0 in Carry
        JC      inp12           ; Bit0 = 1 (high) --> I/O port 18
;
        ANI     0000$0100B      ;
        JZ      inp0            ; Bit3 = 0 (low) --> I/O port 0
;
; Else, I/O port 16 (bit3 = 1).
;
        IN      10H             ; I/O port 16 = 2SIO board
        RRC                     ;
        RET                     ;
;
inp12:  IN      12H             ; I/O port 18 = 2SIO board
        RRC                     ;
        RET                     ;
;
inp0:   IN      00H             ; I/O port 0 = Single-SIO board
        RRC                     ;
        CMC                     ;
        RET                     ;
;
;--------------------------------
; Input a character, whatever the I/O ports involved.
;
inch:   CALL    polcat          ; Check status again
;
        JC      ina             ; One available: Go process it
;
; Status was FALSE: There is no char in one of the I/O
; ports, so let's check the cassette tape player, instead.
;
        LDA     period          ; Terminal/cassette echo switch
        ANI     0000$0010B
;                    ||||
;                    |||+--> 0 = Nothing displayed on terminal
;                    |||     1 = Chars.  displayed on terminal
;                    ||+---> 2 = Input from cassette without echo <--
;                    |+----> 3 = Output  to cassette with    echo
;                    +-----> 4 = Output  to cassette without echo
;
        JZ      inch            ; Yes: Loop
;
; No: Input from cassette with echo.
;
        IN      06H             ; I/O port 6 = cassette Status
        RRC                     ;
        JC      inch            ; Loop while waiting for char
;
        IN      07H             ; I/O port 7 = cassette Data
        JMP     oute            ;   and show it on terminal.
;
; Status was TRUE: There is a char in one of the I/O ports.
;
ina:    LDA     uparrow         ; Number of terminal option currently running
        RRC                     ;
        JC      in12            ; Bit0 = 1 (high) --> I/O port 19
;
        ANI     0000$0100B      ;
        JZ      in0             ; Bit3 = 0 (low) --> I/O port 1
;
; Else, I/O port 17 (bit 3 = 1).
;
        IN      11H             ; I/O port 17 = 2SIO Data
        JMP     ascii           ;
;
in12:   IN      13H             ; I/O port 19 = 2SIO Data
        JMP     ascii           ;
;
in0:    IN      01H             ; I/O port 1 = Single-SIO Data
;
ascii:  ANI     0111$1111B      ; Make sure it is an ASCII character
;
; . . . . . . . . . . . . . . . .
; Fall through the "output char" subroutine.
;
outch:  PUSH    PSW             ; Save char to check if cassette echo
        LDA     period          ; Terminal echo switch
        ANI     0000$0100B      ; Output to cassette with echo?
        JZ      outa            ; Yes: Go do it
;
; No: Save on cassette without display on terminal.
;
outc:   IN      06H             ; I/O port 6 = cassette Status
        INR     A               ;
        JZ      outa            ; Char sent: Now, check if it is displayed
;
        RLC                     ;
        JC      outc            ; Loop while waiting for char
;
        POP     PSW             ; Restore char
        OUT     07H             ; I/O port 7 = cassette Data
oute:   PUSH    PSW             ;
;
; Now, determine on which terminal to display the char.
;
outa:   LDA     period          ; Terminal echo switch
        RRC                     ; Rotate A-reg right, putting bit0 in Carry
        JNC     outb            ; None: POP PSW and Return
;
        LDA     uparrow         ; Number of terminal option currently running
        RRC                     ;
        JC      out12           ; Bit0 = 1 (high) --> I/O port 18
;
        ANI     0000$0100B      ;
        JZ      out0            ; Bit3 = 0 (low) --> I/O Port 0
;
; Else, I/O port 16 (bit3 = 1).
;
out10:  IN      10H             ; I/O port 16 = 2SIO board Status
        ANI     0000$0010B      ;
        JZ      out10           ; Loop while waiting
;
        POP     PSW             ;
        OUT     11H             ; I/O port 17 = 2SIO board Data
        RET                     ;
;
out12:  IN      12H             ; I/O port 18 = 2SIO board Status
        ANI     0000$0010B      ;
        JZ      out12           ; Loop while waiting
;
        POP     PSW             ;
        OUT     13H             ; I/O port 19 = 2SIO board Data
        RET                     ;
;
out0:   IN      00H             ; I/O port 0 = Single-SIO board Status
        RLC                     ;
        JC      out0            ; Loop while waiting
;
        POP     PSW             ;
        OUT     01H             ; I/O port 1 = Single-SIO board Data
        RET                     ;
;
;--------------------------------
;
        END