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 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". -=;+ 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+(YI*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)I*250 330 #=C=0*80 340 L=L+1 350 #=*-&/213*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