The manual talks about it being a compiler, but I'm not sure I see significant differences between its I-code and tokenized BASICs in general.
Well, so called 'tokenized' BASIC isn't really tokenized, but rather put in a shorthand. Tokenization does require to put everything into token format, which isn't true for average BASICs. The term 'crunched' used by Microsoft is far more appropriated here (*1).
BASIC09 did not only turn recognized keywords into single byte identifiers, keeping the rest (constants/variable names) as is, but puts everything into tokens, which is a virtual machine instruction (*2) as well.
A variable reference is a token of its type (byte/integer/float/string/complex), followed by the address of its value. Similar constants, but with the value following. So unlike 'standard' basics, the interpreter doesn't has to figure out every time what it's about - no matter how lightweight - but simply executes the token.
So far this might look like some sophisticate cruncher, but all operators are as well available in typed version. For example there are
- two different
-
instructions for byte/integer and float
- three different
+
instructions for byte/integer, float and strings
- four different
=
instructions for byte/integer, float, strings and bool
- four different
<>
instructions for byte/integer, float, strings and bool
and so on. Depending on operand types used in an expression BASIC09 will compile the appropriate instruction. This extends to whole BASIC instructions aswell, as there are for example two NEXT
instructions for the VM, one that accepts an integer coutner variable and one for float. The later ofc slower.
Next, all expressions are turned into RPN. For example an IF statement will look like this:
(Made arbitrary complex on purpose)
IF MID$(Input$,Pos,1)="\" AND Found=0 THEN
is compiled to
- <IF>
- <String-Var> address of Input$
- <Integer-Var> address of Pos
- <Byte-Const> value 1
- <MID$>
- <String-Const> value "\"
- <String Comparison Operator '='>
- <Integer-Var> address of Found
- <Byte-Const> value 0
- <Integer/Byte Comparison Operator '='>
- <OR>
- <Invisible Goto> address of ELSE/ENDIF
- <THEN>
- <End of Line>
It's quite clear to see that a stack machine is operating along the instruction stream of an expression.
<End of Line> is rather syntactic sugar to insert automatic line breaks (and indentation) when listed, as BASIC09 prefers. Similar all expressions will be 'normalized' and alternate spellings like ><
put into their normal form <>
.
While I-Code structure is (naturally) close to BASIC, it does as well show differences. For example the TO
and STEP
parts of a FOR
-NEXT
are (together with the repeated variable reference) compiled to the NEXT
instruction, saving the need to lookup what to examine for end condition and step width. In addition the SEP value will be always compiled as part of the NEXT instruction. As result the FOR statement will only be executed for initialisation. all following iterations will direct jump to the next instruction after FOR
(*2).
And so on.
Long story short:
I-CODE is not simply crunched (tokenized) BASIC, but consists of instructions toward a virtual machine, which just happens to be very BASIC compatible.
I know you can PACK it, but that seems more like a symbol stripper.
Yes, it that's what PACK
is for. It removes all remaining source reference, like variable names, to save space. In addition it more aggressive optimizes expressions than the interactive interpreter does. After packing a program can only be executed - usually by RUNB
.
Keep in mind, OS/9 is a RAM based system and so is BASIC09. BASIC09
(the program) is loaded into memory, thus reducing the RAM available to the user program. RUNB
is essentially just the virtual machine (and BASIC runtime). By replacing BASICC09
with the much smaller RUNB
more RAM was available by essentially dropping the editor, compiler and debugger parts (*3). IIRC BASIC09
was more than 20 KiB, while RUNB
was about half of that. so 10+ KiB more space for a user program is quite a lot.
*1 - Yes, BASIC's come in many colours, including some that normalize way more than MS did.
*2 - This is not done for WHILE
loops, as they are rejecting structures, unlike FOR
which iterates at least once.
*3 - Well, and even more after packing.