© 2009–2017, Lyle Kopnicky
Congratulations on installing Vintage BASIC! You are now the proud owner of a copy of a versatile tool for BASIC development.
Vintage BASIC is a language derived from the Microsoft's original Altair BASIC. It would feel right at home on a Commodore 64. The design tensions of Vintage BASIC are:
Vintage BASIC requires minimal resources, and will run on Windows, Mac, and Linux. (It can be compiled from source to run on these or other platforms, too.) Please see the downloads page for installation instructions.
The interpreter consists of one command-line tool, vintbas
.
It takes one filename parameter, the name of a BASIC source file.
By convention (but not fiat), it should end in .bas
. So, if you've written
BASIC code and saved it in miracle.bas
, you'd
run it by typing vintbas miracle.bas
.
If you have used the Windows installer, you can open Vintage BASIC using the
Vintage BASIC Prompt item in the Vintage BASIC folder on the Start Menu. Or, you
can just open Command Prompt. If you have a BASIC program saved with a .bas
extension, you can double-click on it in Explorer to run it.
Each line in your BASIC source file should start with a BASIC
line number (also called a label).
These numbers are part of the text at the start of the
line, and not just a count of lines from the start of the file. They
are used as labels for GOTO
, GOSUB
,
and THEN
targets, as well as in RESTORE
statements. Here is a sample BASIC program:
10 INPUT"YES OR
NO";A$
20 IF A$ = "YES" THEN 50
30 IF A$ = "NO" THEN 60
40 PRINT"YOU MUST ENTER YES OR NO.":GOTO 10
50 PRINT"GREAT!":END
60 PRINT"WHY NOT?":END
Barring any flow control changes, the lines are executed in order by line number, regardless of the sorting of the input file. If two lines in the input file have the same line number, the later one will take precedence (you will get a warning when lines are superseded). A space is not required following the line number, but it aids in readability.
Control flow will end when your program reaches the end of the
lines, when an END
or STOP
statement is encountered, or when an error occurs. Errors may occur
during parsing or execution, and always being with an exclamation point
(!
).
BASIC keywords are not case sensitive. You may freely use lowercase, uppercase, or mix both. Examples will be shown in caps since that is traditional for the language. Literal strings in quotes are case sensitive. They will be printed in the case used in the source.
Spaces are not required between keywords. This is a violation
of the ANSI standard, but was implemented on some early microcomputers
in order to save memory. It will aid in compatibility with running
programs written for such computers. Spaces are encouraged because the
code can be harder to read without them. For example, FORK=1TON
appears to set the value of a variable FORK
to a weight of 1 ton. In reality it begins a FOR
loop with control variable K
, ranging in
value from 1
to N
.
Ignoring spaces means that keywords must be tokenized by the parser
before the parsing phase. It also makes it difficult to use long
variable names, which might accidentally contain keywords. For example,
FACTOR
contains the keyword TO
.
Experience shows that people can get used to reading code without
spaces.
-1
, and the canonical
false value is 0
. These are the values that
will be generated by comparison operators. However, any nonzero value
will be treated as true by the boolean operations.
Expressions in BASIC, like in most languages, are used to
compute a value. They can be used in many statements, such as LET
,
DIM
, ON-GOTO
, ON-GOSUB
,
IF-THEN
, FOR
, PRINT
,
and DEF FN
. They consist of literal values
and variable references, composed by operators and function calls.
Let's look at each of these in turn.
0
,
2
, 5.
, 5.6
,
90000000
, -.01
, 1E-20
.
Precision depends upon your platform, but is generally IEEE bin32
(single precision). There are no special IEEE values, like +Inf or NaN.
String literals are enclosed in quotes, e.g. "HELLO"
.
There are no escape sequences. To generate a quote character, append a CHR$(34)
.MILK123
and MINT145
are considered the same.$
, and integer
variables with a %
, while floating-point
variables have no type indicator. Variable names with the same
significant letters and digits but different type indicators are
distinct. Thus, you can store a string in A$
,
a floating-point value in A
, and an integer
value in A%
without a conflict.A(3)
is the value from
index 3
in the floating-point array named A
,
and FR$(2,3)
is a string value from index 2
(in the first dimension) and 3 (in the second dimension) in an array
named FR$
. Scalar and array variables are
also distinct from each other, so it is possible to have a scalar
variable A
with value 3
,
so that A(A)
would be A(3)
and might contain some other value.FN
varname(
expr1,
expr2,
...)
DEF FN
statements, they are
accessed in expressions by preceding the function name with the FN
keyword. Example: FN A(4)
calls the
floating-point user-defined function A
with
argument value 4
.(
expr1,
expr2,
...)
(
expr)
-
expr+
expr^
expr2*
expr2/
expr2+
expr2-
expr2=
expr20
if the two strings or numbers are equal, -1
if they differ.<>
expr2-1
if the two strings or numbers are equal, 0
if
they differ.<
expr2-1
if the
first string or number is less than the second, 0
otherwise. (Strings are compared alphabetically.)<=
expr2-1
if
the first string or number is less than or equal to the second, 0
otherwise. (Strings are compared alphabetically.)>
expr2-1
if
the first string or number is less than the second, 0
otherwise. (Strings are compared alphabetically.)>=
expr2-1
if the first string or number is less than or equal to the second, 0
otherwise. (Strings are compared alphabetically.)NOT
expr0
and 0
becomes -1
.AND
expr20
,
then the result is the value of expr1.
Otherwise, the result is 0
.OR
expr2Operators | Associativity |
unary - , unary + |
N/A |
^ |
right |
* , / |
left |
+ , - |
left |
= , <> ,
< , <= ,
> , >= |
left |
NOT |
N/A |
AND |
left |
OR |
left |
ABS(
float)
ASC(
string)
ATN
(
float)
CHR$(
float)
COS(
float)
EXP(
float)
INT(
float)
LEFT$(
string,
float)
LEN(
string)
LOG(
float)
MID$(
string,
float
)
MID$(
string,
float,
float)
RIGHT$(
string,
float)
RND(
float)
SGN(
float)
SIN(
float)
SPC(
float)
SQR(
float)
STR(
float)
PRINT
, but
without the trailing space.TAB(
float)
PRINT
statements, but in Vintage BASIC, it is just another builtin function.
Vintage BASIC keeps track of the output column as text is printed. The TAB
function generates a string with the number of spaces required
to advance the cursor to the column number specified by the
argument. Column numbers start with zero, which is consistent with
Microsoft BASIC but not the ANSI standard, which starts with one
instead. If the cursor is already past the desired column, the result
string will be empty. This behavior is consistent with
Microsoft BASIC but not the ANSI standard, which specifies that the
cursor should move to a new line before tabbing to the specified column.TAN(
float)
VAL(
string)
DATA
literal1,
literal2,
...READ
statement. Each value can be a string or floating-point literal (not an
expression). Whitespace is ignored around values. Double quotes can be
placed around a string to escape whitespace and commas between the
quotes. DATA
statements can occur on
the same line as other statements, but, due to its special parsing
rules, it must be the last statement on the line. The line on which the
DATA
statement occurs can be used as the
target of a RESTORE
statement. Example: DATA
January, 31, "Martian History Month"
.DEF FN
varname(
arg1,
arg2,
...)
=
exprDEF
FN F(X) = X^2 + 3*X - 4
. The argument variables shadow
the original variables; that is, the value of the original variable
with the same name will be restored after the function is evaluated.
Like scalar variables but unlike arrays, user-defined functions may be
repeatedly redefined. Warning: Recursive functions are definable, but
will always result in an infinite loop.DIM
varname(
bound1,
bound2,
...)
DIM
RX$(20, 5)
creates a two-dimensional, 20x5 array of
strings.END
FOR
varname = start TO
end [STEP
increment]FOR-NEXT
loop, and its termination conditions. The control variable must be a
floating-point variable. It is initialized to the value of the start expression.
Each time a NEXT
is encountered, the control
variable is incremented by one, or by increment if the
optional STEP
clause is specified. If the
value of the control variable has not exceeded the value
of the end
expression, control will continue with the statement following the FOR
.
But if the value of the control value exceeds end (greater than
end if increment
is positive, less than end if increment
is negative), control will pass to the statement following the NEXT
.
The expressions start,
end,
and increment
are evaluated only once at the start of the loop. The termination
condition is not evaluated before entering the loop, as it is not
always possible to determine where the corresponding NEXT
might be, should the loop meet the termination condition. (This
contradicts the ANSI standard, but the ANSI standard is unenforceable.)
Example: FOR I = 1 TO 20 STEP 2: PRINT I, I*2: NEXT I
.GOSUB
labelRETURN
statement, control will
return to the statement following the GOSUB
.
This is the cornerstone of structured programming in BASIC. Note: GO
and SUB
are separate keywords and may be
separated by space.GOTO
labelGO
and TO
are separate keywords and may be
separated by space.
IF
expr THEN
statement1:
statement2:
...IF
expr
THEN
label
IF
expr THEN
GOTO
label.
Expressions are expected to have floating-point values. They are
considered false if zero and true otherwise. Note: if statements follow
the label
form on the same line, they are ignored. Example: IF
A>0 THEN Y=Y+3:GOTO 80
.INPUT
[prompt;
]
var1,
var2,
...DATA
statement, so
quotes can be used to escape spaces or commas. Value types entered must
match the variable types, or an error will be generated and the user
will be re-prompted. The user will also be re-prompted (with a double
question mark) if the user enters insufficient values to fill the
variables. The variables may be scalar or (indexed) array variables.
Examples: INPUT "NAME AND AGE";NA$(I), AG(I)
.LET
var = exprLET
keyword is optional.NEXT
[var]FOR
statement to determine whether the loop should be repeated. If the
termination condition has not been met, control will proceed with the
line following the FOR
statement. If the
termination condition has been met, control will proceed with the
statement following the NEXT
. How does the
interpreter determine which FOR
goes with the
NEXT
? It is determined dynamically. If the
NEXT
has no variable, then it is the most
recently encountered non-terminated FOR
. If
the NEXT
indicates a variable, then it is the
most recently encountered non-terminated FOR
with that control variable. Usually, it is simple to glance at code and
see which pairs of FOR
and NEXT
statements go together. But due to the dynamic nature of the connection
between them, it is possible to write NEXT
statements that are sometimes connected with one FOR
,
sometimes another. For an example, see FOR
.ON
expr GOSUB
label1,
label2,
...GOSUB
, but makes a decision
about the target line based on the result of evaluating an expression.
The value of expr
is rounded down to the nearest integer, and is used as an index into
the list of labels, starting with 1. If the index is less than 1 or
greater than the number of labels, control falls through to the next
statement (as in Microsoft BASIC but in violation of the ANSI standard,
which prescribes a runtime error).ON
expr GOTO
label1,
label2,
...GOTO
, but makes a decision
about the target line based on the result of evaluating an expression.
The value of expr
is rounded down to the nearest integer, and is used as an index into
the list of labels, starting with 1. If the index is less than 1 or
greater than the number of labels, control falls through to the next
statement (as in Microsoft BASIC but in violation of the ANSI standard,
which prescribes a runtime error).PRINT
expr1
[;
|,
]
expr2 [;
|,
]
...
PRINT
statement ends in a semicolon or comma. Floating-point values are
automatically
padded with one trailing space, and positive values are also padded
with a leading space, so that they take up the same amount of space as
negative numbers. Example: PRINT "TURN";TU, "YOU
HAVE";LI;"LIVES LEFT"
.RANDOMIZE
RND
function, based on the number of seconds that have elapsed since
midnight, local time.READ
var1,
var2,
...DATA
statements
into variables. A pointer is maintained into the DATA
values, which could be anywhere within the program. Values are read in
order into the variables, and the pointer is advanced. A runtime error
occurs if there are not enough DATA
values to
fill the variables. The DATA
pointer can be
reset using a RESTORE
statement. Example: READ
A$, B
.REM
textREM
BRIDGE BY J. Q. PROGRAMMER
.RESTORE
[label]DATA
value pointer.
Without a line number, the pointer is reset to the first DATA
statement of the program. With a line number, the
pointer is moved to the first DATA
statement
on or following that line.RETURN
GOSUB
to which control has
not already been RETURN
ed. GOSUB-RETURN
pairs can be nested to any depth. If there is no such GOSUB
,
a runtime error occurs. As with FOR-NEXT, association of a RETURN
with a GOSUB
is dynamic; it cannot be
statically determined in all cases.STOP
In early BASICs, the END
statement could only appear at the end of the program. The STOP
statement was designed to send control to that solitary END
statement. Vintage BASIC imitates this behavior by treating the STOP
statement as an END
. This is unlike Microsoft
version of BASIC, in which STOP
is used to
terminate the program without clearing variables, so that they can be
inspected for debugging purpose, and the CONT
statement can resume control where it left off. That wouldn't be very
useful in Vintage BASIC, since it does not have a read-eval-print loop.!BAD GOSUB TARGET
label1
in line
label2GOSUB
.!BAD GOTO TARGET
label1
in line
label2GOTO
.!BAD RESTORE TARGET
label1
in line
label2RESTORE
.!BREAK IN LINE
labelSTOP
statement was encountered.!DIVISION BY ZERO IN LINE
label!END OF INPUT IN LINE
labelINPUT
characters
failed because the end of the standard input stream was reached.!INVALID ARGUMENT IN LINE
label!LINE NUMBERING ERROR IN RAW LINE
nn!MISMATCHED ARRAY DIMENSIONS IN LINE
label!NEGATIVE ARRAY DIM IN LINE
labelDIM
statement.!NEXT WITHOUT FOR ERROR IN LINE
labelNEXT
statement was encountered,
but a matching FOR
statement could not be
found. Either a FOR
statement was never
encountered, or all FOR
statements have
reached their termination conditions.!NEXT WITHOUT FOR ERROR (VAR
X) IN
LINE
labelNEXT
statement was encountered,
but a matching FOR
statement could not be
found. Either a FOR
statement with that
control variable was never encountered, or all FOR
statements with that control variable have reached their termination
conditions.!OUT OF ARRAY BOUNDS IN LINE
label!OUT OF DATA IN LINE
labelREAD
statement in this line
tried to read data, but the DATA
pointer has
already reached the end of data strings in the program. You may have
too few DATA
statements, or a bad loop
termination condition that causes too many READ
s.!REDIM'D ARRAY IN LINE
labelDIM
statement was encountered,
but the array has already been dimensioned, either explicitly (through
anorther DIM
statement), or automatically
(through reference to the array).!RETURN WITHOUT GOSUB ERROR IN LINE
labelRETURN
statement was
encountered, but a matching GOSUB
statement
could not be found. Either a GOSUB
statement
was never encountered, or all GOSUB
statements have already been RETURN
ed to.!SYNTAX ERROR IN LINE
label!TYPE MISMATCH IN LINE
labelREAD
statement, the type of the variable did not match the value read.!UNDEFINED FUNCTION
X IN
LINE
labelFN
but has never been defined by DEF FN
.!WRONG NUMBER OF ARGUMENTS IN LINE
label