Background (link)
The first generation Wang 2200 CPU ran the dialect of BASIC known as "Wang BASIC." The CPU went through a few revisions of a relatively minor nature: repackaging as technology advanced, adding more microcode to support additional features. None of those changes affected performance.
While the Wang BASIC was advancing, the design team started a new CPU project from scratch in 1974(?). While the influence of the first microarchitecture is evident in the second, the changes were large and required a complete rewrite of the BASIC interpreter. This new BASIC dialect was called BASIC-2.
Although backward compatibility was a major goal, the designers of BASIC-2 took the opportunity to extend the syntax and power of the language.
This page doesn't try to cover every detail of BASIC-2; it tries to highlight some of the interesting and significant improvements made to the BASIC dialect. It will make the most sense to those who only know Wang BASIC. Those who know BASIC-2 will probably be amazed that Wang BASIC programmers lived without some of these features.
Still the Same (link)
BASIC-2 was nearly a strict superset of Wang BASIC; only a handful of language features were dropped, and the vast majority of Wang BASIC programs ran under BASIC-2 unchanged or with only a minor edit.
Fundamental features of Wang BASIC that didn't change for BASIC-2 included:
- data was statically allocated during the resolve phase
- there was almost no concept of variable scoping; every variable was global
- variable names were limited to a single letter or a letter-number pair
Better Performance (link)
BASIC-2 running on the VP CPU was also dramatically faster, typically about eight times faster. Some of it was due to the shorter microinstruction cycle time, some due to the more powerful instruction set, and the rest due to a shift in priorities to write the interpreter to be fast instead of small.
More Robust Implementation (link)
As noted on the Stupid Tricks page, Wang BASIC had a number of design flaws.
BASIC-2 was much more robust, for a few reasons. One reason was that the VP CPU used static RAM for holding microcode instead of ROMs. This meant that as bugs were discovered, they would be routinely fixed in the next release of the operating system, which entailed mailing out floppy disks, instead of having to upgrade as many as 20 ROMs.
Another reason was simply that the designers of BASIC-2 thought very hard about making it difficult for an end-user to peek into the innards of the implementation. For instance, in Wang BASIC, when a line was recalled for editing, the user was editing the tokenized representation of the line. This allowed injecting bogus token values into the edit buffer. In contrast, BASIC-2 detokenized the line before editing, then retokenized after editing.
As another example of the more robust design, more care was taken to
preserve accuracy in implementing the fundamental computations, namely add,
subtract, multiply, divide, transcendentals, and even RND()
.
Not only was RND()
almost 100 times faster in BASIC-2,
it was more random!
Raising the Existing Limits (link)
The simplest kind of improvement that BASIC-2 made was to improve a few of the limits that were part of Wang BASIC operation.
In Wang BASIC, the maximum array dimension was 255. In BASIC-2, 2D arrays retained this limit, but 1D arrays could have a maximum dimension of 65535. Practically speaking, memory capacity would have been exhausted before this limit was reached.
String variables are declared to have a fixed allocation size. BASIC-2 raises the limit per string from 64 bytes to 124. This is important as screens were commonly 80 characters wide.
Generalization of Existing Features (link)
In a number of commands and functions, BASIC-2 took an existing Wang BASIC feature and added to the syntax to allow for more functionality. Some of these features were common in other BASIC dialects, but many were unique to BASIC-2. Below are some examples.
Wang BASIC's
IF ... THEN
statement only allowed branching to some line if the condition was true. BASIC-2 generalized this, like many other BASIC dialects, to allow most statements to be executed.Wang BASIC:
100 IF A<=0 THEN 110:S=S+A 110 ...
BASIC-2:
100 IF A>0 THEN S=S+A 110 ...
BASIC-2 added the
ELSE
clause to theIF
statement, common to most BASICs.Wang BASIC:
100 IF A<=0 THEN 105:S=S+A:GOTO 110 105 A=A+1 110 ...
BASIC-2:
100 IF A<=0 THEN A=A+1:ELSE S=S+A 110 ...
BASIC-2 added the
ELSE
clause to theON ... GOTO
andON ... GOSUB
commands as well.100 INPUT "(R)ead, (W)rite, (T)est, or (Q)uit", A$ 110 ON POS("RWTQ"=A$) GOTO 100,200,300,400:ELSE PRINT "ERROR":GOTO 100
The
IF
was improved by allowing conjunctive conditionals. Conditionals were simply evaluated left to right.Wang BASIC:
100 INPUT "Enter three numbers",A,B,C 110 IF A>=B THEN 120:IF B>=C THEN 120:PRINT "Ascending" 120 ...
BASIC-2:
100 INPUT "Enter three numbers",A,B,C 110 IF A<B AND B<C THEN PRINT "Ascending"
In the case of nested
FOR
loops, BASIC-2 allowed specifying the nested completion of the loops in a single statement.Wang BASIC:
100 FOR I=1 TO 10:FOR J=1 TO 10:PRINT 10*I+J;:NEXT J:NEXT I
BASIC-2:
100 FOR I=1 TO 10:FOR J=1 TO 10:PRINT 10*I+J;:NEXT J,I
PRINT
received new pseudo functions, in addition toTAB()
:AT(X,Y)
- move cursor to screen location X,Y
BOX(W,H)
- draw a box of a given size at a current screen location
HEXOF(<string>)
- like
HEXPRINT
, but as a function instead of a command
The syntax for assigning to string variables was considerably enhanced. Wang BASIC starting with the 2200B CPU added commands for performing boolean (bitwise) operations on a pair of alphanumeric variables. BASIC-2 turned these into a functional form, and added the
ALL()
function as well.BASIC-2 also added a string concatenation operator,
&
, only in the context of string assignment.Here are some examples of this flexible syntax.
Wang BASIC:
10 INPUT "What is your last name", N$ 20 A$="Mr.":STR(A$,5)=N$:STR(A$,LEN(A$)+1)=", I presume" 30 A$=B$ : AND(A$,C$) 40 AND(A$,B$) 50 AND(A$,B$) : OR(A$,F0) 60 A$=B$ : ADDC(A$,C$) 70 INIT(".") A$
BASIC-2:
10 INPUT "What is your last name", N$ 20 A$ = "Mr. " & N$ & ", I presume" 30 A$ = B$ AND C$ 40 A$ = AND B$ : REM like A$ = A$ AND B$ 50 A$ = AND B$ OR ALL(F0) 60 A$ = B$ ADDC C$ 70 A$ = ALL(".")
PRINTUSING
was generalized to accept an image string, not just an image line reference, eg:Wang BASIC:
10 % #.########## 20 PRINTUSING 10, 1/3
BASIC-2:
10 % #.########## 20 PRINTUSING 10, 1/3 30 PRINTUSING "#.##########", 1/3 40 A$="#.##########" 50 PRINTUSING A$, 1/3
The output of
PRINTUSING
could be captured by an alpha variable, although there were some twists to using it. The first two bytes of the receiving var held a count of the bytes received. EachPRINTUSING TO
updated this count such that a series ofPRINTUSING TO
's could be glued together.10 A$=ALL(00):REM initialize byte count 20 PRINTUSING TO A$, "#.##", SQR(3) 30 PRINTUSING TO A$, "#.##", SQR(5) 40 PRINT STR(A$,3,VAL(A$,2)) :RUN 1.73 2.23
It was often very inconvenient to work with binary data that was larger than 64 bytes due to the fact that Wang BASIC allowed a maximum of 64 bytes per string. BASIC-2 relaxed this somewhat to the seemingly (but with good reason) bizarre 124 bytes. That was an improvement when dealing with strings of that didn't need to be wider than the 80 column CRT screen, but it was of little help to programs which manipulated larger binary strings.
To get around this 124 byte limit, BASIC-2 added a notation which allows a string array to be interpreted as a single large contiguous string. This notation was usable in most contexts that accepted a scalar string.
10 DIM A$(50)50:REM 2500 bytes ... 100 STR(A$(),5,50) = STR(A$(),1000,50) : REM move 50 bytes 110 A$()=ALL(" ") 120 A$(2)="Test" 120 A$(3)="..+.." 130 PRINT LEN(A$()):REM produces 54 140 PRINT POS(A$()="+"):REM produces 103
BASIC-2 generalized both
BIN(A)
and its inverse,VAL(A$)
, to optionally take a second parameter, which specifies that these convert to and from a 16 bit number, instead of just 8 bits.10 A$=HEX(1234) 20 PRINT VAL(A$) : REM produces 18 30 PRINT VAL(A$,2) : REM produces 4660
In Wang BASIC,
RENUMBER
could change the line numbers of a block of lines, but the relative order of all lines was not affected. BASIC-2 generalized this to allow renumbering a block of lines such that they moved into an empty range of line numbers.The
LIST
command had quite a few additionsLIST D
- "decompressed" - one statement printed per line
LIST #
- line number cross reference
LIST V
- variable cross reference
LIST '
- DEFFN' cross reference
LIST T "text"
- find lines containing specified text
LIST DT
- list device tables
LIST I
- list interrupt table
In the Wang BASIC dialect,
RESTORE 20
restored theREAD
data pointer to the 20thDATA
element found in the program.BASIC-2 added the new syntax
RESTORE LINE 20
to restore theREAD
pointer to the firstDATA
element of line 20.The existing
RETURN CLEAR
command clears one level of subroutine nesting. BASIC-2 extended the syntax to allow the statementRESTORE CLEAR ALL
, which clears the stack of all outstanding subroutines.The
ROTATE
command could rotate 1-7 bits of each byte of a string. The new syntaxROTATEC
was likeROTATE
, but bits carried between bytes of string, such that the rotation was over the entire length of the string.The General I/O command,
$GIO
added new commands, including branching ops.
New Functions (link)
BASIC-2 added some new functions, as briefly described here.
FIX(v)
- Integer part of the expression
- Same as SGN(v)*INT(ABS(v))
MOD(v1,v2)
- Finds the remainder of v1/v2
ROUND(v1,v2)
- Rounds v1 to the v2'th decimal place; v2 can be positive or negative
MAX(v1[,v2...])
- Finds the maximum value among the specified expressions or numeric array(s)
MIN(v1[,v2...])
- Finds the minimum value among the specified expressions or numeric array(s)
LGT(v)
- Finds log base 10 of the expression
VER(A$,F$)
- verifies that a string matches a given format
Clarifying a bit, MAX()
and MIN()
took a variable
number of arguments, including array references. For example,
100 M = MAX(0, A())
finds the largest number in the array
A()
, but if that number is negative, zero is returned instead.
New Commands (link)
BASIC-2 added some entirely new commands to the language, in addition to the improvements already noted to existing commands. Many other commands were added to BASIC-2 once the MVP OS was developed, but won't be listed here.
The normal
INPUT
statement scanned the user's response, removing spaces and breaking fields at commas. This meant that in general usingINPUT
to accept a literal string from a user wouldn't be reliable, as they might have entered something with leading spaces or commands.The answer was the
LINPUT
(line input) statement. Whatever the user typed was exactly what was received by the argument variable.10 LINPUT "Your answer? ", A$ 20 PRINT "OK"; A$ :RUN Your answer? *This, that OK,This, that
The
$PACK
and$UNPACK
commands took a character string that encoded the format of how data was packed into the receiving variable. Manipulating these codes was error prone, so BASIC-2 added the$FORMAT
command to make building these format strings more self-documenting and easier to create.10 REM 3 digit Integer, packed 8.2 number, 10 byte string 20 $FORMAT F$=I3, P8.2, A10 30 $PACK (F=F$) B$() FROM X,Y,A$
The new BASIC-2 commands
HEXPACK
and its inverse,HEXUNPACK
, convert to and from ASCII hex strings and packed binary data.10 DIM A$2,B$1 20 A$="43" 30 HEXPACK B$ FROM A$ 40 HEXPRINT B$:REM produces "43" 50 B$="T" 60 HEXUNPACK B$ TO A$ 70 PRINT A$:REM produces "54"
SELECT
was already the swiss-army knife of Wang BASIC. BASIC-2 added a few more blades.SELECT ERROR
- Allows suppressing errors below a given threshold
SELECT NO ROUND
- Disables rounding of arithmetic results
SELECT ROUND
- Enables rounding of arithmetic results
SELECT LINE
- Specify how many lines of text are on an output device
ON <expr> SELECT ...
- Allows performing one of a set of
SELECT
statements with a single command SELECT ON/<addr> GOSUB <line number>
- Programmable interrupt feature
SELECT OFF
- Programmable interrupt feature
The
ON ... SELECT
syntax is quite useful, especially when there were many more I/O choices, such as allowing the user to select a disk drive address.Wang BASIC:
10 INPUT "Output to Screen or Printer",A$ 20 ON POS("SP"=STR(A$,1,1)) GOTO 30,40:GOTO 10 30 SELECT PRINT/005(80):GOTO 50 40 SELECT PRINT/215(132) 50 ...
BASIC-2:
10 INPUT "Output to Screen or Printer",A$ 20 ON POS("SP"=STR(A$,1,1)) SELECT PRINT/005(80);PRINT/215(132):ELSE GOTO 10
An entirely new concept was added for BASIC-2, namely an interrupt driven model under program control. The
SELECT ON .. GOSUB ...
and other variations were used to trigger a call to a subroutine when a given I/O device signalled it was ready for work. Read Chapter 8 of the BASIC-2 manual for the details, but here is a simple example.10 SELECT ON/001 GOSUB 100 20 D=0 30 IF D=0 THEN 30:A=A+1:PRINT A;:GOTO 30 ... 100 REM keyboard interrupt routine 110 KEYIN A$ 120 IF A$="G" THEN D=1:REM GO 130 IF A$="S" THEN D=0:REM STOP 140 SELECT ON/001:REM re-enable interrupt 150 RETURN
When the program is run, nothing appears to happen. The program spends all of its time executing line 30, inspecting the variable
D
. When the user types a key, the code starting at line 100 is called. If the user has typed a "G",D=1
and line 30 starts filling the display with incrementing numbers. When the user types "S",D=0
and the numbers stop appearing.
Multiuser Operation (link)
The initial releases of BASIC-2 were for the 2200 VP CPU. The VP OS supported a single user, just like the first generation machines. With the introduction of the 2200 MVP CPU, the OS was reworked to perform time sharing between multiple "partitions." Once a partition was selected to run, it would occupy the CPU until either it blocked waiting for some I/O device, or a 30 millisecond timer expired. It would then be put back on the task list, and the next task would be started, in round-robin order.
A partition was a contiguous chunk of memory which held a BASIC context: a program and its associated data. Although the simplest scenario maps each terminal to one partition, it was possible for a single terminal to control multiple partitions, with one in the foreground and one or more in the background. Background partitions would timeslice along with all the other partitions until they blocked trying to access the terminal I/O device. At the point the blocked partition was moved to the foreground status, it would spring back to life.
The details of defining and managing partitions were complicated, such as the minimum and maximum size of a partition, how the partitions mapped into 64 KB "banks" of memory, and how partitions were mapped to terminals. Further complicating things, an 8 KB section of RAM, a "global partition", could be made accessible to other partitions, again with various restrictions. This global code and data could shared between cooperating partitions.
Many commands were introduced into BASIC-2 to manage partitions and resource sharing. Some existing commands that did not play well with resource sharing and time slicing either were modified or had constraints placed on their use. Some commands to manage concurrency and resource sharing.
To get the full story, see Chapter 16 of the BASIC-2 manual.
Differences Between Versions (link)
For the most part, BASIC-2 retained just about all the syntax and functionality of Wang BASIC. See Appendix C of the BASIC-2 manual for a detailed exposition. Here, then, are just a few key differences.
The I/O commands specific to controlling TTY devices (punches), card readers, and cassette tapes were dropped in BASIC-2.
There was simply insufficient demand to justify rewriting that part of the BASIC interpreter.
Wang BASIC had a means of trapping most errors, using the
ON ERROR A$,N$ GOTO 1000
syntax. BASIC-2 performs error control using a few different bits of syntax.SELECT ERROR > nn
- sets threshold of errors to ignore
ERR
- returns error code of the most recent error condition
ERROR statement
- If an error occurs in the execution of a statement, but
the very next statement is an
ERROR
statement, the normal error processing is ignored and the statements following theERROR
are performed
Numeric results could be different in the least significant digit.
The pseudo-random sequence returned by
RND()
was different.