BASIC (Basic Programming Language) - Nakazoto/CenturionComputer GitHub Wiki

BASIC

Surprisingly, Centurion did include BASIC in some form on the OS. However, it is not like the interactive BASIC such as Microsoft BASIC provided with 1970s and 1980s microcomputers. It is instead compiled into an executable, but that executable can only be run if the Basic Monitor has been loaded. Other than that, the BASIC language does behave like fairly typical Integer BASIC, although heavily influenced by CPL and the Centurion batch file processing paradigm. It is interesting to note that Centurion BASIC does support UPSI and PUPSI, meaning it has the ability to pass values back and forth with JCL scripts. This really elevates the usefulness of BASIC on the Centurion.

INSERT SCREENSHOT HERE

Hellorld! Example

The following is an example BASIC program that prints "Hellorld!" 10 times on the screen.

10 FOR I = 1 TO 10
20 PRINT "HELLORLD!"
30 NEXT I

Compiling

To actually compile this program, it must first be typed up in an ASCII file. Follow the steps below to create the ASCII file, edit it, and then compile and execute the program. These steps are written with the Operating System loaded onto the fixed disk (1) and the default disk set to 1.

  1. .NEW ZHELLO ON 1 'A' 1S
    • Can be skipped if using CED as it will create the file for you
    • File name must have "Z" at the beginning
  2. .NEW @SCRE0 ON 1 'E' 1T
    • Only needs to be done if file doesn't already exist
  3. S.CED
    • CED is a wonderful line editor that can be used to write code into ZHELLO
  4. P.BASIC HELLO 1 CRT0 X
  5. .LOAD BASIC
    • Must be run once to load the basic monitor.
  6. .RUN XHELLO

The compiler is a single pass translator which produces a Centurion executable consisting of a stub to call the BASIC monitor followed by the program logic converted to Reverse Polish Notation bytecode. The BASIC monitor executes the bytecode which follows the stub.

P.BASIC JCL script

The easiest way of compiling BASIC is through the P.BASIC JCL script.

Syntax: P.BASIC job disk output [map] [lib]

job: the name of the source file without the Z prefix
disk: the disk number of the location of the source file
output: an output device (e.g., CRT0) or use LST to output to the file @LSTn where n is the partition (e.g., @LST0)
MAP: the compiled bytecode will be listed on the output device
LIB: a library filename will be requested and the preprocessor will be run.

If LST is specified as the output device, if there are compile errors S.SCAN will be executed for review of the source code and errors.

If LIB is specified the script will prompt for a library file and execute the preprocessor XBSPRP. This allows subroutine libraries to be provided and inserted into the source file before compilation.

If there are compile errors S.TXT will execute to allow correction.

XBSPRP preprocessor

If the LIB option is provided to P.BASIC then the preprocessor is executed. P.BASIC will prompt for a copy library to be used as the source file for copying subroutines into the original program.

There is a reserved statement for the preprocessor of COPY which specifies the "book" in the library.

The format of a book copy entry is as follows:

  • ( as the first character of the line followed by the book name of up to six characters. The rest of the line is ignored.
  • One or more lines of the code to be copied.
  • If the line starts with a \ followed by a four digit number then it can be used as a label for a GOSUB or GOTO in the copied code. The \ and number are optional.
  • The entry ends with the start of the next entry indicated by ( or the end of file.

An example of a book entry for printing HELLORLD! 10 times:

(HELLO PRINT HELLORLD
\0001LET I = 0
\0002PRINT "HELLORLD!"
\0003LET I = I + 1
\0004IF I < 10 THEN \0002

An example of a BASIC program which includes this code:

10 GOSUB 1000
20 STOP
1000 COPY HELLO
1010 RETURN

The preprocessed source is saved in @SCR0 to be used as input to the compiler. The original source file is untouched

10 GOSUB 1000
20 STOP
01000 REM $COPY HELLO
01001 LET I = 0
01002 PRINT "HELLORLD!"
01003 LET I = I + 1
01004 IF I < 10 THEN 01002
1010 RETURN

BASIC Language Reference

This was reverse engineered using Ren's excellent disassembler.

Line format

Lines must start with a line number greater than zero and less then 32768 (or less than 32767 if the source file does not include an END statement). Lines must be in sequential order in the source file.

Multiple statements can be included on a source line, separated by :

Comments are indicated by ! which will result in the compiler ignoring the rest of the line, or * which will result in the compiler skipping to the next statement indicated by : or the end of the line.

Types

There are three main types:

  • Integer - 16 bit 2s complement integers.
  • Real - 48 bit fixed point 6 decimal place real numbers.
  • Strings - Any character between pairs of ' or " characters.

Type specifiers

  • % specifies an integer value or variable/function
  • $ specifies a string value or variable/function

The default type for variables and numeric literals is Real.

Variables

Variables are any combination of alphanumeric characters starting with a letter and optionally including a type specifier. The identifier including the type specifier uniquely identifies the variable. Variables can't have the same identifier (excluding type specifier) as a function.

Variables can be declared as an array with the DIM statement. Up to two dimension arrays are permitted. The array size indicates the number of elements which is zero indexed, similar to C style arrays. Dimensioned arrays are determined at compile time so must have fixed sizes. The same identifier (including type specifier) cannot be used for both dimensioned and non-dimensioned variables.

Functions

There are 32 functions available in the language. Not all of them are implemented in the runtime environment.

Function Usage Notes
ABS ABS(arg) Absolute value
ADDR ADDR(arg) Not implemented
ASCII ASCII(arg) The ascii code for the first character of a string
CHR CHR(arg) The character (string) from the ascii code
DATE/DATE$ DATE
END END(arg)
ERR ERR(arg)
EXP EXP(arg) The natural exponential function
FALSE FALSE The logical false value
FILEID FILEID(arg)
INT INT(arg) The integer value of arg
JP JP(arg) See FILEID
KEYX KEYX(arg) Not implemented
LC LC(arg) The lower case representation of arg
LEN LEN(arg) The string length of arg
LOG LOG(arg) The natural logarithm of arg
PEEK PEEK(H$, N$) Find the first occurence of N$ in H$. Zero indexed. Returns -1 if not found
POKE POKE(arg, arg, arg) Not implemented
RECLEN RECLEN
REP REP(arg, arg)
RND RND(arg) Returns a pseudorandom number between 0 and 1. The argument is ignored
SGN SGN(arg) Returns the sign multiplied by one of its argument
SHL SHL(arg, arg) Not implemented
SHR SHR(arg, arg) Not implemented
SSTR SSTR(S$, I%, L%) The substring with length L% starting from position I% (zero indexed)
SSW SSW(S%) Returns TRUE if the sense switch S% is set, FALSE otherwise
STATUS STATUS
TAB TAB(arg) Sets the position of the print buffer to its argument. Returns an empty string
TRACE TRACE(arg) Non zero argument initiates program tracing. Returns the previous state
TRUE TRUE The logical true value
UC UC(S$) The upper case representation of S$
UPSI UPSI(arg)

Expressions

The following operators are available, with their precedence (1 is highest):

Operator Precedence Notes
() 1 Parenthesis
() 1 Array index
() 1 Function application
**, ^ 2 Exponentiation
* 3 Multiplication
/ 3 Division
+ 4 Addition
- 4 Subtraction
// 5 String Concatenation
<=, LE 6 String or numeric comparison
<>, NE 6 String or numeric comparison
<, LT 6 String or numeric comparison
=, EQ 6 String or numeric comparison
>=, GE 6 String or numeric comparison
>, GT 6 String or numeric comparison
NOT 7 Integer bitwise NOT
&, AND 8 Integer bitwise AND
OR 9 Integer bitwise OR
XOR 9 Integer bitwise XOR

The expression syntax in EBNF-like grammar:

expr      = andexpr [ { ( "OR" | "XOR" ) andexpr} ].
andexpr   = notexpr [ { ( "&" | "AND" ) notexpr } ].
notexpr   = [ "NOT" ] compexpr.
compexpr  = catexpr [ ( "<=" | "<>" | "<" | "=" | ">=" | ">" | "EQ" | "GE" | "GT" | "LE" | "LT" | "NE" ) catexpr ].
catexpr   = addexpr [ { "//" addexpr } ].
addexpr   = [ ( "-" | "+" ) ] multexpr [ { ( "+" | "-" ) multexpr } ].
multexpr  = expexpr [ { ( "*" | "/" ) expexpr } ].
expexpr   = priexpr [ { ( "**" | "^" ) priexpr } ].
priexpr   = "(" expr ")"
          | numliteral
          | stringliteral
          | function
          | dimvar
          | ident.
function  = fnident [ "(" expr [ { "," expr } ] ")" ].
dimvar    = dimident "(" expr [ "," expr ] ")".

Notes:

  • Exponentiation has the wrong associativity, it should be right associative (this is consistent with original Dartmouth BASIC).
  • Operands are converted to the correct type if possible at runtime.
  • Unary Plus and Minus when applied to literals are a separate operation. To create a negative numeric literal, then a string representation is required, such as "-1234" which will be converted to a negative number at runtime.

Statements

Statement Usage Notes
CALL
CHAIN
CLOSE
CURSOR
DATA
DECLARE
DEF Define a user defined function
DIM Define a dimensioned variable (array)
DLTKEY
END END Indicates the end of the program source. Any source following the END statement is ignored. The compiler will insert an END statement if none is present in the source
ELSE The negative branch of an IF statement
FILE
FNEND End the definition of a user defined function
FORMAT
FOR The start of a FOR loop
FREE
GETKEY
GOSUB
GOTO
HOLD
IF
INPUT
LET
LINELENGTH
LOCAL
MOVE
NEWKEY
NEXTKEY
NEXT The end of a FOR loop
NOTE
ON
OPEN
OUTPUT
POINT
PRINT
PJP
PUPSI
RANDOMIZE
READ
RECORD
REM
RESTORE
RETURN Return from a subroutine user defined function
REWRITE
STOP Finish execution of the program
WRITE

Files

Function and Statement Reference

Functions

ABS - return the absolute value of its argument

ABS ( expression )

10 INPUT "ENTER A NUMBER (0 TO QUIT): " A
20 PRINT "THE ABSOLUTE VALUE OF " A " IS " ABS(A)
30 IF A <> 0 THEN 10

. RUN XABS
08:53:25 .RUN XABS
ENTER A NUMBER (0 TO QUIT): 5
THE ABSOLUTE VALUE OF 5 IS 5
ENTER A NUMBER (0 TO QUIT): -12.34
THE ABSOLUTE VALUE OF 12.34- IS 12.34
ENTER A NUMBER (0 TO QUIT): 12.34-
THE ABSOLUTE VALUE OF 12.34- IS 12.34
ENTER A NUMBER (0 TO QUIT): 0
THE ABSOLUTE VALUE OF 0 IS 0

DATE - return the system date

DATE

DATE$

There are two versions of DATE, a numeric version which returns a five or six digit representation of the date as MMDDYY and a string version which returns a date string in the format MM/DD/YY

10 PRINT "DATE (NUMBER VERSION) IS " DATE
20 PRINT "DATE (STRING VERSION) IS " DATE$

.run xdate
00:04:52 .RUN XDATE
DATE (NUMBER VERSION) IS 22388
DATE (STRING VERSION) IS 02/23/88

INT - return the integer value of its argument

INT ( expression )

INT returns a real number with the integer value which is the next closest to zero.

10 INPUT "ENTER A NUMBER (0 TO QUIT): " A
20 PRINT "THE INTEGER VALUE OF " A " IS " INT(A)
30 IF A <> 0 THEN 10

.run xint
00:57:05 .RUN XINT
ENTER A NUMBER (0 TO QUIT): 1234.56
THE INTEGER VALUE OF 1234.56 IS 1234
ENTER A NUMBER (0 TO QUIT): -1234.56
THE INTEGER VALUE OF 1234.56- IS 1234-
ENTER A NUMBER (0 TO QUIT): 0
THE INTEGER VALUE OF 0 IS 0

SGN - return the sign of its argument

SGN ( expression )

If expression is less than zero, return -1
If expression is greater the zero, return 1
If expression is equal to zero, return 0

Bugs

If the real value 0.000000 is used as the argument the result is incorrect.

10 INPUT "ENTER A NUMBER (0 TO QUIT): " A
20 PRINT "THE SIGN OF " A " IS " SGN(A)
30 IF A <> 0 THEN 10

. run xsgn
19: 12:07 .RUN XSGN
ENTER A NUMBER (O TO QUIT): 5
THE SIGN OF 5 IS 1
ENTER A NUMBER (0 TO QUIT): -12.34
THE SIGN OF 12.34- IS 1-
ENTER A NUMBER (0 TO QUIT): 12.34-
THE SIGN OF 12.34- IS 1-
ENTER A NUMBER (0 TO QUIT): 0
THE SIGN OF 0 IS 32256-

Statements

DEF - Define a user defined function

DEF allows the user to define their own functions. There are two forms: a single line form where the function is calculated from an expression combining its arguments and a multi-line form where the function can be composed from multiple statements.

Functions must be defined earlier in the source than their first use.

Recursion is not permitted.

RETURN can be used for an early return of the function, returning the value of the function variable at that point.

DEF can be used to define subroutines (i.e., functions which do not return any values). Because the value of the function variable is always returned, subroutines can be invoked using CALL which will ignore any function result.

DEF identifier [(identifier {[, identifier]})]
statements
FNEND

DEF identifier [ ( identifier [ {, identifier } ] ) ] = expression

10 DEF E=EXP(1)
20 PRINT "THE MATHEMATICAL CONSTANT E IS EQUAL TO " E

.run xdef
07:44:56 .RUN XDEF
THE MATHEMATICAL CONSTANT E IS EQUAL TO 2.718282
10 DEF E
20 LET E=EXP(1)
30 FNEND
40 PRINT "THE MATHEMATICAL CONSTANT E IS EQUAL TO " E

.run xdef
08:06:32 .RUN XDEF
THE MATHEMATICAL CONSTANT E IS EQUAL TO 2.718282

FOR - define a loop

FOR identifier = expression TO expression [ STEP expression] [WHILE expression]
statements
NEXT identifier

Bugs

Use of WHILE is valid syntax although the bytecode is incorrect and will result in runtime errors. Possibly because the programmer made a typo and wrote 13 instead of 23.

IF - conditional program logic

⚠️ **GitHub.com Fallback** ⚠️