diff --git a/programs/develop/tinybasic/Makefile b/programs/develop/tinybasic/Makefile new file mode 100644 index 0000000000..c2d2c8a1e7 --- /dev/null +++ b/programs/develop/tinybasic/Makefile @@ -0,0 +1,28 @@ +CC = kos32-gcc +LD = kos32-ld + +SDK_DIR:= $(abspath ../../../contrib/sdk) + +LDFLAGS = -static -S -nostdlib -T $(SDK_DIR)/sources/newlib/app.lds --image-base 0 + +CFLAGS = -c -fno-ident -O2 -fomit-frame-pointer -U__WIN32__ -U_Win32 -U_WIN32 -U__MINGW32__ -UWIN32 + +INCLUDES= -I $(SDK_DIR)/sources/newlib/libc/include +LIBPATH:= -L $(SDK_DIR)/lib -L /home/autobuild/tools/win32/mingw32/lib + +SOURCES = TinyBasic.c \ + $(NULL) + +OUTFILE= TinyBasic + +OBJECTS = $(patsubst %.c, %.o, $(SOURCES)) + +default: hello + +hello: $(OBJECTS) + $(LD) $(LDFLAGS) $(LIBPATH) -o $(OUTFILE) $(OBJECTS) -lgcc -lc.dll -lapp -lgcc_eh -Map=$(OUTFILE).map + kos32-strip $(OUTFILE) + kos32-objcopy $(OUTFILE) -O binary + +%.o : %.c $(SOURCES) + $(CC) $(CFLAGS) $(INCLUDES) -o $@ $< diff --git a/programs/develop/tinybasic/TBuserMan.txt b/programs/develop/tinybasic/TBuserMan.txt new file mode 100644 index 0000000000..b9bfd992c7 --- /dev/null +++ b/programs/develop/tinybasic/TBuserMan.txt @@ -0,0 +1,1540 @@ + + + + + + + + + + + + I T T Y B I T T Y C O M P U T E R S + + TINY BASIC User Manual + + + + + + + + Congratulations! You have received the first of what we hope +is a long line of low cost software for hobby computers. We are +operating on a low margin basis, and hope to make a profit on +volume. Please help us to stay in business by respecting the +Copyright notices on the software and documentation. + If you are in a hurry to try TINY BASIC, Appendix C will tell +you how to get on the air. Then come back and read the rest of this +manual --- most of it is useful information. + + The TINY BASIC interpreter program has been extensively tested +for errors ("bugs"), but it would be foolish to claim of any program +that it is guaranteed bug-free. This program does come with a +"Limited Warranty" in that any errors discovered will be corrected in +the first 90 days. Catastrophic bugs will be corrected by +automatically mailing out corrected versions to all direct mail +customers and local dealers. Minor bugs will be corrected by +request. In any case this warranty is limited to replacement of the +Program Tape and/or documentation, and no liability for consequential +damages is implied. + If you think you have found a bug, make a listing of the +program that demonstrates the bug, together with the run input and +output. Indicate on the listing what you think is wrong and what +version number you are running and your serial number (on the tape +leader). Mail this to: + + ITTY BITTY COMPUTERS + P.0. Box 6539 + San Jose, CA 95150 + +We will try to be responsive to your needs. + + +---------- +(C) Copyright 1976 by Tom Pittman. All rights reserved. + +"Itty Bitty" is a Trademark of the ITTY BITTY COMPUTERS Company. + + + 1 + + TINY BASIC was conceived by the dragons at the People's +Computer Company (PCC), a non-profit corporation in Menlo Park CA. +and its implementation defined by Dennis Allison and others in the +PCC newspaper and an offshoot newsletter. The implementation of this +program follows the philosophy defined there. The reader is referred +to PCC v.4 Nos 1-3 for a discussion of the inner workings of this +software. + In keeping with the "small is good" philosophy, TINY BASIC +employs the two level interpreter approach (with its consequent speed +cost) so that the whole system occupies only 2K of program memory +(exclusive of user program; some versions are slightly larger). +With 1K of additional RAM small but useful user programs (50 lines or +less) may be accommodated. A system with 4K of RAM can contain the +interpreter and about 100 lines of user program. + + TINY BASIC is offered in several versions for each processor. +One is designed to be used with an arbitrary operating system, and +executes out of low memory (e.g. 0100-08FF for the 6800). The other +versions are configured for unusual memory requirements of particular +operating systems. All are "clean" programs, in that they will +execute properly from protected memory (such as PROM). Direct +addressing is used for interpreter variables as much as possible, so +memory Page 00 is largely dedicated. In all cases the user programs +are placed at the end of that part of lower memory used by TINY, and +they may occupy all the remaining contiguous memory. Appendix D is a +a summary of the important low-memory addresses. + TINY BASIC is designed to be I/O independent, with all input +and output funneled through three jumps placed near the beginning of +the program. In the non-standard versions these are preset for the +particular operating system I/O, so the discussion to follow is +primarily concerned with the standard versions. For this +discussion, it is assumed that the interpreter begins at hex address +0100, though the remarks may be applied to other versions with an +appropriate offset. + Location 0106 is a JMP to a subroutine to read one ASCII +character from the console/terminal. Location 0109 is a JMP to a +subroutine to type or display one ASCII character on the +console/terminal. In both cases the character is in the A +accumulator, but the subroutine need not preserve the contents of the +other registers. It is assumed that the character input routine will +simultaneously display each character as it is input; if this is not +the case, the JMP instruction in location 0106 may be converted to a +JSR, so that each character input flows through the output subroutine +(which in this case must preserve A) before being fed to TINY. +Users with terminals using Baudot or some other non-ASCII code should +perform the character conversion in the Input and Output subroutines. +If your console is a CRT and/or you have no need to output or +display extra pad characters with each Carriage Return and Linefeed, +you may intercept these in the output routine to bypass their +display. Each input prompt by TINY is followed by an "X-ON" +character (ASCII DC1) with the sign bit set to 1 (all other +characters except rubout are output with the sign bit set to 0) so +these are also readily detected and deleted from the output stream. +Appendix C shows how to perform these tests. + A third subroutine provided by you is optional, and gives TINY + + + 2 + +a means to test for the BREAK condition in your system. Appendix C +shows how this subroutine may be implemented for different types of +I/O devices. If you choose to omit this subroutine, TINY will assume +that a BREAK condition never happens; to include it, simply replace +locations 010C-010E with a JMP to your subroutine, which returns with +the break condition recorded in the Carry flag (1 = BREAK, 0 = no +BREAK). The Break condition is used to interrupt program execution, +or to prematurely terminate a LIST operation. Tiny responds to the +Break condition any time in the LIST, or just before examining the +next statement in program execution. If a LIST statement included +within a program is aborted by the Break condition, the Break +condition must be held over to the next statement fetch (or repeated) +to stop program execution also. + All input to Tiny is buffered in a 72 character line, +terminated by a Carriage Return ("CR"). Excess characters are +ignored, as signaled by ringing the console/terminal bell. When the +CR is typed in, Tiny will echo it with a Linefeed, then proceed to +process the information in the line. If a typing error occurs during +the input of either a program line or data for an INPUT statement, +the erroneous characters may be deleted by "backspacing" over them +and retyping. If the entire line is in error, it may be canceled +(and thus ignored) by typing the "Cancel" key. The Backspace code is +located near the beginning of the program (location 010F), and is +set by default to "left-arrow" or ASCII Underline (shift-O on your +Teletype). To change this to the ASCII Standard Backspace code (or +anything else you choose), the contents of location 010F may be +changed to the desired code. Similarly the Cancel code is located at +memory address 0110, and is set by default to the ASCII Cancel code +(Control-X). Four characters which may not be used for line edits +(Backspace or Cancel) are DC3 (hex 13), LF (0A), NUL (00), and DEL +(FF). These codes are trapped by the TINY BASIC input routines +before line edits are tested. + When Tiny ends a line (either input or output), it types a CR, +two pad characters, a Linefeed, and one more pad character. The pad +character used is defined by the sign bit in location 0111, and is +set by default to the "Rubout" or Delete code (hex FF; Location 0111 +Bit 7 = 1) to minimize synchronization loss for bit-banger I/O +routines. The pad character may be changed to a Null (hex 00) by +setting the sign of location 0111 to 0. The remainder of this byte +defines the number of Pad characters between the CR and linefeed. +More than two pad characters may be required if large user programs +are to be loaded from tape (see comments on Tape Mode, below). + TINY BASIC has a provision for suppressing output (in +particular line prompts) when using paper tape for loading a program +or inputting data. This is activated by the occurrence of a Linefeed +in the input stream (note that the user normally has no cause to type +a Linefeed since it is echoed in response to each CR), and disables +all output (including program output) until the tape mode is +deactivated. This is especially useful in half-duplex I/O systems +such as that supported by Mikbug, since any output would interfere +with incoming tape data. The tape mode is turned off by the +occurrence of an X-OFF character (ASCII DC3, or Control-S) in the +input, by the termination of an executing program due to an error, or +after the execution of any statement or command which leaves Tiny in +the command mode. The tape mode may be disabled completely by +replacing the contents of memory location 0112 with a 00. + + 3 + + Memory location 0113 is of interest to those 6800 users with +extensive operating systems. Normally Tiny reserves 32 bytes of +stack space for use by the interpreter and I/O routines (including +interrupts). Up to half of these may be used by Tiny in normal +operation, leaving not more than 16 bytes on the stack for I/O. If +your system allows nested interrupts or uses much more than ten or +twelve stack bytes for any purpose, additional space must be +allocated on the stack. Location 0113 contains the reserve stack +space parameter used by Tiny, and is normally set to 32 (hex 20). If +your system requires more reserve, this value should be augmented +accordingly before attempting to run the interpreter. + All of these memory locations are summarized in Appendix D. +Note that there are no Input or Output instructions or interrupt +disables in the interpreter itself; aside from the routines provided +for your convenience (which you may connect or disconnect), your +system has complete control over the I/O and interrupt structure of +the TINY BASIC environment. + + TINY BASIC is designed to use all of the memory available to it +for user programs. This is done by scanning all the memory from the +beginning of the user program space (e.g. 0900 for the standard 6800 +version) for the end of contiguous memory. This then becomes the +user program space, and any previous contents may be obliterated. +If it is desired to preserve some part of this memory for machine +language subroutines or I/O routines, it will be necessary to omit +the memory scan initialization. This is facilitated in TINY BASIC by +the definition of two starting addresses. Location 0100 (or the +beginning of the interpreter) is the "Cold Start" entry point, and +makes no assumptions about the contents of memory, except that it is +available. Location 0103 is the "Warm Start" entry point, and +assumes that the upper and lower bounds of the user program memory +have been defined, and that the program space is correctly +formatted. The Warm Start does not destroy any TINY BASIC programs +in the program space, so it may be used to recover from catastrophic +failures. The lower bound is stored in locations 0020-0021 and the +upper bound is in locations 0022-0023. When using the Warm Start to +preserve memory, you should be sure these locations contain the +bounds of the user space. Also when using the Warm Start instead of +the Cold Start, the first command typed into TINY should be "CLEAR" +to properly format the program space. + + +STATEMENTS + + TINY BASIC is a subset of Dartmouth BASIC, with a few +extensions to adapt it to the microcomputer environment. Appendix B +contains a BNF definition of the language; the discussion here is +intended to enable you to use it. When TINY issues a line prompt (a +colon on the left margin) you may type in a statement with or without +a line number. If the line number is included, the entire line is +inserted into the user program space in line number sequence, without +further analysis. Any previously existing line with the same line +number is deleted or replaced by the new line. If the new line +consists of a line number only, it is considered a deletion, and +nothing is inserted. Blanks are not significant to TINY, so blanks + + + 4 + +imbedded in the line number are ignored; however, after the first +non-blank, non-numeric character in the line, all blanks are +preserved in memory. + The following are valid lines with line numbers! + + 123 PRINT "HELLO" + 456 G O T O 1 2 3 + 7 8 9 PRINT "THIS IS LINE # 789" + 123 + 32767 PRINT "THIS IS THE LARGEST LINE #" + 1PRINT"THIS, IS THE SMALLEST LINE #" + 10000 TINY BASIC DOES NOT CHECK + 10001 FOR EXECUTABLE STATEMENTS ON INSERTION. + + 0 Is not a valid line number. + + If the input line does not begin with a line number it is +executed directly, and must consist of one of the following statement +types: + + LET GOTO REM + IF...THEN GOSUB CLEAR + INPUT RETURN LIST + PRINT END RUN + + These statement types are discussed in more detail in the pages +to follow. + Note that all twelve statement types may be used in either the +Direct Execution mode (without a line number) or in a program +sequence (with a line number). Two of the statements (INPUT and RUN) +behave slightly differently in these two operating modes, but +otherwise each statement works the same in Direct Execution as within +a program. Obviously there is not much point in including such +statements as RUN or CLEAR in a program, but they are valid. +Similarly, a GOSUB statement executed directly, though valid, is +likely to result in an error stop when the corresponding RETURN +statement is executed. + + +EXPRESSIONS + + Many of these statement types involve the use of EXPRESSIONS. +An Expression is the combination of one or more NUMBERS or VARIABLES, +joined by OPERATORS, and possibly grouped by Parentheses. There are +four Operators: + + addition + - subtraction + * multiplication + / division +These are hierarchical, so that in an expression without parentheses, +multiplication and division are performed before addition and +subtraction. Similarly, sub-expressions within parentheses are +evaluated first. Otherwise evaluation proceeds from left to right. +Unary operators (+ and -) are allowed in front of an expression to +denote its sign. + + + 5 + + A Number is any sequence of decimal digits (0, 1, 2, ... 9), +denoting the decimal number so represented. Blanks have no +significance and may be imbedded within the number for readability if +desired, but commas are not allowed. All numbers are evaluated as +16-bit signed numbers, so numbers with five or more digits are +truncated modulo 65536, with values greater than 32767 being +considered negative. The following are some valid numbers (note +that the last two are equivalent to the first two in TINY): + + 0 + 100 + 10 000 + 1 2 3 4 + 32767 + 65536 + 65 636 + + A Variable is any Capital letter (A, B, ... Z). This variable +is assigned a fixed location in memory (two bytes, the address of +which is twice the ASCII representation of the variable name). It +may assume any value in the range, -32768 to +32767, as assigned to +it by a LET or INPUT statement. + The following are some examples of valid expressions: + A + 123 + 1+2-3 + B-14*C + (A+B)/(C+D) + -128/(-32768+(I*1)) + (((((Q))))) + + All expressions are evaluated as integers modulo 65536. Thus +an expression such as + N / P * P +may not evaluate to the same value as (N), and in fact this may be +put to use to determine if a variable is an exact multiple of some +number. TINY BASIC also makes no attempt to discover arithmetic +overflow conditions, except in the case of an attempt to divide by +zero (which results in an error stop). Thus all of the following +expressions evaluate to the same value: + -4096 + 15*4096 + 32768/8 + 30720+30720 + + TINY BASIC allows two intrinsic functions. These are: + RND (range) + USR (address,Xreg,Areg) +Either of these functions may be used anywhere an (expression) is +appropriate. + + + + + + + + 6 + +FUNCTIONS + + + RND (range) + + This function has as its value, a positive pseudo-random number +between zero and range-1, inclusive. If the range argument is zero +an error stop results. + + + + USR (address) + USR (address,Xreg) + USR (address,Xreg,Areg) + + This function is actually a machine-language subroutine call to +the address in the first argument. If the second argument is +included the index registers contain that value on entry to the +subroutine, with the most significant part in X. If the third +argument is included, the accumulators contain that value on entry to +the subroutine, with the least significant part in A. On exit, the +value in the Accumulators (for the 6800; A and Y for the 6502) +becomes the value of the function, with the least significant part in +A. All three arguments are evaluated as normal expressions. + It should be noted that machine language subroutine addresses +are 16-bit Binary numbers. TINY BASIC evaluates all expressions to +16-bit binary numbers, so any valid expression may be used to define +a subroutine address. However, most addresses are expressed in +hexadecimal whereas TINY BASIC only accepts numerical constants in +decimal. Thus to jump to a subroutine at hex address 40AF, you must +code USR(16559). Hex address FFB5 is similarly 65461 in decimal, +though the equivalent (-75) may be easier to use. + For your convenience two subroutines have been included in the +TINY BASIC interpreter to access memory. If S contains the address +of the beginning of the TINY BASIC interpreter (256 for standard +6800, 512 for standard 6502, etc.), then location S+20 (hex 0114) is +the entry point of a subroutine to read one byte from the memory +address in the index register, and location S+24 (hex 0118) is the +entry point of a subroutine to store one byte into memory. + Appendix E gives examples of the USR function. + + + + + + + + + + + + + + + + + + 7 + +STATEMENT TYPES + + + PRINT print-list + PR print-list + + This statement prints on the console/terminal the values of the +expressions and/or the contents of the strings in the print-list. +The print-list has the general form, + item,item... or item;item... +The items may be expressions or alphanumeric strings enclosed in +quotation marks (e.g. "STRING"). Expressions are evaluated and +printed as signed numbers; strings are printed as they occur in the +PRINT statement. When the items are separated by commas the printed +values are justified in columns of 8 characters wide; when semicolons +are used there is no separation between the printed items. Thus, + PRINT 1,2,3 +prints as + 1 2 3 +and + PRINT 1;2;3 +prints as + 123 +Commas and semicolons, strings and expressions may be mixed in one +PRINT statement at will. + If a PRINT statement ends with a comma or semicolon TINY BASIC +will not terminate the output line so that several PRINT statements +may print on the same output line, or an output message may be +printed on the same line as an input request (see INPUT). When the +PRINT statement does not end with a comma or semicolon the output is +terminated with a carriage return and linefeed (with their associated +pad characters). To aid in preparing data tapes for input to other +programs, a colon at the end of a print-list will output an "X-OFF" +control character just before the Carriage Return. + + Although the PRINT statement generates the output immediately +while scanning the statement line, output lines are limited to 125 +characters, with excess suppressed. + + While the Break key will not interrupt a PRINT statement in +progress, the Break condition will take effect at the end of the +current PRINT statement. + + The following are some examples of valid PRINT statements: + PRINT "A=";A,"B+C=";B+C + PR (one blank line) + PRI (prints the value of I) + PRINT 1,","Q*P;",",R/42: + + + + + + + + + + 8 + + INPUT input-list + + This statement checks to see if the current input line is +exhausted. If it is, a question mark is prompted with an X-ON +control character, and a new line is read in. Then or otherwise, the +input line is scanned for an expression which is evaluated. The +value thus derived is stored in the first variable in the input-list. +If there are more variables in the input-list the process is +repeated. In an executing program, several values may be input on a +single request by separating them with commas. If these values are +not used up in the current INPUT statement they are saved for +subsequent INPUT statements. The question mark is prompted only when +a new line of input values is required. Note that each line of input +values must be terminated by a carriage return. Since expressions +may be used as input values, any letter in the input line will be +interpreted as the value of that variable. Thus if a program sets +the value of A to 1, B to 2, and C to 3, and the following statement +occurs during execution: + INPUT X,Y,Z +and the user types in + A,C,B +the values entered into X, Y, and Z will be 1, 3, and 2, +respectively, just as if the numbers had been typed in. Note also +that blanks on the input line are ignored by TINY, and the commas are +required only for separation in cases of ambiguity. In the example +above + ACB +could have been typed in with the same results. However an input, +line typed in as + +1 -3 +6 0 +will be interpreted by TINY as a single value (=58) without commas +for separators. There is one anomaly in the expression input +capability: if in response to this INPUT, the user types, + RND+3 +TINY will stop on a bad function syntax error (the RND function must +be of the form, RND(x)); but if the user types, + RN,D+3 +the values in the variables R, N, and the expression (D+3) will be +input. This is because in the expression evaluator the intrinsic +function names are recognized before variables, as long as they are +correctly spelled. + + Due to the way TINY BASIC buffers its input lines, the INPUT +statement cannot be directly executed for more than one variable at a +time, and if the following statement is typed in without a line +number, + INPUT A,B,C +the value of B will be copied to A, and only one value (for C) will +be requested from the console/terminal. Similarly, the statement, + INPUT X,1,Y,2,Z,3 +will execute directly (loading X, Y, and Z with the values 1, 2, and +3), requesting no input, but with a line number in a program this +statement will produce an error stop after requesting one value. + + If the number of expressions in the input line does not match +the number of variables in the INPUT statement, the excess input is + + 9 + +saved for the next INPUT statement, or another prompt is issued for +more data. The user should note that misalignment in these +circumstances may result in incorrect program execution (the wrong +data to the wrong variables). If this is suspected, data entry may be +typed in one value at a time to observe its synchronization with +PRINT statements in the program. + There is no defined escape from an input request, but if an +invalid expression is typed (such as a period or a pair of commas) an +invalid expression error stop will occur. + + Because Tiny Basic does not allow arrays, about the only way to +process large volumes of data is through paper tape files. Each +input request prompt consists of a question mark followed by an X-ON +(ASCII DC1) control character to turn on an automatic paper tape +reader on the Teletype (if it is ready). A paper tape may be +prepared in advance with data separated by commas, and an X-OFF +(ASCII DC3 or Control-S) control character preceding the CR (a +Teletype will generally read at least one more character after the +X-OFF). In this way the tape will feed one line at a time, as +requested by the succession of INPUT statements. This tape may also +be prepared from a previous program output (see the PRINT +statement). + + + + + + LET var = expression + var = expression + + This statement assigns the value of the expression to the +variable (var). The long form of this statement (i.e. with the +keyword LET) executes slightly faster than the short form. The +following are valid LET statements: + + LET A = B+C + I = 0 + LET Q = RND (RND(33)+5) + + + + + + + + + + + + + + + + + + + + 10 + + GOTO expression + + The GOTO statement permits changes in the sequence of program +execution. Normally programs are executed in the numerical sequence +of the program line numbers, but the next statement to be executed +after a GOTO has the line number derived by the evaluation of the +expression in the GOTO statement. Note that this permits you to +compute the line number of the next statement on the basis of program +parameters during program execution. An error stop occurs if the +evaluation of the expression results in a number for which there is +no line. If a GOTO statement is executed directly, it has the same +effect as if it were the first line of a program, and the RUN +statement were typed in, that is, program execution begins from that +line number, even though it may not be the first in the program. +Thus a program may be continued where it left off after correcting +the cause of an error stop. The following are valid GOTO +statements: + GOTO 100 + GO TO 200+I*10 + G 0 T 0 X + + + + + + + GOSUB expression + + The GOSUB statement is like the GOTO statement, except that TINY +remembers the line number of the GOSUB statement, so that the next +occurrence of a RETURN statement will result in execution proceeding +from the statement following the GOSUB. Subroutines called by GOSUB +statements may be nested to any depth, limited only by the amount of +user program memory remaining. Note that a GOSUB directly executed +may result in an error stop at the corresponding RETURN. The +following are some examples of valid GOSUB statements: + GOSUB 100 + GO SUB 200+I*10 + + + + + + +RETURN + + The RETURN statement transfers execution control to the line +following the most recent unRETURNed GOSUB. If there is no matching +GOSUB an error stop occurs. + + + + + + + + + 11 + + IF expression rel expression THEN statement + IF expression rel expression statement + + The IF statement compares two expressions according to one of +six relational operators. If the relationship is True, the statement +is executed; if False, the associated statement is skipped. The six +relational operators are: + = equality + < less than + > greater than + <= less or equal (not greater) + >= greater or equal (not less) + <>, >< not equal (greater or less) + + The statement may be any valid TINY BASIC statement (including +another IF statement). The following are valid IF statements: + IF I>25 THEN PRINT "ERROR" + IF N/P*P=N GOTO 100 + IF 1=2 Then this is nonsense + IF RND (100) > 50 THEN IF I <> J INPUT Q,R + + + + + + + END + + The END statement must be the last executable statement in a +program. Failure to include an END statement will result in an error +stop after the last line of the program is executed. The END +statement may be used to terminate a program at any time, and there +may be as many END statements in a program as needed. The END +statement also clears out any saved GOSUB line numbers remaining, and +may be used for that purpose in the direct execution mode. + + + + + + + REM comments + + The REM statement permits comments to be interspersed in the +program. Its execution has no effect on program operation, except +for the time taken. + + + + + + + + + + + + 12 + + CLEAR + + The CLEAR statement formats the user program space, deleting +any previous programs. If included in a program (i.e. with a line +number) the program becomes suicidal when the statement is executed, +although no error results. If the Warm Start is used to initialize +the interpreter, this must be the first command given. + + + + RUN + RUN,expression-list + + The RUN statement is used to begin program execution at the +first (lowest) line number. If the RUN statement is directly +executed, it may be followed by a comma, followed by values to be +input when the program executes an INPUT statement. + If the RUN statement is included in a program with a line +number, its execution works like a GO TO first statement of the +program. + + + + LIST + LIST expression + LIST expression,expression + + The LIST statement causes part or all of the user program to be +listed. If no parameters are given, the whole program is listed. A +single expression parameter in evaluated to a line number which, if +it exists, is listed. If both expression parameters are given, all +of the lines with line numbers between the two values (inclusive) are +listed. If the last expression in the LIST statement evaluates to a +number for which there is no line, the next line above that number +which does exist (if any) is listed as the last line. Zero is not a +valid line number, and an error stop will occur if one of the +expressions evaluates to zero. A LIST statement may be included as +part of the program, which may be used for printing large text +strings such as instructions to the operator. A listing may be +terminated by the Break key. + If the terminal punch (or cassette recorder) is turned on for a +LIST operation, the tape may be saved to reload the program into TINY +at a later time. + The following are valid LIST statements: + LIST + LIST 75+25 (lists line 100) + LIST 100,200 + LIST 500,400 (lists nothing) + + + + + + + + + + 13 + + A P P E N D I X A + + ERROR MESSAGE SUMMARY + + +0 Break during execution +8 Memory overflow; line not inserted +9 Line number 0 not allowed +13 RUN with no program in memory +18 LET is missing a variable name +20 LET is missing an = +23 Improper syntax in LET +25 LET is not followed by END +34 Improper syntax in GOTO +37 No line to GO TO +39 Misspelled GOTO +40,41 Misspelled GOSUB +46 GOSUB subroutine does not exist +59 PRINT not followed by END +62 Missing close quote in PRINT string +73 Colon in PRINT is not at end of statement +75 PRINT not followed by END +95 IF not followed by END +104 INPUT syntax bad - expects variable name +123 INPUT syntax bad - expects comma +124 INPUT not followed by END +132 RETURN syntax bad +133 RETURN has no matching GOSUB +134 GOSUB not followed by END +139 END syntax bad +154 Can't LIST line number 0 +164 LIST syntax error - expects comma +183 REM not followed by END +184 Missing statement type keyword +186 Misspelled statement type keyword +188 Memory overflow: too many GOSUB's ... +211 ... or expression too complex +224 Divide by 0 +226 Memory overflow +232 Expression too complex ... +233 ... using RND ... +234 ... in direct evaluation; +253 ... simplify the expression +259 RND (0) not allowed +266 Expression too complex ... +267 ... for RND +275 USR expects "(" before arguments +284 USR expects ")" after arguments +287 Expression too complex ... +288 ... for USR +290 Expression too complex +293 Syntax error in expression - expects value +296 Syntax error - expects ")" +298 Memory overflow (in USR) +303 Expression too complex (in USR) + + + 14 + +304 Memory overflow (in function evaluation) +306 Syntax error - expects "(" for function arguments +330 IF syntax error - expects relation operator + + Other error message numbers may possibly occur if the +interpreter is malfunctioning. If this happens, check the program in +memory, or reload it, and try again. + + Error number 184 may also occur if TINY BASIC is incorrectly +interfaced to the keyboard input routines. A memory dump of the +input line buffer may disclose this kind of irregularity. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 15 + + A P P E N D I X B + + FORMAL DEFINITION OF TINY BASIC + + +line ::= number statement CR + statement CR +statement ::= PRINT printlist + PR printlist + INPUT varlist + LET var = expression + var = expression + GOTO expression + GOSUB expression + RETURN + IF expression relop expression THEN statement + IF expression relop expression statement + REM commentstring + CLEAR + RUN + RUN exprlist + LIST + LIST exprlist +printlist ::= + printitem + printitem : + printitem separator printlist +printitem ::= expression + "characterstring" +varlist ::= var + var , varlist +exprlist ::= expression + expression , exprlist +expression ::= unsignedexpr + + unsignedexpr + - unsignedexpr +unsignedexpr ::= term + term + unsignedexpr + term - unsignedexpr +term ::= factor + factor * term + factor / term +factor ::= var + number + ( expression ) + function +function ::= RND ( expression ) + USR ( exprlist ) +number ::= digit + digit number +separator ::= , ! ; +var ::= A ! B ! ... ! Y ! Z +digit ::= 0 ! 1 2 ! ... ! 9 +relop ::= < ! > ! = ! <= ! >= ! <> ! >< + + + + 16 + + + A P P E N D I X C + + IMPLEMENTING I/O ROUTINES + +COSMAC + +COSMAC TINY occupies the same space as 6800 TINY -- 0100-08FF. +Similarly, the general parameters occupy 0020-00B7, as defined in +the manual. However, COSMAC TINY also uses locations 0011-001F to +contain copies of interpreter parameters and other run-time data; do +not attempt to use these locations while running TINY. + +Like all Itty Bitty Computer software, COSMAC TINY contains no I/O +instructions (nor references to Q or EF1-4), no interrupt enables or +disables, and no references to an operating system. The three jumps +(LBR instructions) at 0106, 0109, and 010C provide all necessary +I/O, as defined in the manual. If you are using UT3 or UT4, you may +insert the following LBR instructions, which jump to the necessary +interface routines: + + .. LINKS TO UT3/4 + 0106 C0076F LBR UTIN + 0109 C00776 LBR UTOUT + 010C C00766 LBR UTBRK + +If you are not using the RCA monitor, you must write your own I/O +routines. For this the standard subroutine call and return linkages +are used, except that D is preserved through calls and returns by +storing it in RF.1. Registers R2-RB and RD contain essential +interpreter data, and if the I/O routines make any use of any of +them they should be saved and restored. Note however, that R2-R6 +are defined in the customary way and may be used to nest subroutine +calls if needed. R0, R1, RC, RE and RF are available for use by the +I/O routines, as is memory under R2. Both the call and return +linkages modify X and the I/O data character is passed in the +accumulator ("D", not RD). + +After connecting TINY to the I/O routines, start the processor at +0100 (the Cold Start). Do not attempt to use the Warm Start without +entering the Cold Start at least once to set up memory from +0011-0023. Any register may be serving as program counter when +entering either the Cold Start or the Warm Start. + +The USR function works the same way as described in the manual, +except that the second argument in the call is loaded into R8, and +the third argument is loaded into RA with the least significant +byte also in the Accumulator. On return RA.1 and the accumulator +contain the function value (RA.0 is ignored). The machine language +subroutine must exit by a SEP R5 instruction. USR machine language +subroutines may use R0, R1, R8, RA, RC-RF, so long as these do not +conflict with I/O routine assignments. TINY BASIC makes no internal +use of R0, R1, RC, or RE. + +RCA Corporation funded the development of COSMAC TINY BASIC, and it +is by RCA's permission that it is made available. + + 17 + If you do not have access to a monitor in ROM with ASCII I/O +built in, you will have to write your own I/O routines. Most likely +you have something connected to a parallel port for the keyboard +input; output may be some parallel port also, or you may want to +use the 1861 video display for a gross dot-matrix kind of text +display. For the moment, let's assume you have parallel ports, +Port C (N=1100) for input, and port 4 (N=0100) for output. Assume +also that EF4 controls both input and output. This is the situation +you would have if you took an ordinary ELF and used the hex input +and display with the single "input" button to step through the +characters. You need for this configuration, two routines, which +might look something like this: + 0106 C0 00E0 LBR KEYIN + 0109 C0 00E7 LBR DISPL + ... + 00E0 3FE0 KEYIN BN4 * + 00E2 E2 SEX 2 + 00E3 6C INP 4 + 00E4 37E4 B4 * + 00E6 68 LSKP + 00E7 3FE7 DISPL BN4 * + 00E9 E2 SEX 2 + 00EA 73 STXD + 00EB 52 STR 2 + 00EC 64 OUT 4 + 00ED 37ED B4 * + 00EF D5 SEP 5 + Of course if you have a keyboard on Port F you will change +the INP instruction to match; if the keyboard pulls EF3 down, then +you must change the first pair of BN4/B4 instructions to BN3/B3 +instructions and change the LSKP to a NOP (C4 or E2). If your +input comes from some device that already displayed the character +typed, then change the LSKP to a Return (D5). + Similarly, if the output is to a different port you must +change the OUT instruction to fit it, and the second pair of BN4/B4 +instructions to match the control line being used. Notice that +the LSKP instruction is only there to prevent your waiting on the +EF4 line twice for each keyin, and should be removed (changed to +a NOP) as soon as you connect up real input and output ports. + Many 1802 systems come equipped with a video output using +the 1861 chip. If you have this, you should get a copy of the +February and April 1979 issues of KILOBAUD MICROCOMPUTING (formerly +just KILOBAUD). I have a two-part article published in these two +issues which explains how to put text on the 1861 graphics display, +with particular emphasis on how to connect it to TINY BASIC. + So far I have not mentioned the Break test. If you leave +that part unchanged, Tiny will work just fine, but you cannot stop +a listing or a program that is getting too long. After you get +your keyboard and display connected up and working, you may want +to use EF4 (or some other flag) as a Break input. It is possible +to use the same flag for Break as for "input ready", if you want +Tiny to stop executing when you press a key on your keyboard (this +does not affect the INPUT instruction, which is obviously waiting +for that keyin). This code will do that: + 010C C000F0 LBR BRKT + ... + 00F0 FC00 BRKT ADI 0 + 00F2 3FF6 BN4 EXIT + 00F4 FF00 SMI 0 + 00F6 D5 EXIT SEP R5 + Notice that the only function of this routine is to set the +Carry (DF) when EF4 is true (low) and clear it otherwise. + + 18 + +KIM + + The Teletype I/O routines in the MOS Technology KIM system may +be used for the character input and output requirements of TINY BASIC +6502. The following break routine is included in Tiny to test the +serial data line at 1740; Since TINY BASIC 6502 does not use the +lower part of memory page 01, the break test routine is ORG'ed to +execute in that space: + + ; BREAK TEST FOR KIM +0100 AD4017 KIMBT LDA KTTY LOOK AT TTY +0103 18 CLC C=O IF IDLE +0104 300E BMI KIMX IDLE +0106 AD4017 LDA KTTY WAIT FOR END +0109 10FB BPL *-3 +010B 200E01 KLDY JSR *+3 +010E A9FF LDA #255 DELAY 2 RUBOUT TIMES +0110 20A01E JSR OUTCH +0113 38 SEC C=1 IF BREAK +0114 60 KIMX RTS + + To run TINY BASIC 6502 load the paper tape into your Teletype +reader, type "L", and turn on the reader. Then key in the following +Jumps: + + ; JUMPS TO KIM +0206 4C5A1E JMP GETCH CHARACTER INPUT +0209 4CA01E JMP OUTCH CHARACTER OUTPUT +020C 4C0001 JMP KIMBT BREAK TEST + + It is recommended that you save a copy of memory on tape +(0100-0114 and 0200-0AFF) before going any further. Or you may +prefer to save it on audio cassette. Set up the starting address for +Tiny at 0200, and type "G". + Because of the awkwardness of putting memory in the 4K gap left +in the KIM-1 system, an alternate version is available which executes +out of 2000-28FF. For this version the Cold Start is at 2000 and +other addresses are at 200x instead of 020x (cf. 010x in Appendix D). + + +JOLT or TIM + + JOLT systems may not always have memory loaded in the space +from 0200 on up, so a special version has been prepared in which the +interpreter resides in the space 1000-18FF. This is the only +difference between the JOLT version and the KIM version, so if your +JOLT or TIM system has contiguous memory from Page 00 you may prefer +to use the KIM version to gain the extra memory space. Since the +serial data in the JOLT/TIM systems is not the same as KIM, a special +break test routine has also been provided for those systems: + + ; JOLT BREAK TEST +0115 A901 JOLBT LDA #1 LOOK AT TTY +0117 2C026E BIT JTTY +011A 18 CLC C=0 IF IDLE + + + 19 + +011B F00E BEQ JOLTX IDLE +011D 2C026E BIT JTTY WAIT FOR END +0120 D0FB BNE *-3 +0122 202501 JSR *+3 DELAY TWO CH TIMES +0125 A9FF LDA #255 +0127 20C672 JSR WRT +012A 38 SEC C=1 = BREAK +012B 60 JOLTX RTS + + To run, load the paper tape into your Teletype reader and type +"LH". Then key in the following Jumps: + + ; JUMPS TO JOLT/TIM +1006 4CE972 JMP RDT CHARACTER INPUT +1009 4CC672 JMP WRT CHARACTER OUTPUT +100C 4C1501 JMP JOLBT BREAK TEST + + As with other versions, the Cold start is the beginning of the +program (1000). + + + +MIKBUG + + Systems that use MIKBUG (TM Motorola) for console I/O may use +the I/O routines in MIKBUG. The following break routine is provided +in Tiny to test the PIA at 8004: + + * BREAK TEST FOR MIKBUG + +B68004 BREAK LDA A PIAA LOOK AT PIA +0C CLC C=0 IF NONE +2B0D BMI EXIT +B68004 LDA A PIAA +2AFB BPL *-3 WAIT FOR END +8D00 BSR *+2 +86FF LDA A #$FF DELAY ONE +BD0109 JSR TYPE CHARACTER TIME +0D SEC C=1 IF BREAK +39 EXIT RTS + + To run, load the paper tape into your Teletype reader and type +"L". Then key in the following Jumps: + + * JUMPS TO MIKBUG + ORG $0106 +0106 7EE1AC JMP $E1AC CHARACTER INPUT +0109 7EE1D1 JMP $E1D1 CHARACTER OUTPUT +010C 7E08FD JMP $08FD BREAK TEST + + It is recommended that you save a copy of memory on tape +(0100-08FF) before going any further. Set the starting address in +A048-A049 to 0100 and type "G". For your convenience the Cold Start +entry leaves the Warm start entry set up in the Mikbug stack, so that +after a reset a simple "G" command will result in a Warm start and +preserve the user programs. + + 20 + +OTHER + + For standard systems (and for special systems with I/O other +than that provided), subroutines must be supplied by the user to +interface TINY to the operator. For ACIA input or output the +following routines may be used, or they may serve as examples for +your coding (6800 opcodes are shown). They should be assembled for +your ACIA address, and in some memory location which is not +contiguous with the TINY BASIC user program memory (which may be +destroyed by the Cold Start). If nothing else is available, +locations 00D8-00FF are not used by Tiny and may be used for this +purpose. + + * + * ACIA I/O + * +B6XXXX BREAK LDA A ACIA +47 ASR A CHECK FOR TYPEIN +2406 BCC BRX NO, NOT BREAK +B6XXXY LDA A ACIA+1 GET IT +2601 BNE BRX NOT NULL IS BREAK +0C CLC IGNORE NULLS +39 BRX RTS +B6XXXX INPUT LDA A ACIA +47 ASR A +24FA BCC INPUT WAIT FOR A CHARACTER +B6XXXY LDA A ACIA+1 GET IT +36 OUTPUT PSH A SAVE CHARACTER +B6XXXX LDA A ACIA +8402 AND A #2 WAIT FOR READY +27F9 BEQ OUTPUT+1 +32 PUL A +B7XXXY STA A ACIA+1 OUTPUT CHARACTER +39 RTS + + Note that this routine will accept any non-null character +typein as a break. Alternatively we could look at the Framing Error +status, but if a character has been input this status will not show +up until that character is read in, rendering it ineffective in some +cases. Nulls are excepted as break characters since one or more of +them may follow the carriage return in an input tape, and still be +pending. Note that for this to work properly, the pad character +defined in location 0111 should be set to NULL (hex 00). + + The 6800 "R" version of TINY BASIC includes these routines in +the code, as shown here. Locations 08FA-08FC contain a JMP to the +break test at the beginning of this block. You should alter the ACIA +addresses to suit your system before using the subroutines. + + + + + + + + + + 21 + +CRT OR TVT + + If a TV Typewriter is used for I/O it may be desirable to +remove excess control characters from the output stream. All +controls except Carriage Return may be removed by the following +instructions at the beginning of the output subroutine (6800 opcodes +shown): + +39 RTS +810A OUTPUT CMP A #0A +2FFB BLE OUTPUT-1 + +Only nulls, Rubouts, X-ON and X-OFF may be deleted by changing the +CMP to a TST A. Nulls may be passed through by also changing the BLE +to a BMI. + + Some TV Typewriters do not scroll up when the cursor reaches +the bottom of the screen, but rather wrap the cursor around to the +top of the screen, writing over the previously displayed data. With +this kind of display it is essential that the I/O routines (or the +hardware) clear to the end of the line whenever a CR-LF is output, +so that previous data does not interfere with the new. If your I/O +routines are fixed in ROM, some sort of preprocessor may be required +to recognize output CR's and convert them to the appropriate sequence +of control functions. It may also be necessary to trap input CR's +(suppressing their echo) since Tiny generally responds with both +another CR and a linefeed. + + Some users prefer to concatenate all output into one "line" of +print, using the terminal comma or semicolon to suppress the line +breaks. Since TINY was designed to limit line lengths to less than +128 characters, if this sort of concatenation is attempted it will +appear that TINY has quit running. To eliminate the print +suppression the most significant two bits of the print control byte +(location 00BF in most versions) may be cleared to zero periodically +with the USR function or in the output driver routine. The least +significant three bits of this same byte are used for the "comma +spacing" in the PRINT statement, and should be left unaltered. + + + +CASSETTE I/O + + Officially, TINY only speaks to one peripheral--the console. +However a certain amount of file storage may be simulated by +attaching these peripherals (such as cassette systems) to the +character input and output routines. If the same electrical and +software interface is used this is very easy. Otherwise the I/O +drivers will require special routines to recognize control characters +in the input and output data for setting internal switches which +select one of several peripherals. The USR function may also be +used either to directly call I/O routines or to alter switches in +memory. + + + + + 22 + + + + + + A P P E N D I X D + + LOW MEMORY MAP + + +LOCATION SIGNIFICANCE +-------- ------------ + +0000-000F Not used by any version of TINY +0011-001F COSMAC version temporaries +0020-0021 Lowest address of user program space +0022-0023 Highest address of program space +0024-0025 Program end + stack reserve +0026-0027 Top of GOSUB stack +0028-002F Interpreter parameters +0030-007F Input line buffer & Computation stack +0080-0081 Random Number Generator workspace +0082-0083 Variable "A" +0084-0085 Variable "B" +... ... +00B4-00B5 Variable "Z" +00B6-00C7 Interpreter temporaries +00B8 Start of User program (PROTO) +00C8-00D7 Sphere parameters (not 0020-002F) +00D8-00FF Unused by standard version + +0100 Cold Start entry point (6800) +0103 Warm Start entry point +0106-0108 JMP (or JSR) to character input +0109-010B JMP to character output +010C-010E JMP to Break test +010F Backspace code +0110 Line Cancel code +0111 Pad character +0112 Tape Mode Enable flag (hex 80 = enabled) +0113 Spare stack size +0114 Subroutine to read one Byte + from RAM to A (address in X) +0118 Subroutine to store A into RAM + at address in X + +0900 Beginning of User program (6800) + +Note that some of these addresses apply to the standard 6800 version. +For other versions addresses above 0100 should be read as addresses +above their respective starting address. + + + + + + + + 23 + + A P P E N D I X E + + AN EXAMPLE PROGRAM + + +10 REM DISPLAY 64 RANDOM NUMBERS < 100 ON 8 LINES +20 LET I=0 +30 PRINT RND (100), +40 LET I=I+1 +50 IF I/8*8=I THEN PRINT +60 IF I<64 THEN GOTO 30 +70 END + + +100 REM PRINT HEX MEMORY DUMP +109 REM INITIALIZE +110 A=-10 +120 B=-11 +130 C=-12 +140 D=-13 +150 E=-14 +160 F=-15 +170 X = -1 +175 O = 0 +180 LET S = 256 +190 REMARK: S IS BEGINNING OF TINY (IN DECIMAL) +200 REM GET (HEX) ADDRESSES +210 PRINT "DUMP: L,U"; +215 REM INPUT STARTING ADDRESS IN HEX +220 GOSUB 500 +230 L=N +235 REM INPUT ENDING ADDRESS IN HEX +240 GOSUB 500 +250 U=N +275 REM TYPE OUT ADDRESS +280 GOSUB 450 +290 REM GET MEMORY BYTE +300 LET N = USR (S+20,L) +305 REM CONVERT IT TO HEX +310 LET M = N/16 +320 LET N = N-M*16 +330 PRINT " "; +335 REM PRINT IT +340 GOSUB 400+M+M +350 GOSUB 400+N+N +355 REM END? +360 IF L=U GO TO 390 +365 L=L+1 +370 IF L/16*16 = L GOTO 280 +375 REM DO 16 BYTES PER LINE +380 GO TO 300 +390 PRINT +395 END +399 PRINT ONE HEX DIGIT +400 PRINT O; + + + 24 + +401 RETURN +402 PRINT 1; +403 RETURN +404 PRINT 2; +405 RETURN +406 PRINT 3; +407 RETURN +408 PRINT 4; +409 RETURN +410 PRINT 5; +411 RETURN +412 PRINT 6; +413 RETURN +414 PRINT 7; +415 RETURN +416 PRINT 8; +417 RETURN +418 PRINT 9; +419 RETURN +420 PRINT "A"; +421 RETURN +422 PRINT "B"; +423 RETURN +424 PRINT "C"; +425 RETURN +426 PRINT "D"; +427 RETURN +428 PRINT "E"; +429 RETURN +430 PRINT "F"; +431 RETURN +440 REM PRINT HEX ADDRESS +450 PRINT +455 REM CONVERT IT TO HEX +460 N = L/4096 +470 IF L<0 N=(L-32768)/4096+8 +480 GOSUB 400+N+N +483 LET N=(L-N*4096) +486 GOSUB 400+N/256*2 +490 GOSUB 400+(N-N/256*256)/16*2 +495 GOTO 400+(N-N/16*16)*2 +496 GOTO=GOSUB,RETURN +500 REM INPUT HEX NUMBER +501 REM FORMAT IS NNNNX +502 REM WHERE "N" IS ANY HEX DIGIT +505 N=0 +509 REM INPUT LETTER OR STRING OF DIGITS +510 INPUT R +520 IF R=X RETURN +525 REM CHECK FOR ERROR +530 IF R>9999 THEN PRINT "BAD HEX ADDRESS +531 REM NOTE ERROR STOP ON LINE 530 (ON PURPOSE!) +535 REM CONVERT INPUT DECIMAL DIGITS TO HEX +540 IF R>999 THEN N=N*16 +545 IF R>99 THEN N=N*16 +550 IF R>9 THEN N=N*16 + + 25 + +555 IF R>0 THEN R=R+R/1000*1536+R/100*96+R/10*6 +559 REM PICK UP NON-DECIMAL DIGIT LETTERS +560 IF R<0 THEN LET R=-R +565 REM ADD NEW DIGIT TO PREVIOUS NUMBER +570 LET N=N*16+R +580 GOTO 510 +590 NOTE: DON'T NEED END HERE + + +1000 TO RUN RANDOM NUMBER PROGRAM, TYPE "RUN" +1010 IT WILL TYPE 8 LINES THEN STOP. +1020 TO RUN HEX DUMP PROGRAM TYPE "GOTO 100" +1030 IT WILL ASK FOR INPUT, TYPE 2 HEX ADDRESSES +1040 EACH TERMINATED BY THE LETTER X, +1050 AND SEPARATED BY A COMMA +1055 (TYPE ALL ZEROS AS LETTER OH). +1060 THE PROGRAM WILL DUMP MEMORY BETWEEN +1070 THOSE TWO ADDRESSES, INCLUSIVE. +1080 EXAMPLE: +1090 :GOTO 100 +1100 DUMP: L,U? AO3EX,AO46X +1110 A03E EE FF +1120 A040 00 11 22 33 44 55 66 +1130 IF THE RANDOM NUMBER PROGRAM +1140 IS REMOVED, OR IF YOU TYPE IN +1150 :1 GOTO 100 +1160 THEN YOU CAN GET THE SAME DUMP BY TYPING +1170 :RUN,AO3EX,AO46X +1180 . +1190 NOTE THAT THIS PROGRAM DEMONSTRATES NEARLY +1200 EVERY FEATURE AVAILABLE IN TINY BASIC. + + + +REMARK: TO FIND OUT HOW MUCH PROGRAM SPACE +REM... YOU HAVE LEFT, TYPE: +LET I=0 +1 LET I=I+2 +2 GOSUB 1 +RUN +REMARK: AFTER A FEW SECONDS, THIS WILL STOP +REM... WITH AN ERROR; THEN TYPE: +END +PRINT "THERE ARE ";I;" BYTES LEFT" + + +REM: TO EXIT FROM TINY BASIC TO YOUR MONITOR/DEBUGGER, +LET S=256 +REM (S AS IN LINE 180 ABOVE) +LET B=0 +IF P=6800 THEN LET B=63 +REM: B IS SWI OR BRK INSTRUCTION +LET A = USR (S+24,0,B) + USR (0) +REM: THE FIRST CALL STORES A BREAK IN 0000 +REM... THE SECOND CALL JUMPS TO IT. + + + 26 \ No newline at end of file diff --git a/programs/develop/tinybasic/TinyBasic.c b/programs/develop/tinybasic/TinyBasic.c new file mode 100644 index 0000000000..d08d45c4d8 --- /dev/null +++ b/programs/develop/tinybasic/TinyBasic.c @@ -0,0 +1,1541 @@ +/* Tiny Basic Intermediate Language Interpreter -- 2004 July 19 */ + +#include +#include +#include /* added 08 Oct 31 */ + +#if defined(__TINYC__) +#include +#define printf con_printf /* siemargl for smaller @tinyC */ +#endif +char *ExplainErr(int code); + + +/* Default input/output file names, if defined (omit otherwise)... */ +#define DefaultInputFile "TBasm.txt" +#define DefaultOutputFile "TBout.txt" + +/* File input/output function macros (adjust for C++ framework) */ +#define FileType FILE* +#define IoFileClose(fi) fclose(fi) +#define InFileChar(fi) CfileRead(fi) +#define OutFileChar(fi,ch) fputc(ch,fi) +#define ScreenChar(ch) printf("%c",ch) +#define KeyInChar (char)getchar() +#define NeedsEcho true +#define BreakTest Broken + +/* File input/output function macros (Qt examples:) */ +/* #define FileType QFile* */ +/* #define IoFileClose(fi) fi->close() */ +/* #define InFileChar(fi) (fi->atEnd()?'\0':fi->getch()) */ +/* #define OutFileChar(fi,ch) fi->putch(ch) */ + +char CfileRead(FileType fi) { /* C file reader, returns '\0' on eof */ + int chn = fgetc(fi); + if (chn == EOF) return '\0'; + return (char)chn;} /* ~CfileRead */ + +/* Constants: */ + +#define aByte unsigned char +#define CoreTop 65536 /* Core size */ +#define UserProg 32 /* Core address of front of Basic program */ +#define EndUser 34 /* Core address of end of stack/user space */ +#define EndProg 36 /* Core address of end of Basic program */ +#define GoStkTop 38 /* Core address of Gosub stack top */ +#define LinoCore 40 /* Core address of "Current BASIC line number" */ +#define ILPCcore 42 /* Core address of "IL Program Counter" */ +#define BPcore 44 /* Core address of "Basic Pointer" */ +#define SvPtCore 46 /* Core address of "Saved Pointer" */ +#define InLine 48 /* Core address of input line */ +#define ExpnStk 128 /* Core address of expression stack (empty) */ +#define TabHere 191 /* Core address of output line size, for tabs */ +#define WachPoint 255 /* Core address of debug watchpoint USR */ +#define ColdGo 256 /* Core address of nominal restart USR */ +#define WarmGo 259 /* Core address of nominal warm start USR */ +#define InchSub 262 /* Core address of nominal char input USR */ +#define OutchSub 265 /* Core address of nominal char output USR */ +#define BreakSub 268 /* Core address of nominal break test USR */ +#define DumpSub 273 /* Core address of debug core dump USR */ +#define PeekSub 276 /* Core address of nominal byte peek USR */ +#define Peek2Sub 277 /* Core address of nominal 2-byte peek USR */ +#define PokeSub 280 /* Core address of nominal byte poke USR */ +#define TrLogSub 283 /* Core address of debug trace log USR */ +#define BScode 271 /* Core address of backspace code */ +#define CanCode 272 /* Core address of line cancel code */ +#define ILfront 286 /* Core address of IL code address */ +#define BadOp 15 /* illegal op, default IL code */ + /* Pascal habits die hard.. */ +#define true 1 +#define false 0 + +/* debugging stuff... */ +#define DEBUGON 1 /* 1 enables \t Debugging toggle, 0 disables */ +#define LOGSIZE 4096 /* how much to log */ +static int Debugging = 0; /* >0 enables debug code */ +int DebugLog[LOGSIZE]; /* quietly logs recent activity */ +int LogHere = 0; /* current index in DebugLog */ +int Watcher = 0, Watchee; /* memory watchpoint */ + +/* Static/global data: */ +aByte Core[CoreTop]; /* everything goes in here */ +aByte DeCaps[128]; /* capitalization table */ +int Lino, ILPC; /* current line #, IL program counter */ +int BP, SvPt; /* current, saved TB parse pointer */ +int SubStk, ExpnTop; /* stack pointers */ +int InLend, SrcEnd; /* current input line & TB source end */ +int UserEnd; +int ILend, XQhere; /* end of IL code, start of execute loop */ +int Broken = false; /* =true to stop execution or listing */ +FileType inFile = NULL; /* from option '-i' or user menu/button */ +FileType oFile = NULL; /* from option '-o' or user menu/button */ + +/************************* Memory Utilities.. *************************/ + +void Poke2(int loc, int valu) { /* store integer as two bytes */ + Core[loc] = (aByte)((valu>>8)&255); /* nominally Big-Endian */ + Core[loc+1] = (aByte)(valu&255);} /* ~Poke2 */ + +int Peek2(int loc) { /* fetch integer from two bytes */ + return ((int)Core[loc])*256 + ((int)Core[loc+1]);} /* ~Peek2 */ + +/************************** I/O Utilities... **************************/ + +void Ouch(char ch) { /* output char to stdout */ + if (oFile != NULL) { /* there is an output file.. */ + if (ch>=' ') OutFileChar(oFile,ch); + else if (ch == '\r') OutFileChar(oFile,'\n');} + if (ch=='\r') { + Core[TabHere] = 0; /* keep count of how long this line is */ + ScreenChar('\n');} + else if (ch>=' ') if (ch<='~') { /* ignore non-print control chars */ + Core[TabHere]++; + ScreenChar(ch);}} /* ~Ouch */ + +char Inch(void) { /* read input character from stdin or file */ + char ch; + if (inFile != NULL) { /* there is a file to get input from.. */ + ch = InFileChar(inFile); + if (ch == '\n') ch = '\r'; + if (ch == '\0') { /* switch over to console input at eof */ + IoFileClose(inFile); + inFile = NULL;} + else { + Ouch(ch); /* echo input to screen (but not output file) */ + return ch;}} + ch = KeyInChar; /* get input from stdin */ + if (ch == 0) exit(0); /* Kolibri specific - our window was killed */ + + if (ch==13) printf ("\n"); + if (ch==8) printf ("\b "); + if (NeedsEcho) ScreenChar(ch); /* alternative input may need this */ + if (oFile != NULL) OutFileChar(oFile,ch); /* echo it to output file */ + if (ch == '\n') { + ch = '\r'; /* convert line end to TB standard */ + Core[TabHere] = 0;} /* reset tab counter */ + return ch;} /* ~Inch */ + +int StopIt(void) {return BreakTest;} /* ~StopIt, .. not implemented */ + +void OutStr(char* theMsg) { /* output a string to the console */ + while (*theMsg != '\0') Ouch(*theMsg++);} /* ~OutStr */ + +void OutLn(void) { /* terminate output line to the console */ + OutStr("\r");} /* ~OutLn */ + +void OutInt(int theNum) { /* output a number to the console */ + if (theNum<0) { + Ouch('-'); + theNum = -theNum;} + if (theNum>9) OutInt(theNum/10); + Ouch((char)(theNum%10+48));} /* ~OutInt */ + +/*********************** Debugging Utilities... ***********************/ + +void OutHex(int num, int nd) { /* output a hex number to the console */ + if (nd>1) OutHex(num>>4, nd-1); + num = num&15; + if (num>9) Ouch((char)(num+55)); + else Ouch((char)(num+48));} /* ~OutHex */ + +void ShowSubs(void) { /* display subroutine stack for debugging */ + int ix; + OutLn(); OutStr(" [Stk "); OutHex(SubStk,5); + for (ix=SubStk; ix>1)&31; /* whom is a specified var, or 0 */ + valu = whom;} + OutLn(); OutStr(" [Vars"); + for (ix=valu; ix<=whom; ix++) { /* all non-zero vars, or else whom */ + valu = (int)((short)Peek2(ix*2+ExpnStk)); + if (valu==0) if (prior==0) continue; /* omit multiple 0s */ + prior = valu; + OutStr(" "); + Ouch((char)(ix+64)); /* show var name */ + OutStr("="); + OutInt(valu);} + OutStr("]");} /* ~ShowVars */ + +void ShoMemDump(int here, int nlocs) { /* display hex memory dump */ + int temp, thar = here&-16; + while (nlocs>0) { + temp = thar; + OutLn(); + OutHex(here,4); + OutStr(": "); + while (thar0) OutHex(Core[here],2); + else OutStr(" ");} + while (++here%16 !=0); + OutStr(" "); + while (temp= nlocs+16) break; + temp = Core[thar++]; + if (temp == (int)'\r') Ouch('\\'); + else if (temp<32) Ouch('`'); + else if (temp>126) Ouch('~'); + else Ouch((char)temp);}} + OutLn();} /* ~ShoMemDump */ + +void ShoLogVal(int item) { /* format & output one activity log item */ + int valu = DebugLog[item]; + OutLn(); + if (valu < -65536) { /* store to a variable */ + Ouch((char)(((valu>>17)&31)+64)); + OutStr("="); + OutInt((valu&0x7FFF)-(valu&0x8000));} + else if (valu < -32768) { /* error # */ + OutStr("Err "); + OutInt(-valu-32768);} + else if (valu<0) { /* only logs IL sequence changes */ + OutStr(" IL+"); + OutHex(-Peek2(ILfront)-valu,3);} + else if (valu<65536) { /* TinyBasic line # */ + OutStr("#"); + OutInt(valu);} + else { /* poke memory byte */ + OutStr("!"); + OutHex(valu,4); + OutStr("="); + OutInt(valu>>16);}} /* ~ShoLogVal */ + +void ShowLog(void) { /* display activity log for debugging */ + int ix; + OutLn(); + OutStr("*** Activity Log @ "); + OutInt(LogHere); + OutStr(" ***"); + if (LogHere >= LOGSIZE) /* circular, show only last 4K activities */ + for (ix=(LogHere&(LOGSIZE-1)); ix65535) { + Poke2(EndUser,65534); + Poke2(65534,0xDEAD);} + else Poke2(EndUser,CoreTop); + WarmStart(); + SrcEnd = Peek2(UserProg); + Poke2(SrcEnd++,0); + Poke2(EndProg,++SrcEnd);} /* ~ColdStart */ + +void TBerror(void) { /* report interpreter error */ + if (ILPC == 0) return; /* already reported it */ + OutLn(); + LogIt(-ILPC-32768); + OutStr("Tiny Basic error #"); /* IL address is the error # */ + OutInt(ILPC-Peek2(ILfront)); + // siemargl - add textual explain + OutStr(" - "); + OutStr(ExplainErr(ILPC-Peek2(ILfront))); + + if (Lino>0) { /* Lino=0 if in command line */ + OutStr(" at line "); + OutInt(Lino);} + OutLn(); + if (Debugging>0) { /* some extra info if debugging.. */ + ShowSubs(); + ShowExSt(); + ShowVars(0); + OutStr(" [BP="); + OutHex(BP,4); + OutStr(", TB@"); + OutHex(Peek2(UserProg),4); + OutStr(", IL@"); + OutHex(Peek2(ILfront),4); + OutStr("]"); + ShoMemDump((BP-30)&-16,64);} + Lino = 0; /* restart interpreter at front */ + ExpnTop = ExpnStk; /* with empty expression stack */ + ILPC = 0; /* cheap error test; interp reloads it from ILfront */ + BP = InLine;} /* ~TBerror */ + +void PushSub(int valu) { /* push value onto Gosub stack */ + if (SubStk<=SrcEnd) TBerror(); /* overflow: bumped into program end */ + else { + SubStk = SubStk-2; + Poke2(GoStkTop,SubStk); + Poke2(SubStk,valu);} + if (Debugging>0) ShowSubs();} /* ~PushSub */ + +int PopSub(void) { /* pop value off Gosub stack */ + if (SubStk>=Peek2(EndUser)-1) { /* underflow (nothing in stack).. */ + TBerror(); + return -1;} + else { + if (Debugging>1) ShowSubs(); + SubStk = SubStk+2; + Poke2(GoStkTop,SubStk); + return Peek2(SubStk-2);}} /* ~PopSub */ + +void PushExBy(int valu) { /* push byte onto expression stack */ + if (ExpnTop<=InLend) TBerror(); /* overflow: bumped into input line */ + else Core[--ExpnTop] = (aByte)(valu&255); + if (Debugging>0) ShowExSt();} /* ~PushExBy */ + +int PopExBy(void) { /* pop byte off expression stack */ + if (ExpnTop0) ShowExSt();} /* ~PushExInt */ + +int PopExInt(void) { /* pop integer off expression stack */ + if (++ExpnTop int */ + int num = 0; + char ch = ' '; + while (ch<'0') /* first skip to num... */ + if (ch == '\0') return -1; else ch = DeCaps[((int)*txt++)&127]; + if (ch>'F' || ch>'9' && ch<'A') return -1; /* not hex */ + while ((ndigs--) >0) { /* only get requested digits */ + if (ch<'0' || ch>'F') return num; /* not a hex digit */ + if (ch>='A') num = num*16-55+((int)ch); /* A-F */ + else if (ch<='9') num = num*16-48+((int)ch); /* 0-9 */ + else return num; /* something in between, i.e. not hex */ + ch = DeCaps[((int)*txt++)&127];} + return num;} /* ~DeHex */ + +int SkipTo(int here, char fch) { /* search for'd past next marker */ + while (true) { + char ch = (char)Core[here++]; /* look at next char */ + if (ch == fch) return here; /* got it */ + if (ch == '\0') return --here;}} /* ~SkipTo */ + +int FindLine(int theLine) { /* find theLine in TB source code */ + int ix; + int here = Peek2(UserProg); /* start at front */ + while (true) { + ix = Peek2(here++); + if (theLine<=ix || ix==0) return --here; /* found it or overshot */ + here = SkipTo(++here, '\r');} /* skip to end of this line */ + } /* ~FindLine */ + +void GoToLino(void) { /* find line # Lino and set BP to its front */ + int here; + if (Lino <= 0) { /* Lino=0 is just command line (OK).. */ + BP = InLine; + if (DEBUGON>0) LogIt(0); + return;} + if (DEBUGON>0) LogIt(Lino); + if (Debugging>0) {OutStr(" [#"); OutInt(Lino); OutStr("]");} + BP = FindLine(Lino); /* otherwise try to find it.. */ + here = Peek2(BP++); + if (here==0) TBerror(); /* ran off the end, error off */ + else if (Lino != here) TBerror(); /* not there */ + else BP++;} /* ~GoToLino */ /* got it */ + +void ListIt(int frm, int too) { /* list the stored program */ + char ch; + int here; + if (frm==0) { /* 0,0 defaults to all; n,0 defaults to n,n */ + too = 65535; + frm = 1;} + else if (too==0) too = frm; + here = FindLine(frm); /* try to find first line.. */ + while (!StopIt()) { + frm = Peek2(here++); /* get this line's # to print it */ + if (frm>too || frm==0) break; + here++; + OutInt(frm); + Ouch(' '); + do { /* print the text */ + ch = (char)Core[here++]; + Ouch(ch);} + while (ch>'\r');}} /* ~ListIt */ + +void ConvtIL(char* txt) { /* convert & load TBIL code */ + int valu; + ILend = ILfront+2; + Poke2(ILfront,ILend); /* initialize pointers as promised in TBEK */ + Poke2(ColdGo+1,ILend); + Core[ILend] = (aByte)BadOp; /* illegal op, in case nothing loaded */ + if (txt == NULL) return; + while (*txt != '\0') { /* get the data.. */ + while (*txt > '\r') txt++; /* (no code on 1st line) */ + if (*txt++ == '\0') break; /* no code at all */ + while (*txt > ' ') txt++; /* skip over address */ + if (*txt++ == '\0') break; + while (true) { + valu = DeHex(txt++, 2); /* get a byte */ + if (valu<0) break; /* no more on this line */ + Core[ILend++] = (aByte)valu; /* insert this byte into code */ + txt++;}} + XQhere = 0; /* requires new XQ to initialize */ + Core[ILend] = 0;} /* ~ConvtIL */ + +void LineSwap(int here) { /* swap SvPt/BP if here is not in InLine */ + if (here=InLend) { + here = SvPt; + SvPt = BP; + BP = here;} + else SvPt = BP;} /* ~LineSwap */ + +/************************** Main Interpreter **************************/ + +void Interp(void) { + char ch; /* comments from TinyBasic Experimenter's Kit, pp.15-21 */ + int op, ix, here, chpt; /* temps */ + Broken = false; /* initialize this for possible later test */ + while (true) { + if (StopIt()) { + Broken = false; + OutLn(); + OutStr("*** User Break ***"); + TBerror();} + if (ILPC==0) { + ILPC = Peek2(ILfront); + if (DEBUGON>0) LogIt(-ILPC); + if (Debugging>0) { + OutLn(); OutStr("[IL="); OutHex(ILPC,4); OutStr("]");}} + if (DEBUGON>0) if (Watcher>0) { /* check watchpoint.. */ + if (((Watchee<0) && (Watchee+256+(int)Core[Watcher]) !=0) + || ((Watchee >= 0) && (Watchee==(int)Core[Watcher]))) { + OutLn(); + OutStr("*** Watched "); + OutHex(Watcher,4); + OutStr(" = "); + OutInt((int)Core[Watcher]); + OutStr(" *** "); + Watcher = 0; + TBerror(); + continue;}} + op = (int)Core[ILPC++]; + if (Debugging>0) { + OutLn(); OutStr("[IL+"); OutHex(ILPC-Peek2(ILfront)-1,3); + OutStr("="); OutHex(op,2); OutStr("]");} + switch (op>>5) { + default: switch (op) { + case 15: + TBerror(); + return; + +/* SX n 00-07 Stack Exchange. */ +/* Exchange the top byte of computational stack with */ +/* that "n" bytes into the stack. The top/left byte of the stack is */ +/* considered to be byte 0, so SX 0 does nothing. */ + case 1: case 2: case 3: case 4: case 5: case 6: case 7: + if (ExpnTop+op>=ExpnStk) { /* swap is below stack depth */ + TBerror(); + return;} + ix = (int)Core[ExpnTop]; + Core[ExpnTop] = Core[ExpnTop+op]; + Core[ExpnTop+op] = (aByte)ix; + if (Debugging>0) ShowExSt(); + break; + +/* LB n 09nn Push Literal Byte onto Stack. */ +/* This adds one byte to the expression stack, which */ +/* is the second byte of the instruction. An error stop will occur if */ +/* the stack overflows. */ + case 9: + PushExBy((int)Core[ILPC++]); /* push IL byte */ + break; + +/* LN n 0Annnn Push Literal Number. */ +/* This adds the following two bytes to the */ +/* computational stack, as a 16-bit number. Stack overflow results in */ +/* an error stop. Numbers are assumed to be Big-Endian. */ + case 10: + PushExInt(Peek2(ILPC++)); /* get next 2 IL bytes */ + ILPC++; + break; + +/* DS 0B Duplicate Top Number (two bytes) on Stack. */ +/* An error stop will occur if there are less than 2 */ +/* bytes (1 int) on the expression stack or if the stack overflows. */ + case 11: + op = ExpnTop; + ix = PopExInt(); + if (ILPC == 0) break; /* underflow */ + ExpnTop = op; + PushExInt(ix); + break; + +/* SP 0C Stack Pop. */ +/* The top two bytes are removed from the expression */ +/* stack and discarded. Underflow results in an error stop. */ + case 12: + ix = PopExInt(); + if (Debugging>0) ShowExSt(); + break; + +/* SB 10 Save BASIC Pointer. */ +/* If BASIC pointer is pointing into the input line */ +/* buffer, it is copied to the Saved Pointer; otherwise the two */ +/* pointers are exchanged. */ + case 16: + LineSwap(BP); + break; + +/* RB 11 Restore BASIC Pointer. */ +/* If the Saved Pointer points into the input line */ +/* buffer, it is replaced by the value in the BASIC pointer; */ +/* otherwise the two pointers are exchanged. */ + case 17: + LineSwap(SvPt); + break; + +/* FV 12 Fetch Variable. */ +/* The top byte of the computational stack is used to */ +/* index into Page 00. It is replaced by the two bytes fetched. Error */ +/* stops occur with stack overflow or underflow. */ + case 18: + op = PopExBy(); + if (ILPC != 0) PushExInt(Peek2(op)); + if (Debugging>1) ShowVars(op); + break; + +/* SV 13 Store Variable. */ +/* The top two bytes of the computational stack are */ +/* stored into memory at the Page 00 address specified by the third */ +/* byte on the stack. All three bytes are deleted from the stack. */ +/* Underflow results in an error stop. */ + case 19: + ix = PopExInt(); + op = PopExBy(); + if (ILPC == 0) break; + Poke2(op,ix); + if (DEBUGON>0) LogIt((ix&0xFFFF)+((op-256)<<16)); + if (Debugging>0) {ShowVars(op); if (Debugging>1) ShowExSt();} + break; + +/* GS 14 GOSUB Save. */ +/* The current BASIC line number is pushed */ +/* onto the BASIC region of the control stack. It is essential that */ +/* the IL stack be empty for this to work properly but no check is */ +/* made for that condition. An error stop occurs on stack overflow. */ + case 20: + PushSub(Lino); /* push line # (possibly =0) */ + break; + +/* RS 15 Restore Saved Line. */ +/* Pop the top two bytes off the BASIC region of the */ +/* control stack, making them the current line number. Set the BASIC */ +/* pointer at the beginning of that line. Note that this is the line */ +/* containing the GOSUB which caused the line number to be saved. As */ +/* with the GS opcode, it is essential that the IL region of the */ +/* control stack be empty. If the line number popped off the stack */ +/* does not correspond to a line in the BASIC program an error stop */ +/* occurs. An error stop also results from stack underflow. */ + case 21: + Lino = PopSub(); /* get line # (possibly =0) from pop */ + if (ILPC != 0) GoToLino() ; /* stops run if error */ + break; + +/* GO 16 GOTO. */ +/* Make current the BASIC line whose line number is */ +/* equal to the value of the top two bytes in the expression stack. */ +/* That is, the top two bytes are popped off the computational stack, */ +/* and the BASIC program is searched until a matching line number is */ +/* found. The BASIC pointer is then positioned at the beginning of */ +/* that line and the RUN mode flag is turned on. Stack underflow and */ +/* non-existent BASIC line result in error stops. */ + case 22: + ILPC = XQhere; /* the IL assumes an implied NX */ + if (DEBUGON>0) LogIt(-ILPC); + Lino = PopExInt(); + if (ILPC != 0) GoToLino() ; /* stops run if error */ + break; + +/* NE 17 Negate (two's complement). */ +/* The number in the top two bytes of the expression */ +/* stack is replaced with its negative. */ + case 23: + ix = PopExInt(); + if (ILPC != 0) PushExInt(-ix); + break; + +/* AD 18 Add. */ +/* Add the two numbers represented by the top four */ +/* bytes of the expression stack, and replace them with the two-byte */ +/* sum. Stack underflow results in an error stop. */ + case 24: + ix = PopExInt(); + op = PopExInt(); + if (ILPC != 0) PushExInt(op+ix); + break; + +/* SU 19 Subtract. */ +/* Subtract the two-byte number on the top of the */ +/* expression stack from the next two bytes and replace the 4 bytes */ +/* with the two-byte difference. */ + case 25: + ix = PopExInt(); + op = PopExInt(); + if (ILPC != 0) PushExInt(op-ix); + break; + +/* MP 1A Multiply. */ +/* Multiply the two numbers represented by the top 4 */ +/* bytes of the computational stack, and replace them with the least */ +/* significant 16 bits of the product. Stack underflow is possible. */ + case 26: + ix = PopExInt(); + op = PopExInt(); + if (ILPC != 0) PushExInt(op*ix); + break; + +/* DV 1B Divide. */ +/* Divide the number represented by the top two bytes */ +/* of the computational stack into that represented by the next two. */ +/* Replace the 4 bytes with the quotient and discard the remainder. */ +/* This is a signed (two's complement) integer divide, resulting in a */ +/* signed integer quotient. Stack underflow or attempted division by */ +/* zero result in an error stop. */ + case 27: + ix = PopExInt(); + op = PopExInt(); + if (ix == 0) TBerror(); /* divide by 0.. */ + else if (ILPC != 0) PushExInt(op/ix); + break; + +/* CP 1C Compare. */ +/* The number in the top two bytes of the expression */ +/* stack is compared to (subtracted from) the number in the 4th and */ +/* fifth bytes of the stack, and the result is determined to be */ +/* Greater, Equal, or Less. The low three bits of the third byte mask */ +/* a conditional skip in the IL program to test these conditions; if */ +/* the result corresponds to a one bit, the next byte of the IL code */ +/* is skipped and not executed. The three bits correspond to the */ +/* conditions as follows: */ +/* bit 0 Result is Less */ +/* bit 1 Result is Equal */ +/* bit 2 Result is Greater */ +/* Whether the skip is taken or not, all five bytes are deleted from */ +/* the stack. This is a signed (two's complement) comparison so that */ +/* any positive number is greater than any negative number. Multiple */ +/* conditions, such as greater-than-or-equal or unequal (i.e.greater- */ +/* than-or-less-than), may be tested by forming the condition mask */ +/* byte of the sum of the respective bits. In particular, a mask byte */ +/* of 7 will force an unconditional skip and a mask byte of 0 will */ +/* force no skip. The other 5 bits of the control byte are ignored. */ +/* Stack underflow results in an error stop. */ + case 28: + ix = PopExInt(); + op = PopExBy(); + ix = PopExInt()-ix; /* <0 or =0 or >0 */ + if (ILPC == 0) return; /* underflow.. */ + if (ix<0) ix = 1; + else if (ix>0) ix = 4; /* choose the bit to test */ + else ix = 2; + if ((ix&op)>0) ILPC++; /* skip next IL op if bit =1 */ + if (Debugging>0) ShowExSt(); + break; + +/* NX 1D Next BASIC Statement. */ +/* Advance to next line in the BASIC program, if in */ +/* RUN mode, or restart the IL program if in the command mode. The */ +/* remainder of the current line is ignored. In the Run mode if there */ +/* is another line it becomes current with the pointer positioned at */ +/* its beginning. At this time, if the Break condition returns true, */ +/* execution is aborted and the IL program is restarted after */ +/* printing an error message. Otherwise IL execution proceeds from */ +/* the saved IL address (see the XQ instruction). If there are no */ +/* more BASIC statements in the program an error stop occurs. */ + case 29: + if (Lino == 0) ILPC = 0; + else { + BP = SkipTo(BP, '\r'); /* skip to end of this line */ + Lino = Peek2(BP++); /* get line # */ + if (Lino==0) { /* ran off the end */ + TBerror(); + break;} + else BP++; + ILPC = XQhere; /* restart at saved IL address (XQ) */ + if (DEBUGON>0) LogIt(-ILPC);} + if (DEBUGON>0) LogIt(Lino); + if (Debugging>0) {OutStr(" [#"); OutInt(Lino); OutStr("]");} + break; + +/* LS 1F List The Program. */ +/* The expression stack is assumed to have two 2-byte */ +/* numbers. The top number is the line number of the last line to be */ +/* listed, and the next is the line number of the first line to be */ +/* listed. If the specified line numbers do not exist in the program, */ +/* the next available line (i.e. with the next higher line number) is */ +/* assumed instead in each case. If the last line to be listed comes */ +/* before the first, no lines are listed. If Break condition comes */ +/* true during a List operation, the remainder of the listing is */ +/* aborted. Zero is not a valid line number, and an error stop occurs */ +/* if either line number specification is zero. The line number */ +/* specifications are deleted from the stack. */ + case 31: + op = 0; + ix = 0; /* The IL seems to assume we can handle zero */ + while (ExpnTop0); + break; + +/* NL 23 New Line. */ +/* Output a carriage-return-linefeed sequence to the */ +/* console. */ + case 35: + Ouch('\r'); + break; + +/* PC "xxxx" 24xxxxxxXx Print Literal String. */ +/* The ASCII string follows opcode and its */ +/* last byte has the most significant bit set to one. */ + case 36: + do { + ix = (int)Core[ILPC++]; + Ouch((char)(ix&127)); /* strip high bit for output */ + } while ((ix&128)==0); + break; + +/* GL 27 Get Input Line. */ +/* ASCII characters are accepted from console input */ +/* to fill the line buffer. If the line length exceeds the available */ +/* space, the excess characters are ignored and bell characters are */ +/* output. The line is terminated by a carriage return. On completing */ +/* one line of input, the BASIC pointer is set to point to the first */ +/* character in the input line buffer, and a carriage-return-linefeed */ +/* sequence is [not] output. */ + case 39: + InLend = InLine; + while (true) { /* read input line characters... */ + ch = Inch(); + if (ch=='\r') break; /* end of the line */ + else if (ch=='\t') { + Debugging = (Debugging+DEBUGON)&1; /* maybe toggle debug */ + ch = ' ';} /* convert tabs to space */ + else if (ch==(char)Core[BScode]) { /* backspace code */ + if (InLend>InLine) InLend--; /* assume console already */ + else { /* backing up over front of line: just kill it.. */ + Ouch('\r'); + break;}} + else if (ch==(char)Core[CanCode]) { /* cancel this line */ + InLend = InLine; + Ouch('\r'); /* also start a new input line */ + break;} + else if (ch<' ') continue; /* ignore non-ASCII & controls */ + else if (ch>'~') continue; + if (InLend>ExpnTop-2) continue; /* discard overrun chars */ + /* Siemargl fix for not so smart consoles*/ + if (ch != (char)Core[BScode]) + Core[InLend++] = (aByte)ch; + } /* insert this char in buffer */ + while (InLend>InLine && Core[InLend-1] == ' ') + InLend--; /* delete excess trailing spaces */ + Core[InLend++] = (aByte) '\r'; /* insert final return & null */ + Core[InLend] = 0; + BP = InLine; + break; + +/* IL 2A Insert BASIC Line. */ +/* Beginning with the current position of the BASIC */ +/* pointer and continuing to the [end of it], the line is inserted */ +/* into the BASIC program space; for a line number, the top two bytes */ +/* of the expression stack are used. If this number matches a line */ +/* already in the program it is deleted and the new one replaces it. */ +/* If the new line consists of only a carriage return, it is not */ +/* inserted, though any previous line with the same number will have */ +/* been deleted. The lines are maintained in the program space sorted */ +/* by line number. If the new line to be inserted is a different size */ +/* than the old line being replaced, the remainder of the program is */ +/* shifted over to make room or to close up the gap as necessary. If */ +/* there is insufficient memory to fit in the new line, the program */ +/* space is unchanged and an error stop occurs (with the IL address */ +/* decremented). A normal error stop occurs on expression stack */ +/* underflow or if the number is zero, which is not a valid line */ +/* number. After completing the insertion, the IL program is */ +/* restarted in the command mode. */ + case 42: + Lino = PopExInt(); /* get line # */ + if (Lino <= 0) { /* don't insert line #0 or negative */ + if (ILPC != 0) TBerror(); + else return; + break;} + while (((char)Core[BP]) == ' ') BP++; /* skip leading spaces */ + if (((char)Core[BP]) == '\r') ix = 0; /* nothing to add */ + else ix = InLend-BP+2; /* the size of the insertion */ + op = 0; /* this will be the number of bytes to delete */ + chpt = FindLine(Lino); /* try to find this line.. */ + if (Peek2(chpt) == Lino) /* there is a line to delete.. */ + op = (SkipTo(chpt+2, '\r')-chpt); + if (ix == 0) if (op==0) { /* nothing to add nor delete; done */ + Lino = 0; + break;} + op = ix-op; /* = how many more bytes to add or (-)delete */ + if (SrcEnd+op>=SubStk) { /* too big.. */ + TBerror(); + break;} + SrcEnd = SrcEnd+op; /* new size */ + if (op>0) for (here=SrcEnd; (here--)>chpt+ix; ) + Core[here] = Core[here-op]; /* shift backend over to right */ + else if (op<0) for (here=chpt+ix; here0) Poke2(chpt++,Lino); /* insert the new line # */ + while (ix>2) { /* insert the new line.. */ + Core[++chpt] = Core[BP++]; + ix--;} + Poke2(EndProg,SrcEnd); + ILPC = 0; + Lino = 0; + if (Debugging>0) ListIt(0,0); + break; + +/* MT 2B Mark the BASIC program space Empty. */ +/* Also clears the BASIC region of the control stack */ +/* and restart the IL program in the command mode. The memory bounds */ +/* and stack pointers are reset by this instruction to signify empty */ +/* program space, and the line number of the first line is set to 0, */ +/* which is the indication of the end of the program. */ + case 43: + ColdStart(); + if (Debugging>0) {ShowSubs(); ShowExSt(); ShowVars(0);} + break; + +/* XQ 2C Execute. */ +/* Turns on RUN mode. This instruction also saves */ +/* the current value of the IL program counter for use of the NX */ +/* instruction, and sets the BASIC pointer to the beginning of the */ +/* BASIC program space. An error stop occurs if there is no BASIC */ +/* program. This instruction must be executed at least once before */ +/* the first execution of a NX instruction. */ + case 44: + XQhere = ILPC; + BP = Peek2(UserProg); + Lino = Peek2(BP++); + BP++; + if (Lino == 0) TBerror(); + else if (Debugging>0) + {OutStr(" [#"); OutInt(Lino); OutStr("]");} + break; + +/* WS 2D Stop. */ +/* Stop execution and restart the IL program in the */ +/* command mode. The entire control stack (including BASIC region) */ +/* is also vacated by this instruction. This instruction effectively */ +/* jumps to the Warm Start entry of the ML interpreter. */ + case 45: + WarmStart(); + if (Debugging>0) ShowSubs(); + break; + +/* US 2E Machine Language Subroutine Call. */ +/* The top six bytes of the expression stack contain */ +/* 3 numbers with the following interpretations: The top number is */ +/* loaded into the A (or A and B) register; the next number is loaded */ +/* into 16 bits of Index register; the third number is interpreted as */ +/* the address of a machine language subroutine to be called. These */ +/* six bytes on the expression stack are replaced with the 16-bit */ +/* result returned by the subroutine. Stack underflow results in an */ +/* error stop. */ + case 46: + Poke2(LinoCore,Lino); /* bring these memory locations up.. */ + Poke2(ILPCcore,ILPC); /* ..to date, in case user looks.. */ + Poke2(BPcore,BP); + Poke2(SvPtCore,SvPt); + ix = PopExInt()&0xFFFF; /* datum A */ + here = PopExInt()&0xFFFF; /* datum X */ + op = PopExInt()&0xFFFF; /* nominal machine address */ + if (ILPC == 0) break; + if (op>=Peek2(ILfront) && op0) LogIt(-ILPC); + break;} + switch (op) { + case WachPoint: /* we only do a few predefined functions.. */ + Watcher = here; + if (ix>32767) ix = -(int)Core[here]-256; + Watchee = ix; + if (Debugging>0) { + OutLn(); OutStr("[** Watch "); OutHex(here,4); OutStr("]");} + PushExInt((int)Core[here]); + break; + case ColdGo: + ColdStart(); + break; + case WarmGo: + WarmStart(); + break; + case InchSub: + PushExInt((int)Inch()); + break; + case OutchSub: + Ouch((char)(ix&127)); + PushExInt(0); + break; + case BreakSub: + PushExInt(StopIt()); + break; + case PeekSub: + PushExInt((int)Core[here]); + break; + case Peek2Sub: + PushExInt(Peek2(here)); + break; + case PokeSub: + ix = ix&0xFF; + Core[here] = (aByte)ix; + PushExInt(ix); + if (DEBUGON>0) LogIt(((ix+256)<<16)+here); + Lino = Peek2(LinoCore); /* restore these pointers.. */ + ILPC = Peek2(ILPCcore); /* ..in case user changed them.. */ + BP = Peek2(BPcore); + SvPt = Peek2(SvPtCore); + break; + case DumpSub: + ShoMemDump(here,ix); + PushExInt(here+ix); + break; + case TrLogSub: + ShowLog(); + PushExInt(LogHere); + break; + default: TBerror();} + break; + +/* RT 2F IL Subroutine Return. */ +/* The IL control stack is popped to give the address */ +/* of the next IL instruction. An error stop occurs if the entire */ +/* control stack (IL and BASIC) is empty. */ + case 47: + ix = PopSub(); /* get return from pop */ + if (ix=ILend) TBerror(); + else if (ILPC != 0) { + ILPC = ix; + if (DEBUGON>0) LogIt(-ILPC);} + break; + +/* JS a 3000-37FF IL Subroutine Call. */ +/* The least significant eleven bits of this */ +/* 2-byte instruction are added to the base address of the IL program */ +/* to become address of the next instruction. The previous contents */ +/* of the IL program counter are pushed onto the IL region of the */ +/* control stack. Stack overflow results in an error stop. */ + case 48: case 49: case 50: case 51: case 52: case 53: case 54: case 55: + PushSub(ILPC+1); /* push return location there */ + if (ILPC == 0) break; + ILPC = (Peek2(ILPC-1)&0x7FF)+Peek2(ILfront); + if (DEBUGON>0) LogIt(-ILPC); + break; + +/* J a 3800-3FFF Jump. */ +/* The low eleven bits of this 2-byte */ +/* instruction are added to the IL program base address to determine */ +/* the address of the next IL instruction. The previous contents of */ +/* the IL program counter is lost. */ + case 56: case 57: case 58: case 59: case 60: case 61: case 62: case 63: + ILPC = (Peek2(ILPC-1)&0x7FF)+Peek2(ILfront); + if (DEBUGON>0) LogIt(-ILPC); + break; + +/* NO 08 No Operation. */ +/* This may be used as a space filler (such as to */ +/* ignore a skip). */ + default: break;} /* last of inner switch cases */ + break; /* end of outer switch cases 0,1 */ + +/* BR a 40-7F Relative Branch. */ +/* The low six bits of this instruction opcode are */ +/* added algebraically to the current value of the IL program counter */ +/* to give the address of the next IL instruction. Bit 5 of opcode is */ +/* the sign, with + signified by 1, - by 0. The range of this branch */ +/* is +/-31 bytes from address of the byte following the opcode. An */ +/* offset of zero (i.e. opcode 60) results in an error stop. The */ +/* branch operation is unconditional. */ + case 2: case 3: + ILPC = ILPC+op-96; + if (DEBUGON>0) LogIt(-ILPC); + break; + +/* BC a "xxx" 80xxxxXx-9FxxxxXx String Match Branch. */ +/* The ASCII character string in IL */ +/* following this opcode is compared to the string beginning with the */ +/* current position of the BASIC pointer, ignoring blanks in BASIC */ +/* program. The comparison continues until either a mismatch, or an */ +/* IL byte is reached with the most significant bit set to one. This */ +/* is the last byte of the string in the IL, compared as a 7-bit */ +/* character; if equal, the BASIC pointer is positioned after the */ +/* last matching character in the BASIC program and the IL continues */ +/* with the next instruction in sequence. Otherwise the BASIC pointer */ +/* is not altered and the low five bits of the Branch opcode are */ +/* added to the IL program counter to form the address of the next */ +/* IL instruction. If the strings do not match and the branch offset */ +/* is zero an error stop occurs. */ + case 4: + if (op==128) here = 0; /* to error if no match */ + else here = ILPC+op-128; + chpt = BP; + ix = 0; + while ((ix&128)==0) { + while (((char)Core[BP]) == ' ') BP++; /* skip over spaces */ + ix = (int)Core[ILPC++]; + if (((char)(ix&127)) != DeCaps[((int)Core[BP++])&127]) { + BP = chpt; /* back up to front of string in Basic */ + if (here==0) TBerror(); + else ILPC = here; /* jump forward in IL */ + break;}} + if (DEBUGON>0) if (ILPC>0) LogIt(-ILPC); + break; + +/* BV a A0-BF Branch if Not Variable. */ +/* If the next non-blank character pointed to by the */ +/* BASIC pointer is a capital letter, its ASCII code is [doubled and] */ +/* pushed onto the expression stack and the IL program advances to */ +/* next instruction in sequence, leaving the BASIC pointer positioned */ +/* after the letter; if not a letter the branch is taken and BASIC */ +/* pointer is left pointing to that character. An error stop occurs */ +/* if the next character is not a letter and the offset of the branch */ +/* is zero, or on stack overflow. */ + case 5: + while (((char)Core[BP]) == ' ') BP++; /* skip over spaces */ + ch = (char)Core[BP]; + if (ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z') + PushExBy((((int)Core[BP++])&0x5F)*2); + else if (op==160) TBerror(); /* error if not letter */ + else ILPC = ILPC+op-160; + if (DEBUGON>0) if (ILPC>0) LogIt(-ILPC); + break; + +/* BN a C0-DF Branch if Not a Number. */ +/* If the next non-blank character pointed to by the */ +/* BASIC pointer is not a decimal digit, the low five bits of the */ +/* opcode are added to the IL program counter, or if zero an error */ +/* stop occurs. If the next character is a digit, then it and all */ +/* decimal digits following it (ignoring blanks) are converted to a */ +/* 16-bit binary number which is pushed onto the expression stack. In */ +/* either case the BASIC pointer is positioned at the next character */ +/* which is neither blank nor digit. Stack overflow will result in an */ +/* error stop. */ + case 6: + while (((char)Core[BP]) == ' ') BP++; /* skip over spaces */ + ch = (char)Core[BP]; + if (ch >= '0' && ch <= '9') { + op = 0; + while (true) { + here = (int)Core[BP++]; + if (here==32) continue; /* skip over spaces */ + if (here<48 || here>57) break; /* not a decimal digit */ + op = op*10+here-48;} /* insert into value */ + BP--; /* back up over non-digit */ + PushExInt(op);} + else if (op==192) TBerror(); /* error if no digit */ + else ILPC = ILPC+op-192; + if (DEBUGON>0) if (ILPC>0) LogIt(-ILPC); + break; + +/* BE a E0-FF Branch if Not Endline. */ +/* If the next non-blank character pointed to by the */ +/* BASIC pointer is a carriage return, the IL program advances to the */ +/* next instruction in sequence; otherwise the low five bits of the */ +/* opcode (if not 0) are added to the IL program counter to form the */ +/* address of next IL instruction. In either case the BASIC pointer */ +/* is left pointing to the first non-blank character; this */ +/* instruction will not pass over the carriage return, which must */ +/* remain for testing by the NX instruction. As with the other */ +/* conditional branches, the branch may only advance the IL program */ +/* counter from 1 to 31 bytes; an offset of zero results in an error */ +/* stop. */ + case 7: + while (((char)Core[BP]) == ' ') BP++; /* skip over spaces */ + if (((char)Core[BP]) == '\r') ; + else if (op==224) TBerror(); /* error if no offset */ + else ILPC = ILPC+op-224; + if (DEBUGON>0) if (ILPC>0) LogIt(-ILPC); + break;}}} /* ~Interp */ + +/***************** Intermediate Interpreter Assembled *****************/ + +char* DefaultIL() { + static char s[9000]; /* be sure to increase size if you add text */ + strcpy(s,"0000 ; 1 . ORIGINAL TINY BASIC INTERMEDIATE INTERPRETER\n"); + strcat(s,"0000 ; 2 .\n"); + strcat(s,"0000 ; 3 . EXECUTIVE INITIALIZATION\n"); + strcat(s,"0000 ; 4 .\n"); + strcat(s,"0000 ; 5 :STRT PC \":Q^\" COLON, X-ON\n"); + strcat(s,"0000 243A91;\n"); + strcat(s,"0003 ; 6 GL\n"); + strcat(s,"0003 27; 7 SB\n"); + strcat(s,"0004 10; 8 BE L0 BRANCH IF NOT EMPTY\n"); + strcat(s,"0005 E1; 9 BR STRT TRY AGAIN IF NULL LINE\n"); + strcat(s,"0006 59; 10 :L0 BN STMT TEST FOR LINE NUMBER\n"); + strcat(s,"0007 C5; 11 IL IF SO, INSERT INTO PROGRAM\n"); + strcat(s,"0008 2A; 12 BR STRT GO GET NEXT\n"); + strcat(s,"0009 56; 13 :XEC SB SAVE POINTERS FOR RUN WITH\n"); + strcat(s,"000A 10; 14 RB CONCATENATED INPUT\n"); + strcat(s,"000B 11; 15 XQ\n"); + strcat(s,"000C 2C; 16 .\n"); + strcat(s,"000D ; 17 . STATEMENT EXECUTOR\n"); + strcat(s,"000D ; 18 .\n"); + strcat(s,"000D ; 19 :STMT BC GOTO \"LET\"\n"); + strcat(s,"000D 8B4C45D4;\n"); + strcat(s,"0011 ; 20 BV * MUST BE A VARIABLE NAME\n"); + strcat(s,"0011 A0; 21 BC * \"=\"\n"); + strcat(s,"0012 80BD; 22 :LET JS EXPR GO GET EXPRESSION\n"); + strcat(s,"0014 30BC; 23 BE * IF STATEMENT END,\n"); + strcat(s,"0016 E0; 24 SV STORE RESULT\n"); + strcat(s,"0017 13; 25 NX\n"); + strcat(s,"0018 1D; 26 .\n"); + strcat(s,"0019 ; 27 :GOTO BC PRNT \"GO\"\n"); + strcat(s,"0019 9447CF;\n"); + strcat(s,"001C ; 28 BC GOSB \"TO\"\n"); + strcat(s,"001C 8854CF;\n"); + strcat(s,"001F ; 29 JS EXPR GET LINE NUMBER\n"); + strcat(s,"001F 30BC; 30 BE *\n"); + strcat(s,"0021 E0; 31 SB (DO THIS FOR STARTING)\n"); + strcat(s,"0022 10; 32 RB\n"); + strcat(s,"0023 11; 33 GO GO THERE\n"); + strcat(s,"0024 16; 34 .\n"); + strcat(s,"0025 ; 35 :GOSB BC * \"SUB\" NO OTHER WORD BEGINS \"GO...\"\n"); + strcat(s,"0025 805355C2;\n"); + strcat(s,"0029 ; 36 JS EXPR\n"); + strcat(s,"0029 30BC; 37 BE *\n"); + strcat(s,"002B E0; 38 GS\n"); + strcat(s,"002C 14; 39 GO\n"); + strcat(s,"002D 16; 40 .\n"); + strcat(s,"002E ; 41 :PRNT BC SKIP \"PR\"\n"); + strcat(s,"002E 9050D2;\n"); + strcat(s,"0031 ; 42 BC P0 \"INT\" OPTIONALLY OMIT \"INT\"\n"); + strcat(s,"0031 83494ED4;\n"); + strcat(s,"0035 ; 43 :P0 BE P3\n"); + strcat(s,"0035 E5; 44 BR P6 IF DONE, GO TO END\n"); + strcat(s,"0036 71; 45 :P1 BC P4 \";\"\n"); + strcat(s,"0037 88BB; 46 :P2 BE P3\n"); + strcat(s,"0039 E1; 47 NX NO CRLF IF ENDED BY ; OR ,\n"); + strcat(s,"003A 1D; 48 :P3 BC P7 '\"'\n"); + strcat(s,"003B 8FA2; 49 PQ QUOTE MARKS STRING\n"); + strcat(s,"003D 21; 50 BR P1 GO CHECK DELIMITER\n"); + strcat(s,"003E 58; 51 :SKIP BR IF (ON THE WAY THRU)\n"); + strcat(s,"003F 6F; 52 :P4 BC P5 \",\"\n"); + strcat(s,"0040 83AC; 53 PT COMMA SPACING\n"); + strcat(s,"0042 22; 54 BR P2\n"); + strcat(s,"0043 55; 55 :P5 BC P6 \":\"\n"); + strcat(s,"0044 83BA; 56 PC \"S^\" OUTPUT X-OFF\n"); + strcat(s,"0046 2493; 57 :P6 BE *\n"); + strcat(s,"0048 E0; 58 NL THEN CRLF\n"); + strcat(s,"0049 23; 59 NX\n"); + strcat(s,"004A 1D; 60 :P7 JS EXPR TRY FOR AN EXPRESSION\n"); + strcat(s,"004B 30BC; 61 PN\n"); + strcat(s,"004D 20; 62 BR P1\n"); + strcat(s,"004E 48; 63 .\n"); + strcat(s,"004F ; 64 :IF BC INPT \"IF\"\n"); + strcat(s,"004F 9149C6;\n"); + strcat(s,"0052 ; 65 JS EXPR\n"); + strcat(s,"0052 30BC; 66 JS RELO\n"); + strcat(s,"0054 3134; 67 JS EXPR\n"); + strcat(s,"0056 30BC; 68 BC I1 \"THEN\" OPTIONAL NOISEWORD\n"); + strcat(s,"0058 84544845CE;\n"); + strcat(s,"005D ; 69 :I1 CP COMPARE SKIPS NEXT IF TRUE\n"); + strcat(s,"005D 1C; 70 NX FALSE.\n"); + strcat(s,"005E 1D; 71 J STMT TRUE. GO PROCESS STATEMENT\n"); + strcat(s,"005F 380D; 72 .\n"); + strcat(s,"0061 ; 73 :INPT BC RETN \"INPUT\"\n"); + strcat(s,"0061 9A494E5055D4;\n"); + strcat(s,"0067 ; 74 :I2 BV * GET VARIABLE\n"); + strcat(s,"0067 A0; 75 SB SWAP POINTERS\n"); + strcat(s,"0068 10; 76 BE I4\n"); + strcat(s,"0069 E7; 77 :I3 PC \"? Q^\" LINE IS EMPTY; TYPE PROMPT\n"); + strcat(s,"006A 243F2091;\n"); + strcat(s,"006E ; 78 GL READ INPUT LINE\n"); + strcat(s,"006E 27; 79 BE I4 DID ANYTHING COME?\n"); + strcat(s,"006F E1; 80 BR I3 NO, TRY AGAIN\n"); + strcat(s,"0070 59; 81 :I4 BC I5 \",\" OPTIONAL COMMA\n"); + strcat(s,"0071 81AC; 82 :I5 JS EXPR READ A NUMBER\n"); + strcat(s,"0073 30BC; 83 SV STORE INTO VARIABLE\n"); + strcat(s,"0075 13; 84 RB SWAP BACK\n"); + strcat(s,"0076 11; 85 BC I6 \",\" ANOTHER?\n"); + strcat(s,"0077 82AC; 86 BR I2 YES IF COMMA\n"); + strcat(s,"0079 4D; 87 :I6 BE * OTHERWISE QUIT\n"); + strcat(s,"007A E0; 88 NX\n"); + strcat(s,"007B 1D; 89 .\n"); + strcat(s,"007C ; 90 :RETN BC END \"RETURN\"\n"); + strcat(s,"007C 895245545552CE;\n"); + strcat(s,"0083 ; 91 BE *\n"); + strcat(s,"0083 E0; 92 RS RECOVER SAVED LINE\n"); + strcat(s,"0084 15; 93 NX\n"); + strcat(s,"0085 1D; 94 .\n"); + strcat(s,"0086 ; 95 :END BC LIST \"END\"\n"); + strcat(s,"0086 85454EC4;\n"); + strcat(s,"008A ; 96 BE *\n"); + strcat(s,"008A E0; 97 WS\n"); + strcat(s,"008B 2D; 98 .\n"); + strcat(s,"008C ; 99 :LIST BC RUN \"LIST\"\n"); + strcat(s,"008C 984C4953D4;\n"); + strcat(s,"0091 ; 100 BE L2\n"); + strcat(s,"0091 EC; 101 :L1 PC \"@^@^@^@^J^@^\" PUNCH LEADER\n"); + strcat(s,"0092 24000000000A80;\n"); + strcat(s,"0099 ; 102 LS LIST\n"); + strcat(s,"0099 1F; 103 PC \"S^\" PUNCH X-OFF\n"); + strcat(s,"009A 2493; 104 NL\n"); + strcat(s,"009C 23; 105 NX\n"); + strcat(s,"009D 1D; 106 :L2 JS EXPR GET A LINE NUMBER\n"); + strcat(s,"009E 30BC; 107 BE L3\n"); + strcat(s,"00A0 E1; 108 BR L1\n"); + strcat(s,"00A1 50; 109 :L3 BC * \",\" SEPARATED BY COMMAS\n"); + strcat(s,"00A2 80AC; 110 BR L2\n"); + strcat(s,"00A4 59; 111 .\n"); + strcat(s,"00A5 ; 112 :RUN BC CLER \"RUN\"\n"); + strcat(s,"00A5 855255CE;\n"); + strcat(s,"00A9 ; 113 J XEC\n"); + strcat(s,"00A9 380A; 114 .\n"); + strcat(s,"00AB ; 115 :CLER BC REM \"CLEAR\"\n"); + strcat(s,"00AB 86434C4541D2;\n"); + strcat(s,"00B1 ; 116 MT\n"); + strcat(s,"00B1 2B; 117 .\n"); + strcat(s,"00B2 ; 118 :REM BC DFLT \"REM\"\n"); + strcat(s,"00B2 845245CD;\n"); + strcat(s,"00B6 ; 119 NX\n"); + strcat(s,"00B6 1D; 120 .\n"); + strcat(s,"00B7 ; 121 :DFLT BV * NO KEYWORD...\n"); + strcat(s,"00B7 A0; 122 BC * \"=\" TRY FOR LET\n"); + strcat(s,"00B8 80BD; 123 J LET IT'S A GOOD BET.\n"); + strcat(s,"00BA 3814; 124 .\n"); + strcat(s,"00BC ; 125 . SUBROUTINES\n"); + strcat(s,"00BC ; 126 .\n"); + strcat(s,"00BC ; 127 :EXPR BC E0 \"-\" TRY FOR UNARY MINUS\n"); + strcat(s,"00BC 85AD; 128 JS TERM AHA\n"); + strcat(s,"00BE 30D3; 129 NE\n"); + strcat(s,"00C0 17; 130 BR E1\n"); + strcat(s,"00C1 64; 131 :E0 BC E4 \"+\" IGNORE UNARY PLUS\n"); + strcat(s,"00C2 81AB; 132 :E4 JS TERM\n"); + strcat(s,"00C4 30D3; 133 :E1 BC E2 \"+\" TERMS SEPARATED BY PLUS\n"); + strcat(s,"00C6 85AB; 134 JS TERM\n"); + strcat(s,"00C8 30D3; 135 AD\n"); + strcat(s,"00CA 18; 136 BR E1\n"); + strcat(s,"00CB 5A; 137 :E2 BC E3 \"-\" TERMS SEPARATED BY MINUS\n"); + strcat(s,"00CC 85AD; 138 JS TERM\n"); + strcat(s,"00CE 30D3; 139 SU\n"); + strcat(s,"00D0 19; 140 BR E1\n"); + strcat(s,"00D1 54; 141 :E3 RT\n"); + strcat(s,"00D2 2F; 142 .\n"); + strcat(s,"00D3 ; 143 :TERM JS FACT\n"); + strcat(s,"00D3 30E2; 144 :T0 BC T1 \"*\" FACTORS SEPARATED BY TIMES\n"); + strcat(s,"00D5 85AA; 145 JS FACT\n"); + strcat(s,"00D7 30E2; 146 MP\n"); + strcat(s,"00D9 1A; 147 BR T0\n"); + strcat(s,"00DA 5A; 148 :T1 BC T2 \"/\" FACTORS SEPARATED BY DIVIDE\n"); + strcat(s,"00DB 85AF; 149 JS FACT\n"); + strcat(s,"00DD 30E2; 150 DV\n"); + strcat(s,"00DF 1B; 151 BR T0\n"); + strcat(s,"00E0 54; 152 :T2 RT\n"); + strcat(s,"00E1 2F; 153 .\n"); + strcat(s,"00E2 ; 154 :FACT BC F0 \"RND\" *RND FUNCTION*\n"); + strcat(s,"00E2 97524EC4;\n"); + strcat(s,"00E6 ; 155 LN 257*128 STACK POINTER FOR STORE\n"); + strcat(s,"00E6 0A;\n"); + strcat(s,"00E7 8080; 156 FV THEN GET RNDM\n"); + strcat(s,"00E9 12; 157 LN 2345 R:=R*2345+6789\n"); + strcat(s,"00EA 0A;\n"); + strcat(s,"00EB 0929; 158 MP\n"); + strcat(s,"00ED 1A; 159 LN 6789\n"); + strcat(s,"00EE 0A;\n"); + strcat(s,"00EF 1A85; 160 AD\n"); + strcat(s,"00F1 18; 161 SV\n"); + strcat(s,"00F2 13; 162 LB 128 GET IT AGAIN\n"); + strcat(s,"00F3 0980; 163 FV\n"); + strcat(s,"00F5 12; 164 DS\n"); + strcat(s,"00F6 0B; 165 JS FUNC GET ARGUMENT\n"); + strcat(s,"00F7 3130; 166 BR F1\n"); + strcat(s,"00F9 61; 167 :F0 BR F2 (SKIPPING)\n"); + strcat(s,"00FA 73; 168 :F1 DS\n"); + strcat(s,"00FB 0B; 169 SX 2 PUSH TOP INTO STACK\n"); + strcat(s,"00FC 02; 170 SX 4\n"); + strcat(s,"00FD 04; 171 SX 2\n"); + strcat(s,"00FE 02; 172 SX 3\n"); + strcat(s,"00FF 03; 173 SX 5\n"); + strcat(s,"0100 05; 174 SX 3\n"); + strcat(s,"0101 03; 175 DV PERFORM MOD FUNCTION\n"); + strcat(s,"0102 1B; 176 MP\n"); + strcat(s,"0103 1A; 177 SU\n"); + strcat(s,"0104 19; 178 DS PERFORM ABS FUNCTION\n"); + strcat(s,"0105 0B; 179 LB 6\n"); + strcat(s,"0106 0906; 180 LN 0\n"); + strcat(s,"0108 0A;\n"); + strcat(s,"0109 0000; 181 CP (SKIP IF + OR 0)\n"); + strcat(s,"010B 1C; 182 NE\n"); + strcat(s,"010C 17; 183 RT\n"); + strcat(s,"010D 2F; 184 :F2 BC F3 \"USR\" *USR FUNCTION*\n"); + strcat(s,"010E 8F5553D2;\n"); + strcat(s,"0112 ; 185 BC * \"(\" 3 ARGUMENTS POSSIBLE\n"); + strcat(s,"0112 80A8; 186 JS EXPR ONE REQUIRED\n"); + strcat(s,"0114 30BC; 187 JS ARG\n"); + strcat(s,"0116 312A; 188 JS ARG\n"); + strcat(s,"0118 312A; 189 BC * \")\"\n"); + strcat(s,"011A 80A9; 190 US GO DO IT\n"); + strcat(s,"011C 2E; 191 RT\n"); + strcat(s,"011D 2F; 192 :F3 BV F4 VARIABLE?\n"); + strcat(s,"011E A2; 193 FV YES. GET IT\n"); + strcat(s,"011F 12; 194 RT\n"); + strcat(s,"0120 2F; 195 :F4 BN F5 NUMBER?\n"); + strcat(s,"0121 C1; 196 RT GOT IT.\n"); + strcat(s,"0122 2F; 197 :F5 BC * \"(\" OTHERWISE MUST BE (EXPR)\n"); + strcat(s,"0123 80A8; 198 :F6 JS EXPR\n"); + strcat(s,"0125 30BC; 199 BC * \")\"\n"); + strcat(s,"0127 80A9; 200 RT\n"); + strcat(s,"0129 2F; 201 .\n"); + strcat(s,"012A ; 202 :ARG BC A0 \",\" COMMA?\n"); + strcat(s,"012A 83AC; 203 J EXPR YES, GET EXPRESSION\n"); + strcat(s,"012C 38BC; 204 :A0 DS NO, DUPLICATE STACK TOP\n"); + strcat(s,"012E 0B; 205 RT\n"); + strcat(s,"012F 2F; 206 .\n"); + strcat(s,"0130 ; 207 :FUNC BC * \"(\"\n"); + strcat(s,"0130 80A8; 208 BR F6\n"); + strcat(s,"0132 52; 209 RT\n"); + strcat(s,"0133 2F; 210 .\n"); + strcat(s,"0134 ; 211 :RELO BC R0 \"=\" CONVERT RELATION OPERATORS\n"); + strcat(s,"0134 84BD; 212 LB 2 TO CODE BYTE ON STACK\n"); + strcat(s,"0136 0902; 213 RT =\n"); + strcat(s,"0138 2F; 214 :R0 BC R4 \"<\"\n"); + strcat(s,"0139 8EBC; 215 BC R1 \"=\"\n"); + strcat(s,"013B 84BD; 216 LB 3 <=\n"); + strcat(s,"013D 0903; 217 RT\n"); + strcat(s,"013F 2F; 218 :R1 BC R3 \">\"\n"); + strcat(s,"0140 84BE; 219 LB 5 <>\n"); + strcat(s,"0142 0905; 220 RT\n"); + strcat(s,"0144 2F; 221 :R3 LB 1 <\n"); + strcat(s,"0145 0901; 222 RT\n"); + strcat(s,"0147 2F; 223 :R4 BC * \">\"\n"); + strcat(s,"0148 80BE; 224 BC R5 \"=\"\n"); + strcat(s,"014A 84BD; 225 LB 6 >=\n"); + strcat(s,"014C 0906; 226 RT\n"); + strcat(s,"014E 2F; 227 :R5 BC R6 \"<\"\n"); + strcat(s,"014F 84BC; 228 LB 5 ><\n"); + strcat(s,"0151 0905; 229 RT\n"); + strcat(s,"0153 2F; 230 :R6 LB 4 >\n"); + strcat(s,"0154 0904; 231 RT\n"); + strcat(s,"0156 2F; 232 .\n"); + strcat(s,"0157 ; 0000\n"); + return s;} /* ~DefaultIL */ + +/**************************** Startup Code ****************************/ + +void StartTinyBasic(char* ILtext) { + int nx; + for (nx=0; nx9) { + len = len/8+len; /* allow for line end expansion */ + IL = (char*)malloc(len+1); + if (IL != NULL) len = fread(IL,1,len,tmpFile); + IL[len] = '\0'; + IoFileClose(tmpFile);}} + else printf("Could not open file %s", argv[nx]);} + else if (strcmp(argv[nx],"-o")==0 && ++nx