From: RWilson@acorn.co.uk
Newsgroups: comp.sys.acorn
Subject: BASIC V in 3.10
Date: 10 Sep 92 16:28:16 GMT

ARM BBC BASIC V: changes between 1.02 and 1.04

(1) OVERLAY

OVERLAY allows a single set of programs (referred to by a pre-initialised
string array) to be loaded into common memory. The LVAR command lists the
first line of libraries or installed libraries; the order of listing is also
the order of searching - which is simply (a) search current program (b)
search any LIBRARYs, last mentioned first (the order shown by LVAR) (c)
search any INSTALLed libraries, again last mentioned first (d) search the
OVERLAY library, starting with the current overlay in core (if there is one)
and then linearly from the start of the OVERLAY array.

The OVERLAY statement takes a string array and allocates a single block of
store large enough to hold any single one of the programs: at this stage no
program is actually loaded. The programs referred to in the string array will
be searched for procedure and functions and, once this has been found, the
interpreter will load in the right overlay in order to execute it. One cannot
call items in other overlays from an overlay library (even implicitly via the
main program or other libraries). The search time can be reduced by setting
the approriate element of the array to a null string when that library no
longer needs to be consulted. It is important not to confuse the interpreter
by changing the names in the string array! Another OVERLAY statement will
work (after forgetting all the referenced procedures) but the store used by
the first one will be lost: however, one can add new strings to the existing
array provided the programs referred to are no larger than the largest one
given to the OVERLAY statement.

DIM A$(10)
A$(1)="adfs:&.Assistant":A$(2)="net:$.Matrices":A$(3)="net:$.PlotLib"
OVERLAY A$()

(2) Save and restore DATA pointer (onto the stack)

Since DATA statements can be used in procedure libraries (by using RESTORE
+), transparent use of the DATA pointer is also provided. The current value
can be saved onto the stack just like the error status.

LOCAL DATA (may be used anywhere, unlike LOCAL A etc)
   Makes the use of DATA local; i.e. saves the DATA pointer on the stack.

RESTORE DATA
   Restore DATA pointer from the stack. An error will occur if the next
thing on the stack is not a DATA pointer.

Returning from a function or procedure will restore any DATA pointer it
finds on the stack. LOCAL DATA must be the last thing to be made local in a
procedure or function (apart from LOCAL ERROR).

REM "safe" routine for reading in an array
DEF PROCRead(A())
REM going to use local DATA so save status
LOCAL DATA
REM set pointer
RESTORE +0
DATA 1,2,3,4,5,6,7,&99
REM do the job!
ENDPROC
REM end of procedure will restore error status

(3) Array initialisation

A$()=<factor>,<expression>,<expression> ..
A%()=<factor>,<expression>,<expression> ..
A()=<factor>,<expression>,<expression> ..

Array initialisation can be done with all elements the same (A()=1) or with
element by element initialisation (A()=1,2,3,4,5,6 etc.). The element by
element form will leave any unspecified values alone. The order of values
given in the list is "last subscript changes quickest" i.e. for DIM A(2,1),
they would be A(0,0); A(0,1); A(1,0); A(1,1); A(2,0); A(2,1) in order.

(4) SUMLEN

SUMLEN which returns the sum of the lengths of all the strings in a string
array.

(5) MOD

MOD which returns the modulus (square root of the sum of the squares of each
element) of a numeric array.

Example of (3), (4) and (5):

transformation()=1,0,0,0,1,0,0,0,1
coordinate()=coordinate().transformation()
PRINT"There are ";SUMLEN(A$())" characters."
normalisedvector()=vector()/MODvector()

(6) ERROR EXT

ERROR EXT <number>,<string> passes an error to the caller's error handler

(7) QUIT

QUIT function: TRUE if BASIC has been called with -quit on command line

(8) Store allocation

When a string is stored, the current space is immediately used if the number
of words is compatible; otherwise the space is deallocated: it is stored on
the appropriate member of an array of free lists, each list having one size
in words. This makes allocation of the new space for a string very quick:
check the free list corresponding to the desired length, if there is an
entry, take it; otherwise use new space. Use of contiguous store has been
disabled in 1.03 since it gives rise to some strange side effects: programs
running on 1.03 may use fractionally more store than on 1.02, but generally
long term use of strings will result in less used memory.

(9) Revised error descriptions

 0,"Silly!"
 0,"No room to do this renumber"
 0,"Line numbers larger than 65279 would be generated by this renumber"
 0,"No room"
 0,"Line too long"
 0,"Stopped"
 0,"Invalid LISTO option"
 0,"Invalid TWINO option"
 0,"Corruption of stack"
 0,"Error control status not found on stack for RESTORE ERROR"
 0,"Missing incore name"
 0,"LIST/TWIN found line number reference"
 0,"HELP has no information on this keyword"
 0,"Incorrect in-core file description"
 1,"No such mnemonic"
 1,"No such suffix on EQU"
 2,"Bad immediate constant"
 2,"Bad address offset"
 2,"Assembler limit reached"
 2,"Bad shift"
 3,"Bad register"
 3,"Duplicate register in multiply"
 4,"Missing ="
 4,"Missing = in FOR statement"
 4,"Mistake"
 5,"Missing ,"
 6,"Type mismatch: number needed"
 6,"Type mismatch: numeric variable needed"
 6,"Type mismatch: numeric array needed"
 6,"Type mismatch: string needed"
 6,"Type mismatch: string variable needed"
 6,"Type mismatch: string array needed"
 6,"Type mismatch: array needed"
 6,"Type mismatch between arrays"
 6,"Can't assign to array of this size"
 6,"Array type mismatch as parameter"
 6,"Can't SWAP arrays of different types"
 7,"Not in a function"
 8,"Too low a value for $<number>"
 9,"Missing """
 10,"DIM() function needs an array"
 10,"No room to do matrix multiply with source(s) the same as destination"
 10,"Impossible dimension"
 10,"No end of dimension list )"
 10,"Bad DIM statement"
 10,"Can't DIM negative amount"
 10,"Arrays cannot be redimensioned"
 11,"No room for this DIM"
 11,"No room for this dimension"
 11,"Attempt to allocate insufficient memory"
 12,"Items can only be made local in a function or procedure"
 13,"Not in a procedure"
 14,"Reference array incorrect"
 14,"Unknown array"
 14,"Unknown array in DIM() function"
 14,"Undimensioned array"
 15,"Subscript out of range"
 15,"Incorrect number of subscripts"
 16,"Syntax error"
 17,"Escape"
 18,"Division by zero"
 19,"String too long"
 20,"Number too big"
 20,"Number too big for arc Sine or arc Cosine"
 21,"Negative root"
 22,"Logarithm range"
 23,"Accuracy lost in Sine/Cosine/Tangent"
 24,"Exponent range"
 26,"Unknown or missing variable"
 26,"Can't use array reference here"
 27,"Missing )"
 27,"Missing ("
 27,"Missing ]"
 27,"Missing {"
 27,"Missing }"
 28,"Bad Hex"
 28,"Hex number too large"
 28,"Bad Binary"
 29,"No such function/procedure"
 30,"Bad call of function/procedure"
 31,"Arguments of function/procedure incorrect"
 31,"Invalid RETURN actual parameter"
 31,"Invalid array actual parameter"
 32,"Not in a FOR loop"
 33,"Can't match FOR"
 34,"Bad FOR control variable"
 35,"The step cannot be zero"
 36,"Missing TO"
 37,"No room for function/procedure call"
 38,"Not in a subroutine"
 39,"ON syntax"
 40,"ON range"
 41,"No such line"
 42,"Out of data"
 42,"DATA pointer not found on stack for RESTORE DATA"
 43,"Not in a REPEAT loop"
 44,"Too many nested structures"
 45,"Missing #"
 46,"Not in a WHILE loop"
 47,"Missing ENDCASE"
 48,"OF missing from CASE statement"
 48,"CASE..OF statement must be the last thing on a line"
 49,"Missing ENDIF"
 50,"Bad MOUSE variable"
 51,"Too many input expressions for SYS"
 51,"Too many output variables for SYS"
 52,"Can't install library"
 52,"Bad program used as function/procedure library"
 52,"No room for library"

(10) New assembler feature

Bit 3 in OPT (opts 8-15) controls an assembler area limit check: the
assembler will check the variable L% to decide if it can deposit information
at the current address (P% or O% depending on OPT as usual) (Note that DIM
P% 1,L% -1 will assign L% a word address (which DIM likes returning) and so
can take a full word of assembly).

(11) More exported routines from CALL

The value in r14 can be returned to with MOV PC,R14 but it also points to an
array of useful values:

 B CALL2REAL ;0th entry in table is return address
;the following values are words containing an offset from ARGP (R8)
;word aligned 256 bytes
 & STRACC ;string accumulator
;word aligned words offset from ARGP
 & PAGE ;current program PAGE
 & TOP ;current program TOP
 & LOMEM ;current variable start
 & HIMEM ;current stack end
 & MEMLIMIT ;limit of available memory
 & FSA ;free space start (high water mark/FD stack limit)
 & TALLY ;value of COUNT
 & TIMEOF ;offset from TIME readable by OSWORD
 & ESCWORD ;exception flag word (contains escflg, trcflg)
 & WIDTHLOC ;value of WIDTH-1
;internal BASIC routines
 B VARIND ;get value of lv
 B STOREA ;store value into lv
 B STSTORE ;store string into type 128 strings
 B LVBLNK ;convert string "variable name" to lv address and type
 B CREATE ;create new variable
 B EXPR ;use expression analyser on string
 B MATCH ;lexical analyse source string to destination string
 B TOKENADDR ;pointer to string for particular token
 & 0 ;stop point for ****** Minerva programs
;new on BASIC V 1.03
 & 9 ;length of extensions
 B FSTA ;store fp in r0-r3 as 5 bytes at r9
 B FLDA :load fp from r9 as 5 bytes into r0-r3
 B FADD ;add fp in r0-r3 to 5 bytes at r9, result in r0-r3
 B FSUB ;subtract fp in r0-r3 from 5 bytes at r9, result in r0-r3
 B FMUL ;multiply fp in r0-r3 by 5 bytes at r9, result in r0-r3
 B FDIV ;divide fp in 5 bytes at r9 by r0-r3, result in r0-r3
 B FLOAT ;float an integer in r0 to an fp in r0-r3
 B FIX ;fix an fp in r0-r3 to an integer in r0
 B FSQRT ;square root of r0-r3

Note that the list is held in two different forms: the first section is
exactly like BASIC 1.02: a list terminated by zero. The list could not be
simply extended because of some unfortunate assumptions bound into poorly
written application programs, so a second list has been tacked on the end.
A client can check the number of extensions field (i.e. the field after the
zero) and it will be 0 or negative if there aren't any.

The pointer TIMEOF is particularly interesting. On Arthur this location
holds a meaningless value; on other systems it holds the offset which should
be subtracted from the time read from the system if one wishes to agree
with the value given by PRINT TIME. However the next three words offset from
the argument register after TIMEOF (i.e. [R8,TIMEOF+4] etc.) contain:

LOCALARLIST: a pointer to a list of local arrays.
INSTALLLIST: a pointer to the list of installed libraries.
LIBRARYLIST: a pointer to the list of libraries.

The local array list is not going to be much use, but the ability to scan the
libraries allows (say) find commands to be installed which can find a
procedure whereever it is. The list consists of a pointer (0 is the end of
the list) to a word (which is the next pointer) and a BASIC program in
internal form immediately following the word. The list is organised in the
search order.

The internal routines are only guarenteed to work in processor user mode.
The following functions are provided:

VARIND: entry with r0=address of lv, r9=type of lv, r12=LINE.
Returns with r0..r3 as the value, r9 the type of the value as follows:

r9         type    where
0          string  in STRACC, r2 points to end (r2-STRACC is length)
&40000000  integer in r0
&80000000  float   in r0..r3

Uses no other registers (including stack). Possible error if asked to take
value of an array fred(): will need r12 valid for this error to be reported
correctly.

STOREA: entry with r0..r3 value, r9=type of value and r4=address of lv,
r5=type of lv, r8=ARGP, r12=LINE (for errors) and r13=SP (for out of store
chcek on string allocation). Will convert between various formats e.g.
integer and float or produce an error if conversion is impossible.
Returns with r0..r7 destroyed. Stack not used.

STSTORE: entry with r4=address of lv, r2=length (address of end), r3=address
of start, r8=ARGP, r12=LINE (for out of store error) and r13=SP (for out of
store check). The string must start on a word boundary, the length must be
255 or less.
Uses r0, r1, r5, r6, r7. Preserves input registers. Stack not used.

LVBLNK: entry with r11 pointing to start of string, r8=ARGP, r12=LINE (many
errors possible e.g. subscript error in array) and r13=stack (will be used
for evaluation of subscript list: calls EXPR). The string will be processed
to read one variable name and provide an address and type which can be given
to VARIND.
Returns with NE status if a variable has been found. Address in r0, type (see
above) in r9. If there is an EQ status then if the carry is set it cannot
possibly be a variable else if the carry is clear it could be, but isn't
known to the interpreter (and registers are set to values for CREATE).
Uses all registers.

CREATE: create a variable. Input is the failure of LVBLNK to find something.
Thus we have r10=first char of item, r3=second char of item or 0, r4 and r11
pointers to start and end of other chars, r8=ARGP, r12=LINE, r13=STACK, r9
contains the number of zero bytes on the end. It is recommended that CREATE
is called immediately after a failed LVBLNK only.
Uses all registers. Return parameters as LVBLNK.

The LVBLNK and CREATE routines can be combined together to provide a routine
which checks for a variable to assign to and creates it if necessary:

SAFELV STMFD SP!,{R14}
       BL LVBLNK
       LDMNEFD SP!,{PC}
       LDMCSFD SP!,{PC}
       BL CREATE
       LDMFD SP!,{PC}

EXPR: entry with r11 pointing to start of string, r8=ARGP, r12=LINE, r13=STACK.
EXPR stops after reading one expression (like those in the PRINT statement).
Uses all registers. The value is returned like VARIND. If status EQ it read a
string, if status NE and plus it read an integer word (in r0) if status NE
and minus it read a floating point value (in r0..r3). r9 contains the type:
the status can be recreated by TEQ r9,#0. r10 contains the delimiting
character, r11 points to the one after.

MATCH: entry with r1=source string (terminated by ASCII CR=13),
r2=destination string, r3=MODE, r4=CONSTA, r13=STACK. Note that MATCH does
not need ARGP or LINE. The MODE value is 0 for LEFT MODE (i.e. before an
equals sign) and 1 for RIGHT MODE (after an equals sign or in an expression).
The CONSTA value is 0 for don't pack constants using token &8D, &8D itself
for pack. Both MODE and CONSTA will be updated during use of the routine
e.g. GOTO will change CONSTA to &8D to read the constant, PRINT will change
MODE to 1 to read the expression. Starting values of MODE=0 and CONSTA=0
will lexcially analyse a statement; MODE=1 and CONSTA=0 an expression;
MODE=0 and CONSTA=&8D is used to extract line numbers in command mode and
probably has little use. MODE affects the values assigned to tokens for
HIMEM etc.
Uses r0-r5. r1 and r2 are left pointing after the CR codes in the strings.
r5 contains status about failures to analyse the line correctly: it can be
used or disregarded: values >= &1000 imply mismatched brackets, bit 8 set
implies a line number was found that was too large to be put into a &8D
constant and if r5 AND 255 equals 1 it implies mismatched string quotes.

TOKENADDR: entry with r0 as the token value, r12=pointer to next byte of
token string. The value in r12 is only used when the address of a two byte
token is required. No other register are used or required.
Returns r1 as the pointer to the first character of a string, terminated by a
value >=&7F (which is the first byte of the token value). r0 is set to the
address of the start of the token table itself. r12 will have been
incremented by 1 if a two byte token was used.

FSTA: Saves the five byte form of a floating point number. Entry with r0-r3
as the floating point value, r9=address of destination. Stack not used. No
error possible. r2 altered (but this does not affect the fp value of r0-r3).

FLDA: Loads the five byte form of a floating point number. Entry with address
in r9. Exit with r0-r3 as floating point value. Stack not used. No error
possible.

FADD: Add floating point value in r0-r3 to five byte form at r9. Result in
r0-r3. Stack not used. Overflow error possible. Uses r4-r7.

FSUB: Subtract floating point value in r0-r3 from five byte form at r9.
Result in r0-r3. Stack not used. Overflow error possible. Uses r4-r7.

FMUL: Multiply floating point value in r0-r3 by five byte form at r9. Result
in r0-r3. Stack not used. Overflow error possible. Uses r4-r7.

FDIV: Divide five byte form at r9 by floating point value in r0-r3. Result
in r0-r3. Stack not used. Divide by zero and overflow error possible. Uses
r4-r7.

FLOAT: Float an integer in r0 to a floating point value in r0-r3. Stack not
used, no error possible. Sets type field in r9 to FLOATING (&80000000).

FIX: Fix a floating point value in r0-r3 to an integer in r0. Stack not used.
Overflow error possible. Sets type field in r9 to INTEGER (&40000000).

FSQRT: Square root of floating point value in r0-r3 return in r0-r3. Uses
r4-r7.

(12) Sped up areas

Simple expressions (indeed very complex ones have been slowed down!). SYS
statement, especially where no results (no TO) are required. String
allocation (by varying amounts depending on what is happening).

(13) Bugs fixed

18-Sep-87   division by power of 2 did not check under/overflow.
24-Sep-87   LOCAL A()
28-Sep-87   A()=const
08-Dec-87   LOCAL A():DIM A(1,1) messed stack due to bug in DIM re type (also
            misallocates integer arrays!).
04-Jan-88   LINE INPUT had sense of LINE flag wrong - misbehaved for A,B ans.
05-Jan-88   HIMEM=HIMEM wouldn't work.
08-Feb-88   CREATE was uncallable due to LVBLNK loosing R4.
26-Feb-88   STRACC postion changed for Arthur 2.00.
29-Feb-88   $A="" check for <&8000.
07-Mar-88   50IF<false>THEN50 unescapeable
12-May-88   INPUT#,a$ at end of file gives end of file message.
23-May-88   ATN of a large power of 2 went wrong.
01-Sep-88   WHILE <no sd> failed in TRACE state due to moving LINE in SLOWMUNG
05-Oct-88   EXP .00001 corrected

[Plus other things which I have forgotten!]