NMH BASIC III Man Page
BASIC(1) General Commands Manual BASIC(1)
*** USAGE ***
BASIC [FILE ...]
*** DESCRIPTION ***
NMH BASIC is a minimal BASIC interpreter for systems with
little memory. It provides a basic (sic!) command set
and a built-in editor. Programs may either be typed in
interactively or read from an external file or device.
Programs can even be saved to and loaded from five-chan-
nel paper tape. NMH BASIC is intended for fun program-
ming.
*** STARTUP PARAMETERS ***
NMH BASIC treats each command line parameter that is
passed to it as a file name and attempts to open the re-
quested files for reading or writing. It prints each
file name and the unit number assigned to it. When it
fails to open a file, it prints an exclamation mark (!)
followed by the file name.
If a file name is prefixed with an exclamation mark, the
file will be opened for writing and otherwise the file
will be opened for reading. The access mode can be
changed later using the IOCTL function.
*** EDITING ***
There is a simple builtin editor that allows the program-
mer to enter and modify programs interactively.
NMH BASIC program memory consists of a sequence of num-
bered lines. Each command that starts with a number will
not be executed immediately but is inserted into program
memory instead. When entering a line with a line number
that already exists, the given line will be replaced. En-
tering a line number with no following text will delete
the given line.
The length of each line is limited to 64 characters.
Longer lines will cause an error.
The LIST command may be used to view lines stored in pro-
gram memory.
The NMH BASIC editor performs some on the fly error
checking. It checks only at scanner level, but this helps
to catch many typos while entering a program. When the
editor finds an error, it discards the erroneous line.
When entering programs, typing the first few characters
of a command name is typically enough. For example PR is
interpreted as PRINT, NE as NEXT, and SY as SYSTEM.
When entering program text or data, the backspace (<--)
or delete (DEL) key can be used to delete already entered
characters. Pressing Control-U will erase all input char-
acters. Pressing Control-C will abort the input opera-
tion.
When a program is executing, pressing Control-C will stop
the program and return to interactive mode.
*** PROGRAMS ***
A program consists of a number of lines, and each line
contains at least one BASIC statement, also known as a
command. Multiple statements may be placed in a single
line by separating them with colons (:).
*** EXPRESSIONS ***
An expression is everything that evaluates to a numeric
value at run time. NMH BASIC recognizes the four basic
math operators (+, -, *, /) and some other arithmetic,
comparison, and logic operators. Operators are used to
combine factors (binary operators) or modify factors
(unary operators). Valid factors are: numbers, variables,
and function values.
Numbers are numeric literals in the range -32767 to
32767. They represent themselves.
Variables are two-character sequences that represent a
variable numeric value. There are 260 numeric integer
variables in NMH BASIC that are named A0...Z9. The first
character must always be a letter, and the second one a
digit. Variables of the form X0 can also be written X.
Function values are the values returned by function
calls.
A function is called by typing the function name, fol-
lowed by a left parenthesis, some function arguments, and
a right parenthesis.
NMH BASIC accepts the following operators:
+-----+-----+------+-----------------------------------+
|Oper | Alt | Type | Description |
+-----+-----+------+-----------------------------------+
| ( ) | | 4 | grouping, array subscripts |
| - | | U4 | unary minus, negative prefix |
+-----+-----+------+-----------------------------------+
| * | ! | B3 | multiplication |
| / | | B3 | division |
| // | | B3 | division remainder |
| + | & | B2 | addition, string concatenation |
| - | | B2 | subtraction |
+-----+-----+------+-----------------------------------+
| = | EQ | B1 | equal to, same string |
| <> | UE | B1 | not equal to, not same string |
| < | LT | B1 | less than, earlier in alphabet |
| > | GT | B1 | greater than, later in alphabet |
| <= | LS | B1 | less/equal, earlier/same |
| >= | GS | B1 | greater/equal, later/same |
+-----+-----+------+-----------------------------------+
| # | | U0 | logical NOT |
| = | | B0 | assignment (only in LET commands) |
+-----+-----+------+-----------------------------------+
Types: U = unary, B = binary, number = precedence level
A higher precedence level indicates that an operator
binds stronger to its arguments. For example, the expres-
sion A+B*C will be interpreted as A+(B*C).
The comparison operators (=, <>, <, >, <=, >=) are only
accepted in condition contexts, i.e. in IF commands.
*** ARRAYS ***
Normally there are 260 integer variables. All these vari-
ables have a unique name (A0...Z9). Sometimes, however,
it is more practical to access variables like vector ele-
ments (through an index, or subscript). Each NMH BASIC
variable may be treated as an array without making any
special declarations. `A', for example, may also be ad-
dressed as `A(0)', A1 is equal to A(1), A(9) is equal to
A9. Larger subscripts cause on overflow to the next vari-
able letter: A(10) would be equal to B0, A(25) to C5,
etc. Z(10), finally, will cause a subscript error.
When more space is needed, or no variable letters must be
`wasted', there is the possibility to dimension an array.
Usually, the `Z' variables are used to build an array,
because they cannot have common memory locations with
other variables.
The statement
DIM Z(200)
will increase the size of Z0 to 200 elements. Note that
the free space for program text will be decreased by this
operation.
Dimensioning a different variable will not occupy that
much dynamic memory because the space for subsequent
variables is used up first. Using A0 as an array with 200
elements [DIM A(200)], for example, will not occupy any
dynamic memory, but will make the variables A1...T9
aliases of A(1)...A(199). In fact, of course, this is
already so, even without any DIM statement. The state-
ment
DIM A(200)
is essentially a null-statement, but may serve as a re-
minder.
*** STRING EXPRESSIONS ***
The second NMH BASIC data type is the string. Strings
are fixed-length memory locations that can be filled with
characters. They are used to store ASCII text.
There is only one operation that may be performed on
strings: the concatenation, which is done by the + opera-
tor.
Valid factors in string expressions are text literals,
string variables, and function values.
A text literal is a string of characters enclosed by
apostrophes. A literal apostrophe may be inserted by
typing it two times in a row. Text literals, like nu-
meric literals, represent themselves.
A string variable is a variable letter followed by a dol-
lar sign ($). There are 36 string variables: A$...Z$ and
0$...9$. String variables may not be dimensioned, but
they can be treated as arrays. For example, A$(1) is the
same as B$ or B$(0). Typically 0$ is used as an array of
10 strings.
The length of NMH BASIC strings is limited to 64 charac-
ters.
*** STATEMENTS ***
A statement (also called a "command") is the smallest ex-
ecutable element of a basic program. Each statement
starts with a keyword and is usually followed by some pa-
rameters and additional keywords. There are three kinds
of statements: simple statements, conditional statements,
and loop statements.
Most statements in NMH BASIC are simple statements. Ex-
amples are:
LET I = 195
GOTO 420
PRINT ASC(A$)
Conditional statements will only be executed when a con-
dition is true. There is only one form of the conditional
statement:
IF condition statement
The keyword IF is followed by a condition. Only when
this condition is true, the statement part will be exe-
cuted. For example,
IF A < 0 PRINT 'NEGATIVE'
will print 'NEGATIVE' only if A is less than zero.
Loop statements consist of three parts, the introducing
statement (FOR), a body consisting of any number of
statements, and a scope terminator (NEXT). In NMH BASIC
there is only one block statement and it looks like this:
FOR I = 1 TO 100
PRINT I,
NEXT
It implements a counting loop. In the example the en-
closed statement "PRINT I," will be executed 100 times.
Each time the loop is passed, the variable I will be in-
creased by one (at the end of the loop). So this loop
will print the numbers from 1 to 100.
NEXT is the scope terminator. When it is reached, the
counting variable is incremented, and if it has not
passed the limit, the enclosed statements are executed
again.
Multiple statements may be placed in the same line by
separating them with colons. E.g.:
FOR I = 1 TO 100 : PRINT I, : NEXT
*** COMMENTS ***
REM {string | command}
Ignore the given command or string. The REM command
can be used to temporarily disable a command without
removing it or, using a string, it can be used to
insert comments into a program.
*** ASSIGNMENT COMMANDS ***
LET var = expr
Evaluate expr and assign the result to var. If var
is a string variable, expr must be a string expres-
sion.
LET var(expr) = expr2
Evaluate expr2 and assign the result to the expr'th
slot of var. Slots start at an index of 0. If var
is a string variable, expr2 must be a string expres-
sion.
*** CONTROL COMMANDS ***
END
End program execution and return to interactive
mode.
FOR var = expr TO expr2 : [command ... :] NEXT
FOR var = expr TO expr2 STEP expr3 : [command ... :] NEXT
A counting loop. First set the variable var to the
value of expr, and then execute all statements up to
a matching NEXT keyword. Then increment var by
expr3. When no STEP and expr3 are given, incrment
by one. If, after incrementing, var is smaller than
or equal to expr2, execute all statements between
FOR and NEXT again.
When the increment expr3 is negative, the loop is
repeated while var is greater than or equal to
expr2.
All expressions are only evaluated once before the
first iteration of the loop!
FOR statements may be nested. The NEXT keyword then
pairs up with the most recently executed FOR state-
ment.
GOSUB line
RETURN
Perform a jump (like GOTO) to the given line number,
remembering the return address. The return address
is pushed onto an internal stack. A RETURN statement
may be used to pop this address off the stack and
continue execution at the statement immediately af-
ter GOSUB. Subroutine calls may be nested.
GOTO line
Jump to the given line number. Execution continues
with the first statement in the specified line. In
fact line may be any expression.
IF condition [, ...] command [: command ...]
Evaluate the condition, and execute the command(s)
only if the condition is true. When the condition is
false, execution continues with the first statement
in the following line (if any).
The condition may be any comparison operation, like
A=B or A<=B, where A and B are expressions. If there
is no comparison operation in the condition, then
the condition is considered to be `true', if its
value is non-zero.
The comparison operators can be used to compare
strings. In this case the equal operator (=) checks
if two strings are exactly equal, i.e. contain the
same characters in the same positions. The less-than
(<) operator checks if the first string would come
before the second string in a lexicon where entries
are ordered using the ASCII alphabet. The other com-
parison operators can also be used and have the
meaning described in the EXPRESSIONS section.
Multiple conditions may be separated by commas. In
this case all conditions have to be true in order
for the command(s) to be executed.
STOP
Stop program execution with an STP error.
SYSTEM
Exit from the interpreter. This will even work when
executing a program stored in memory.
*** INPUT/OUTPUT COMMANDS ***
INPUT #expr
Redirect input to the given unit number. All subse-
quent input will be read from the file or device as-
sociated with the unit number.
INPUT {var | strvar} [, ...]
Read input from the terminal (or redirected unit)
and assign each line of input to a variable. Input
will be automatically converted to the proper for-
mat.
When reading a string A$ from a unit on which no in-
put is available, the string will have the character
255 in its first slot, i.e. ASC(A$) = 255.
The INPUT command does not emit a prompt. Use
PRINT 'prompt';
for that.
PRINT
Print a newline sequence.
PRINT #expr
Redirect all output to the given unit number. Note
that the interpreter will reset the default output
unit when entering interactive mode.
PRINT {expr | strexpr} [{,|;} {expr | strexpr} ...]
[{,|;}]
Evaluate each given expression (string or numeric),
and write its value to the output unit (default:
terminal). Expressions may be separated by commas
or semicolons. A comma emits a horizontal TAB and a
semicolon pastes output together.
A newline character is emitted after the last ex-
pression unless the print statement ends with a
comma or semicolon.
READ {var | strvar} [, ...]
DATA {number | string} [, ...]
Read DATA items into the specified variables. The
items are taken from DATA lists which may be speci-
fied at any place in the program (but are typically
placed at the end).
Each item in the list must have the same type as the
corresponding variable in READ or an error will oc-
cur. Each READ operation advances the data pointer
by n items, where n is the number of variables in
the READ statement.
When the data pointer is moved past the end of the
list while READ executes, the operation will fail
with an error.
Each DATA statement defines a list of initialization
data. The data objects (string or numeric literals)
follow the DATA keyword as a space-separated list.
RESTOR
Reset the data pointer to the beginning of the ini-
tialization list (the first element of the first
DATA statement in the program).
*** PROGRAM AND STORAGE COMMANDS ***
CLEAR
Clear all variables and array dimensions. After is-
suing CLEAR, variables will be set to zero and
string variables will contain empty strings.
DIM var ( expr ) [, ...]
Define variable var as an array with size elements.
DIM does not actually allocate any memory to arrays
in most cases and serves documentary purposes only.
When the variable A is dimensioned with 100 ele-
ments, for example, the space used by the subsequent
variables (C,D, is used, so A(10) becomes an alias
of B0 and A(25) an alias of C5, etc. Only when the
end of the array moves beyond Z9, the space for
variables is actually extended. In this case program
memory is reduced.
LIST [expr1 [, expr2]]
List all lines from expr1 to expr2. When only one
line is given, list just that line. When no lines
are given, list all lines.
LOAD #expr
Redirect input to the specified unit number and pass
each line of input to the interpreter. When the end
of input is reached, redirect input back to the ter-
minal.
When done, LOAD leaves the file pointer at the EOF
of the given unit.
LOAD does not erase program memory, so multiple pro-
grams can be merged with LOAD. Merged programs
should not share line numbers.
NEW
Clear program memory and all variables and array di-
mensions.
RUN
Clear all variables and dimensions and then run the
program in program memory.
SAVE #expr
Redirect output to the given unit number and then
call LIST to write the program in program memory to
the associated file or device. After writing the
program, redirect output back to the terminal.
SAVE leaves the file pointer of the unit at the end
of the listing.
*** FUNCTIONS ***
ASC( strexpr )
Return the ASCII code of the first character in
string. Return 0 for an empty string.
FRE(0)
Return the number of free bytes in program memory.
IOCTL( expr1, expr2 )
Request the specified service (expr2) for unit num-
ber expr1.
Available services are:
100 rewind unit and switch to read-only mode
103 rewind and truncate unit and switch to write-only mode
200 switch unit to ASCII mode
201 switch unit to Baudot (CCITT-2) mode
When an input unit is in Baudot mode, input read
from the unit will be expected in CCITT-2 encoding
and will be automatically converted to to ASCII.
When an output unit is in Baudot mode, output will
be automatically converted to CCITT-2 encoding.
IOCTL returns a code that indicates the status of
the requested operation. Generally, zero means suc-
cess and non-zero means failure.
When an invalid service number is passed to IOCTL,
an error occurs.
LEN( strexpr )
Return the length of the given string expression.
An empty string has a length of zero.
VAL( strexpr )
Compute the decimal value represented by the given
string expression and return it. A leading minus
sign is recognized. Trailing non-digit characters
are ignored. Non-numeric strings will return zero.
*** STRING FUNCTIONS ***
CHR$( expr )
Return a one-character string that contains the
character corresponding to the ASCII code expr.
MID$( strexpr, expr [, expr2] )
Extract a substring from strexpr and return it. The
position expr1 specifies the first character to be
extracted and expr2 gives the length of the desired
substring.
When the position is greater then the length of the
string, an empty string will be returned.
When position+length is greater than the length of
strexpr, all characters from the position up to the
end of the string are returned.
The abbreviation
MID$(strexpr, expr)
can be used to extract all characters from the
starting position to the end of the string.
The first character in a string is at position one,
not zero, but zero is treated as an alias for one.
STR$( expr )
Convert the given value to a string of decimal dig-
its and return it. Negative numbers will have a
leading minus sign.
*** MACHINE CODE COMMANDS AND FUNCTIONS ***
On the processor-specific versions of NMH BASIC there are
commands and functions for reading and writing memory lo-
cations and calling machine code functions. These are not
present in the portable (T3X/0) version of the inter-
preter.
POKE expr1,expr2
Write the value expr2 to the memory location expr1.
The value is written to a byte-sized cell, so it
will be truncated to eight bits.
PEEK( expr )
Read the byte stored at the given memory location
and return it.
USR( expr )
Call the machine code function stored at the given
address and return the value returned by it. On a
Z80 CPU, this would be the value contained in the HL
register when the machine code function returns.
*** ERROR CODES ***
When NMH BASIC detects an error, it will print a three
letter code, followed by a colon and the line that caused
the error. When an error occurs in execution mode, the
program will stop.
ARG
Bad argument - an invalid value has been passed to a
function.
BRK
Break - program stopped by user.
DIV
Division by zero. Also caused by X // 0.
EOD
End of data - a READ statement contains more vari-
ables than there are remaining items in the initial-
ization lists (DATA).
ILN
Invalid line number - a GOTO or GOSUB statement con-
tains a non-existant line number.
LLO
Line too long - a line exceeds the maximum length of
64 characters.
MEM
Insufficient memory - caused by DIM or by inserting
lines into program memory.
NST
Nesting error - a RETURN statement occurs where NEXT
has been expected, or vice versa. Also occurs when a
statement that is being LOADed attempts to LOAD an-
other program (LOAD may not be nested) or when the
return stack is not empty when a program terminates.
OVF
Overflow - a numeric literal does not fit in 15 bits
+ 1 sign bit. The valid range for numeric literals
is -32767 to 32767.
PTX
Paper tape error - a program could not be saved to
paper tape, because it contains unconvertible char-
acters.
SBO
String buffer overflow - the concatenation of two
strings is longer than 64 characters.
SXO
String expression overflow. This happens when too
few temporary buffers are available in string opera-
tions. A string expression is too complex.
STP
Stop - program stopped by a STOP command.
STK
Stack error - the internal return stack has over-
flowed. Caused by nesting subroutines or FOR-NEXT
loops too deeply.
SUB
Bad subscript - caused by an attempt to access a
non-existant array element. This happens, for exam-
ple, when an element beyond Z(9) is referenced with-
out first allocating storage to it via DIM.
SYN
Syntax error - the most common error with an infi-
nite number of possible causes . Frequently caused
by typos. Generally, the input that causes this er-
ror is not a valid BASIC statement.
TYP
Type mismatch - a variable in a READ statement has a
different type than the current item in the initial-
ization list (DATA).
XXX
Aaah! - interpreter in hopeless confusion - time to
bail out!
*** EXAMPLES ***
Print a sixbit ASCII table.
100 FOR I = 32 TO 95
110 PRINT CHR$(I); ' '; I-32,
120 NEXT
Load (and merge) programs from units 3 and 4, then save
the merged program to unit 5. The IOCTL call rewinds and
truncates unit 5.
LOAD #3
LOAD #4
PRINT IOCTL(5, 103)
SAVE #5
Print data from unit 5. The IOCTL call rewinds the unit.
100 LET X = IOCTL(5, 100) : INPUT #5
110 INPUT A$ : IF ASC(A$) = 255 INPUT #0 : END
120 PRINT A$ : GOTO 110
*** DIFFERENCES BETWEEN THE INTERPRETERS ***
The CP/M version of the interpreter connects unit two to
AUX:/RDR: and unit three to AUX:/PUN:. There are only
seven units in total, i.e. only units 4,5,6 can be con-
nected to files.
The DOS version connects unit two to SYSERR, unit three
to AUX:, and unit four to PRN:.
The Unix versions connect unit two to SYSERR.
*** DIFFERENCES TO EARLIER VERSIONS ***
The CMPS function has been replaced with string compari-
son operators, like A$ = 'FOO', A$ < B$, etc.
Expressions can no longer contain comparison operators.
These are limited to IF statements. E.g. LET X = A < B is
no longer a valid statement.
The division remainder operator is // instead of \.
Commands can be abbreviated, e.g. PR for PRINT or SY for
SYSTEM.
There are baudot-compatible keywords and symbols for some
operators, e.g. LT for <, GS (greater or same) for >=, &
for +, etc.
The IOCTL services 101 (truncate) and 102 (append) have
been removed. The service 103 (rewind and truncate) has
been added.
The IOCTL service 100 (rewind) now switches its unit to
read-only mode.
The new IOCTL service 200 (ASCII) switches a device to
ASCII mode.
The new IOCTL service 201 (Baudot) switches a device to
baudot mode.
The CALL, TRON, and TROFF commands have been removed.
The POKE command and the PEEK function have been removed
from the portable T3X/0 version, but remain in the CP/M
version.
The USR function was added to the CP/M version.
The ESXO (string expression overflow) and EPTX (paper
tape transcoding) errors have been added.
The ENIM (not implemented) error has been removed.
Units are now opened read-only by default. They can be
opened write-only by using the !file syntax on the com-
mand line.
*** NOTES ***
Some versions of the interpreter expect programs in DOS
file format, i.e. lines terminated with CR,LF. All ver-
sions of the interpreter do accept DOS format, some also
accept Unix (LF) format.
When saving programs or data using baudot encoding, some
characters will be replaced with alternative characters
or sequences. When reading such data back, only BASIC to-
kens will be converted back. E.g., the line
100 LET X = X+1 : REM 'X = X + 1'
will be saved as
100 LET X EQ X&1 : REM 'X EQ X & 1'
and then read back as
100 LET X = X+1 : REM 'X EQ X & 1'
Characters in strings will not be converted back, because
they are not BASIC tokens. Characters read via INPUT will
never be converted.
***** BUGS *****
The interpreter will misinterpret string expression when
they are too complex. It will catch some of these condi-
tions, but not all of them, so it is best to keep string
expressions short and simple.
In particular, nesting MID$ or concatenation (via string
function arguments) will probably cause trouble.
When the interpreter misinterprets expressions or reports
SXO errors, rewrite the offending expression using string
variables for intermediate results.
When the INPUT function reads data from the console on
CP/M, it is possible to backspace past the beginning of
the input. This is only a visual artefact, though, and no
data corruption will occur.
When loading baudot-encoded programs from a file on CP/M,
a syntax error will occur, if the program size is not an
exact multiple of 128. The offending line will consist
of a number of 'J' or apostrophe characters. This error
can be ignored. The reason for this error is that 01Ah,
which CP/M uses for padding, is a valid baudot character.
*** LICENSE ***
NMH BASIC
By Nils M Holm, 1991-1994,2023-2025
MAIL: fs29@rummelplatz.uni-mannheim.de (DEFUNCT!)
NMH BASIC is free software, do whatever you want with it.
If it breaks something, don't blame me!
BASIC(1)