Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 7140 → Rev 7141

/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 $@ $<
/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
/programs/develop/tinybasic/TinyBasic.c
0,0 → 1,1541
/* Tiny Basic Intermediate Language Interpreter -- 2004 July 19 */
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h> /* added 08 Oct 31 */
 
#if defined(__TINYC__)
#include <conio.h>
#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<UserEnd; ix++) {
OutStr(" ");
OutInt(Peek2(ix++));}
OutStr("]");} /* ~ShowSubs */
 
void ShowExSt(void) { /* display expression stack for debugging */
int ix;
OutLn(); OutStr(" [Exp "); OutHex(ExpnTop,3);
if ((ExpnTop&1)==0) for (ix=ExpnTop; ix<ExpnStk; ix++) {
OutStr(" ");
OutInt((int)((short)Peek2(ix++)));}
else for (ix=ExpnTop; ix<ExpnStk; ix++) {
OutStr(".");
OutInt((int)Core[ix]);}
OutStr("]");} /* ~ShowExSt */
 
void ShowVars(int whom) { /* display vars for debugging */
int ix, valu = 1, prior = 1;
if (whom==0) whom = 26; else {
whom = (whom>>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 (thar<here) {OutStr(" "); thar++;}
do {
OutStr(" ");
if (nlocs-- >0) OutHex(Core[here],2);
else OutStr(" ");}
while (++here%16 !=0);
OutStr(" ");
while (temp<thar) {OutStr(" "); temp++;}
while (thar<here) {
if (nlocs<0) if ((thar&15) >= 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)); ix<LOGSIZE; ix++) ShoLogVal(ix);
for (ix=0; ix<(LogHere&(LOGSIZE-1)); ix++) ShoLogVal(ix);
OutLn();
OutStr("*****");
OutLn();} /* ~ShowLog */
 
void LogIt(int valu) { /* insert this valu into activity log */
DebugLog[(LogHere++)&(LOGSIZE-1)] = valu;}
 
/************************ Utility functions... ************************/
 
void WarmStart(void) { /* initialize existing program */
UserEnd = Peek2(EndUser);
SubStk = UserEnd; /* empty subroutine, expression stacks */
Poke2(GoStkTop,SubStk);
ExpnTop = ExpnStk;
Lino = 0; /* not in any line */
ILPC = 0; /* start IL at front */
SvPt = InLine;
BP = InLine;
Core[BP] = 0;
Core[TabHere] = 0;
InLend = InLine;} /* ~WarmStart */
 
void ColdStart(void) { /* initialize program to empty */
if (Peek2(ILfront) != ILfront+2) ILend = Peek2(ILfront)+0x800;
Poke2(UserProg,(ILend+255)&-256); /* start Basic shortly after IL */
if (CoreTop>65535) {
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 (ExpnTop<ExpnStk) return (int)Core[ExpnTop++];
TBerror(); /* underflow (nothing in stack) */
return -1;} /* ~PopExBy */
 
void PushExInt(int valu) { /* push integer onto expression stack */
ExpnTop = ExpnTop-2;
if (ExpnTop<InLend) TBerror(); /* overflow: bumped into input line */
else Poke2(ExpnTop,valu);
if (Debugging>0) ShowExSt();} /* ~PushExInt */
 
int PopExInt(void) { /* pop integer off expression stack */
if (++ExpnTop<ExpnStk) return (int)((short)Peek2((ExpnTop++)-1));
TBerror(); /* underflow (nothing in stack) */
return -1;} /* ~PopExInt */
 
int DeHex(char* txt, int ndigs) { /* decode hex -> 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<InLine || 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 (ExpnTop<ExpnStk) { /* or more numbers, so get them.. */
op = ix;
ix = PopExInt();} /* get final line #, then initial.. */
if (op<0 || ix<0) TBerror();
else ListIt(ix,op);
break;
 
/* PN 20 Print Number. */
/* The number represented by the top two bytes of the */
/* expression stack is printed in decimal with leading zero */
/* suppression. If it is negative, it is preceded by a minus sign */
/* and the magnitude is printed. Stack underflow is possible. */
case 32:
ix = PopExInt();
if (ILPC != 0) OutInt(ix);
break;
 
/* PQ 21 Print BASIC String. */
/* The ASCII characters beginning with the current */
/* position of BASIC pointer are printed on the console. The string */
/* to be printed is terminated by quotation mark ("), and the BASIC */
/* pointer is left at the character following the terminal quote. An */
/* error stop occurs if a carriage return is imbedded in the string. */
case 33:
while (true) {
ch = (char)Core[BP++];
if (ch=='\"') break; /* done on final quote */
if (ch<' ') { /* error if return or other control char */
TBerror();
break;}
Ouch(ch);} /* print it */
break;
 
/* PT 22 Print Tab. */
/* Print one or more spaces on the console, ending at */
/* the next multiple of eight character positions (from the left */
/* margin). */
case 34:
do {Ouch(' ');} while (Core[TabHere]%8>0);
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; here<SrcEnd; here++)
Core[here] = Core[here-op]; /* shift it left to close gap */
if (ix>0) 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) && op<ILend) { /* call IL subroutine.. */
PushExInt(here);
PushExInt(ix);
PushSub(ILPC); /* push return location */
ILPC = op;
if (DEBUGON>0) 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<Peek2(ILfront) || 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; nx<CoreTop; nx++) Core[nx] = 0; /* clear Core.. */
Poke2(ExpnStk,8191); /* random number seed */
Core[BScode] = 8; /* backspace */
Core[CanCode] = 27; /*escape */
for (nx=0; nx<32; nx++) DeCaps[nx] = '\0'; /* fill caps table.. */
for (nx=32; nx<127; nx++) DeCaps[nx] = (char)nx;
for (nx=65; nx<91; nx++) DeCaps[nx+32] = (char)nx;
DeCaps[9] = ' ';
DeCaps[10] = '\r';
DeCaps[13] = '\r';
DeCaps[127] = '\0';
if (ILtext == NULL) ILtext = DefaultIL(); /* no IL given, use mine */
ConvtIL(ILtext); /* convert IL assembly code to binary */
ColdStart();
Interp(); /* go do it */
if (oFile != NULL) IoFileClose(oFile); /* close output file */
if (inFile != NULL) IoFileClose(inFile); /* close input file */
oFile = NULL;
inFile = NULL;} /* ~StartTinyBasic */
 
int main(int argc, char* argv[]) {
/* CONSOLE_INIT("TinyBasic"); */
#if defined(__TINYC__)
if (con_init_console_dll()) return 1; // init fail
#endif
 
int nx;
long int len;
char* IL = NULL;
FileType tmpFile;
inFile = NULL;
oFile = NULL;
for (nx=1; nx<argc; nx++) { /* look for command-line args.. */
if (strcmp(argv[nx],"-b")==0 && ++nx<argc) { /* alt IL file.. */
tmpFile = fopen(argv[nx],"r");
if (tmpFile != NULL) if (fseek(tmpFile,0,SEEK_END)==0) {
len = ftell(tmpFile); /* get file size.. */
if (fseek(tmpFile,0,SEEK_SET)==0) if (len>9) {
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<argc) /* output file */
oFile = fopen(argv[nx],"w");
else if (strcmp(argv[nx],"-i")==0 && ++nx<argc) /* input file */
inFile = fopen(argv[nx],"r");
else if (inFile==NULL) /* default (unadorned) is also input file */
inFile = fopen(argv[nx],"r");} /* ignore other args */
 
#ifdef DefaultInputFile
if (inFile==NULL) inFile = fopen(DefaultInputFile,"r");
#endif
#ifdef DefaultOutputFile
if (oFile==NULL) oFile = fopen(DefaultOutputFile,"w");
#endif
 
StartTinyBasic(IL); /* go do it */
return 0;} /* ~main */
 
char *ExplainErr(int code)
{
switch (code)
{
case 0: return "Break during execution";
case 8: return "Memory overflow; line not inserted";
case 9: return "Line number 0 not allowed";
case 13: return "RUN with no program in memory";
case 18: return "LET is missing a variable name";
case 20: return "LET is missing an =";
case 23: return "Improper syntax in LET";
case 25: return "LET is not followed by END";
case 34: return "Improper syntax in GOTO";
case 37: return "No line to GO TO";
case 39: return "Misspelled GOTO";
case 40:
case 41: return "Misspelled GOSUB";
case 46: return "GOSUB subroutine does not exist";
case 59: return "PRINT not followed by END";
case 62: return "Missing close quote in PRINT string";
case 73: return "Colon in PRINT is not at end of statement";
case 75: return "PRINT not followed by END";
case 95: return "IF not followed by END";
case 104: return "INPUT syntax bad - expects variable name";
case 123: return "INPUT syntax bad - expects comma";
case 124: return "INPUT not followed by END";
case 132: return "RETURN syntax bad";
case 133: return "RETURN has no matching GOSUB";
case 134: return "GOSUB not followed by END";
case 139: return "END syntax bad";
case 154: return "Can't LIST line number 0";
case 164: return "LIST syntax error - expects comma";
case 183: return "REM not followed by END";
case 184: return "Missing statement type keyword";
case 186: return "Misspelled statement type keyword";
case 188: return "Memory overflow: too many GOSUB's ...";
case 211: return "Memory overflow: ... or expression too complex";
case 224: return "Divide by 0";
case 226: return "Memory overflow";
case 232: return "Expression too complex ...";
case 233: return "Expression too complex ... using RND ...";
case 234: return "Expression too complex ... in direct evaluation";
case 253: return "Expression too complex ... simplify the expression";
case 259: return "RND (0) not allowed";
case 266: return "Expression too complex ...";
case 267: return "Expression too complex ... for RND";
case 275: return "USR expects \"(\" before arguments";
case 284: return "USR expects \")\" after arguments";
case 287: return "Expression too complex ...";
case 288: return "Expression too complex ... for USR";
case 290: return "Expression too complex";
case 293: return "Syntax error in expression - expects value";
case 296: return "Syntax error - expects \")\"";
case 298: return "Memory overflow (in USR)";
case 303: return "Expression too complex (in USR)";
case 304: return "Memory overflow (in function evaluation)";
case 306: return "Syntax error - expects \"(\" for function arguments";
case 330: return "IF syntax error - expects relation operator";
default: return "Unknown error, interpreter is malfunctioning";
}
}
/programs/develop/tinybasic/make_tcc.bat
0,0 → 1,3
:also using tcc can be compiled in Kolibri
 
kos32-tcc.exe TinyBasic.c -lck