HiSoft BASIC

Fast, Floating-Point ZX BASIC Compiler

















Written by Cameron Hayne with routines by Andy Pennell

Set using an Amstrad PC 1512 running WordPerfect 5.1
Getting Started and Tutorial     
         

Introduction

HiSoft BASIC is a BASIC compiler that surpasses all others for the
Spectrum. There are integer compilers that can make BASIC programs
run more than 100 times faster but they only handle integers (no
decimal, only whole numbers from -32768 to 32767 or from 0 to
65535) and often have other restrictions. There are floating-point
compilers that handle the full range of decimal numbers and all of
the Spectrum's functions but (in spite of advertised claims) they
speed up programs by only a factor of 3 to 5).

HiSoft BASIC combines the advantages of these two types of
compilers without any of the disadvantages.  It is a floating-point
compiler that can obtain the speed of an integer compiler when
doing operations that don't require the complexities of floating-
point arithmetic.  In fact, HiSoft BASIC is simultaneously the
fastest integer compiler and the fastest floating-point compiler
available for the Spectrums.

HiSoft BASIC can compile almost all of the Spectrum's BASIC into
fast machine code.  Unlike some floating-point compilers, it can
handle user-defined functions and two dimensional numeric and
string arrays. Most other compilers have a block of routines about
5K in length (called runtimes) that must be present for the
compiled code to work.  This means that even the shortest BASIC
program compiles to more than 5K.  HiSoft BASIC includes only the
runtime routines that are actually necessary for your code so that
a short BASIC program may compile into only a few hundred bytes.
Also, unlike other compilers, HiSoft BASIC allows you to put the
compiled code anywhere in RAM you want, even in locations normally
occupied by the compiler itself!

HiSoft BASIC is only about 11K in length so it loads quickly.  It
can compile BASIC programs in memory up to about 30K in length on
a 48K Spectrum or up to 40K on the larger, 128K machines.  Another
distinguishing feature of HiSoft BASIC is that it provides full
information on the code that it produces so that it is easy to
interface the compiled code with a co-resident BASIC program.  Or,
if you're interested in machine code, you could use this
information to learn how to use the ROM routines.

Finally, HiSoft BASIC does not blindly follow a recipe in
converting your BASIC to machine code.  Instead it watches for
simple cases (eg: operations with powers of 2, constant array
indices, etc) which it can compile into especially efficient code.

HiSoft BASIC is very easy to use but we recommend you read through
this manual before starting any serious compiling.
Which Spectrum?   

There is a version of HiSoft BASIC for every English-ROM Spectrum
so far released, from the humble, rubber-keyed 48K machine up to
the impressive Spectrum +3.  The cassette has one version for 48K
machines, another for the 128K and +2, and the disk version is
specifically for the +3 that adds file handling. Please make sure
that you have the correct version for your computer before
proceeding.

We do not have a version of HiSoft BASIC that is guaranteed to work
on the+3 with Spanish ROMs, nor do we yet have one that runs on the
+2A!

This manual described all versions of HiSoft BASIC; where there are
machine-dependent features they will be marked in the following
way:

     48   applicable to 48K machines only.

     128  applies to the 128 and +2 computers (not +3) only.

     +3   special +3 features.

Unless so marked, text applies to all versions of the compiler.

There are various key-combinations used to invoke HiSoft BASIC
commands and these differ between the 48K and 128K (including +3)
versions.  Wherever we need to refer to any of these keys they will
be shown like this: Compile which means press the key-combination
that will compile your program.  The particular keys corresponding
to each command are given in Appendix 4, which you should refer to
now before proceeding further.

Making a back-up copy 

Cassette version

One side of the cassette is marked 48K, the other 128K and you
should use the one corresponding to your computer - please note
that neither version will work on a +3.

The facility for making a back-up copy is provided in the BASIC
loader program.  LOAD as usual but hold down the S key during the
last part.  When you hear the beep, prepare your new tape for
recording and then press a key.

To transfer HiSoft BASIC to Microdrive, follow the above procedure
but BREAK into the program after the beep.  Then modify it by
inserting *"m";d; after every SAVE and LOAD, where d is the drive
number desired.  Finally GOTO 9999 to produce a Microdrive copy.

If you have a disk interface such as Disciple or Opus you should
modify the syntax of the loader to suit your interface, in a
similar way to that described above for the Microdrives.

+3 Version

Before doing anything you should write-protect your master disk to
prevent embarrassing accidents.  Make sure you are in +3 BASIC
mode, then insert your master disk and type:

COPY "A:*.*" TO "M:" [ENTER]

Now insert a new blank disk and type:

FORMAT "A:" [ENTER] 
COPY "M:*.*" TO "A:" [ENTER]
ERASE "M:" [ENTER]

General

In all cases, store your original cassette or disk in a safe, dry
place away from heat, magnetic fields and kids!  Only use your
back-up copy so that you can always make a new back-up from the
original if something goes wrong.

Over 3000 man-hours of hard work went into making HiSoft BASIC and
every pirated copy steals away some of our rightful reward for this
work. Ultimately, software piracy hurts you, the consumer, because
prices go up or nasty copy-protection schemes are used and because
programmers like ourselves will no longer find it rewarding to put
the effort into writing good programs for your computer.  If you
somehow find yourself in possession of a pirated copy of HiSoft
BASIC and you find it a good, useful program, then please do the
honest thing and go out and buy yourself a legitimate copy! Now,
lets get started using HiSoft BASIC.

Tutorial - Try this First 

The instructions for using HiSoft BASIC follow this introductory
section but instead of leaving you to read them and figure out
things for yourself, we'll show you the ropes with a few example
programs.

If you have the cassette version, load HiSoft BASIC by putting your
backed-up tape into your tape recorder and type:

LOAD "" [ENTER]  and press PLAY on your tape player.

If you have a disk copy of the compiler, insert your back-up disk
and type:

LOAD "HIBASIC" [ENTER]

When it is finished loading you'll see a copyright notice at the
top of the screen.  We have supplied a number of example programs
on the tape/disk and we will try loading, compiling and running
them now.  Load in the first example program by typing:

LOAD "EXAMPLE1" [ENTER]

LIST it and then RUN it to test it out and make sure it works. This
is a vital step before attempting to compile any program!

As you might not always want to compile all parts of your BASIC
program, it is necessary to tell HiSoft BASIC where to start and
where to stop compiling. As with all instructions to HiSoft BASIC
(called compiler directives) this is done via a REM statement.  The
start-compiling instruction is: REM : OPEN # (do this now by making
this instruction line 1 of the example program).

The stop-compiling instruction is: REM : CLOSE # but this is
optional here since we want to compile right to the end of the
BASIC program.  Now Compile and compiling will start (if you don't
know how to Compile then look in Appendix 4).

During compilation, HiSoft BASIC will pause twice, showing you some
information at the bottom of the screen.  You'll have to press a
key to continue (don't worry about the information - you'll not
need it now).  The borders will change colour (magenta-cyan-white)
and strange dots and colours will appear on the screen.  We'll
explain later what all this is; for now we just need the
information that will appear after the second key press.  For the
first example program, this should indicate that the compiled code
(machine code) is 357 bytes long and that 10 bytes must be reserved
for machine code variables.  For the sake of comparison the number
of bytes taken up by the BASIC program without variables is also
given.  (Please note that your particular compiler may produce
slightly different sized programs to those described here as we may
change the compiler from time to time and it may generate slightly
different code).

The most vital information is in the two lines that tell you how to
save and load the compiled code.  The address is the LOAD line is
the address to be used after RANDOMIZE USR when you want to execute
the compiled code.  For example, if the code is to be loaded to
address 65001 then RANDOMIZE USR 65001 will execute the compiled
code.  But while HiSoft BASIC is resident there's an easier way:
Run will execute the compiled code (this refers to the compiler
command Run detailed in Appendix 4, NOT the interpreter command
RUN).

You can test out the machine code now if you like.  By the way,
don't be alarmed at the fact that this very small program seems to
require so many bytes in machine code.  Most of the bytes are taken
up by the runtimes - subroutines that are included as needed but
that will be re-used by other parts of a larger program.  Thus the
ration of bytes used for machine code to those in the BASIC will
decrease as the size of the program increases.

Your BASIC program is still there after compilation and can be
modified and re-compiled.  Without changing anything, try compiling
it a second time (use Compile) just to see what happens.  All the
information on the final screen will be the same except for the
address where the compiled code is located. Each time you compile
a BASIC program, the compiled code is placed at what the Spectrum
considers to be the top of your memory space (ie just below RAMTOP)
and the RAMTOP is changed to just before the newly-compiled code.
To reclaim that memory (by resetting RAMTOP to its original value)
use Clear.

We want to use the first example program to illustrate that the
variables used by BASIC and the variables used by the compiled code
are totally distinct.

Re-compile the program (Compile).  Now RUN the BASIC version, and
then, as a direct command, execute PRINT N1, N2.  Now execute the
machine code version (Run), this time responding with different
numbers than those you used for the BASIC version.  Now re-execute
the direct command PRINT N1, N2. The BASIC variables are still as
they were.  The machine code variables are local to the compiled
code.

Before we leave this example, we must point out that the INPUT
command is one that behaves slightly differently in the compiled
code than it does in BASIC.  The difference is in its response to
errors.  In BASIC, an error in input returns control to the editor
with an error message.  This would be inconvenient in machine code,
so in the compiled code INPUT commands are error-trapped so that
any error results is a restart of the INPUT. Test this out for
yourself with the compiled code.

Now type:

LOAD "EXAMPLE2" [ENTER]

and LIST it once it has loaded.  Put in a new line:

9 REM : OPEN #

and RUN it to make sure it works, then Compile.  The thing we want
to bring to your attention now is the number of bytes taken up by
machine code variables.  The total is 277; this is 15 bytes for the
FOR/NEXT variable I, 5 bytes for I, and 257 bytes for N$.  The 257
is made up of 2 bytes for the length of N$ and 255 bytes to hold
the actual characters.  Since no name is ever going to be that
long, it seems wasteful to reserve that much space for it.  By
using the REM : LEN directive we can tell HiSoft BASIC how much
space to reserve for a string variable.

In this case suppose we decide that we are safe in assuming that no
name could possibly ever be longer than 50 characters, then we can
tell HiSoft BASIC this by inserting a new line:

9 REM : LEN N$ <=50

(on the 48K Spectrum the <= is the single character obtained by
pressing [SYMBOL SHIFT]-Q).  Do this now and re-compile.  You will
see the number of bytes for machine code variables is now only 72
(52 bytes reserved for N$). It may seem that 50 is still too long,
but it's better to err on the long side - too little space can be
fatal.  Incidentally, all this is necessary because we've chosen to
opt for efficiency over convenience.  In BASIC, when you assign to
a string variable, the old copy is destroyed, all the other
variables are shuffled down, and the new string is inserted at the
end of the variables list. But this takes time! In the compiled
code from HiSoft BASIC, all variables including string variables
are at fixed locations, which gives a great improvement in speed. 
Note that for DIMensioned string variables, HiSoft BASIC can tell
from the DIM statement how much space to reserve and so the REM :
LEN directive is not necessary.

The first two example programs served to illustrate some essential
points about using HiSoft BASIC but they weren't very interesting
as programs and they certainly didn't show any perceptible increase
in speed; and speed after all is what you're here for!  So type:

LOAD "EXAMPLE3" [ENTER]

and we'll start to explore the true capabilities of the compiler. 
RUN the program as usual, to make sure it works (we emphasise that
this is an essential step before attempting to compile any
program).  If you LIST the program you will find that we've already
included the REM : OPEN # directive at the beginning so we're ready
to compile.  Compile and watch.  You will find that you get line
290 at the top of your screen with a flashing ? and the message NOT
SUPPORTED at the bottom.  What is not supported is the tape command
SAVE.  None of the operating-system commands are supported by
HiSoft BASIC because they are usually more appropriately left in
BASIC.  This is where the directive REM : CLOSE # is useful. 
Insert a new line:

271 REM : CLOSE #

and then re-compile.  It will work this time.  Try out the newly-
compiled machine code and you will see the spiral drawn more than
three times faster. A few asides on the program: note, in lines
20-50, the values of the SIN and COS functions are computed only
once and then assigned to variables for future use.  As these
functions (along with TAN, ASN, ACS, ATN, EXP, LN, SQR) are very
slow, this is a smart thing to do wherever possible.  Note also the
CLS in line 15.  This is redundant in BASIC since a CLS is done
automatically when we RUN the program, but it is needed for the
compiled code.

But what about the line 290 that was left out of the compilation? 
Since we now have a machine code version of the program, what we
want is a BASIC loader program that 
looks like the program on the next page:

     10 CLEAR wwwww
     20 LOAD "spiral" CODE xxxxx : RANDOMIZE USR xxxxx
     30 STOP
     40 SAVE "spiral" CODE xxxxx,yyy

where the xxxxx and yyy are the numbers given by HiSoft BASIC and
wwwww is less than xxxxx (ie below) xxxxx.

Now type:

LOAD "EXAMPLE4" [ENTER]

and LIST it.  You will see that it is the same as EXAMPLE 3 but
with the additional lines at the end.  We've already put the line
271 REM : CLOSE #.  If you compile it as it is now, the compiled
code would be precisely the same as that from EXAMPLE 3.  The BASIC
lines after 271 would simply be ignored (although they do figure in
the number given by the HiSoft BASIC for the bytes taken up by
BASIC).  What line 1000 does is to POKE the picture on the screen
into storage at memory address 4500; line 2000 recalls it from
memory onto the screen.

Clear and RUN the program.  When the drawing is completed, execute
GOTO 1000.  After the STOP message (it will take a few minutes)
execute CLS and then GOTO 2000.  After another few minutes, the
spiral will have re-appeared on the screen, but since it takes
several times longer to recall the spiral from memory than it would
take to simply re-draw it, this seems pointless!  But the compiled
version will be faster!  What we want in the compiled version is to
have three separate entry points to the machine code:  The first to
draw the spiral and the second and third to store and recall it
from memory.  We already have the first entry point at REM : OPEN
# and return to BASIC occurs at the REM : CLOSE #.

We want additional entry points at lines 1000 and 2000, so we
insert new lines:

999  REM : OPEN # and
1000 REM : OPEN # (do this now)

The returns to BASIC from these sections of code will be from their
STOP statements.  There is no need for any additional REM : CLOSE
# because we want to compile right to the end of the BASIC.  Now
we're ready to compile so Compile.  This time you will be required
repeatedly to press a key as information about various entry points
comes up on the bottom of the screen.

We will now explain what these numbers mean.  During the first pass
(with a cyan border) you will be told the relative addresses of the
various entry points -ie relative to the start of the compiled code
During the second pass (with a white border) you will be told the
execution addresses (in both decimal and hexadecimal) of the
various entry points. Make a note of these for use later.  Note
also (from the final screen) the number of bytes taken up by the
compiled code.

Remember that if you miss some information during compilation, you
can always re-compile later (after Clear if desired).  Now try out
the compiled code by executing the first part to draw the spiral,
then the second part to store it in memory, the CLS and finally
execute the third part of the compiled code to recall the spiral
screen.  Note that Run only works for the first entry point. You
should find that it now takes about the same time to recall the
spiral from memory as it would to re-draw it.

So our store and recall routine still doesn't seem very useful. But
there's a further way improvement we can make that will
dramatically increase the speed.  The key fact to notice is that
the variables I, SOURCE, and DESTINATION of lines 1000-5040 take on
only values that are positive integers (they range from 16384 to
51911).  If HiSoft BASIC is informed of this fact (it's not quite
smart enough to notice it for itself!) it will generate much more
efficient code because it can then use the native abilities of the
Z80 processor rather than relying on the ROM routines for floating-
point arithmetic.  The way to inform the compiler is to use the
directive REM : INT +.  This directive must come before the first
REM : OPEN #, so we insert a new line (do this now!):

9 REM : INT + I, SOURCE, DESTINATION

This tells HiSoft BASIC that these variables will take on only
values that are positive integers in the range from 0 to 65535.  We
know this to be true for lines 1000 to 5040 but before we can re-
compile we must check that it is true for the whole program. There
is a variable i (which to the Spectrum is the same as variable I)
in lines 130, 160, 230 but we easily can see that it too takes
positive integer values in the right range.  So go ahead and re-
compile. You will notice that the new compiled code takes fewer
bytes, but the real difference is in the speed.

Now it takes less than 0.7 seconds to recall a screen from memory! 
If you time it with a stop watch you will also find a small
decrease in the time taken to draw the spiral.  There is no
dramatic increase in the speed of drawing the spiral because most
of the time is spent in the ROM DRAW routine.

We have just seen how much more efficient it is to use integer
variables wherever possible.  However, in this program it was
relatively easy to convince ourselves that the variables I, SOURCE
and DESTINATION take on only integer values.  In other, more
complex programs it may be more difficult to pick out the 
integer-valued variables.  But help is at hand!  Type Info. Nothing
will happen right away.  But now RUN the BASIC program.  You should
see that the lower screen go bright and the spiral being drawn more
slowly than usual.

When it's finished, type Info again and this time you will be
rewarded with a list of variables.  Beside each variable is the
type of that variable: REAL, INTEG or POSINT (or POSINTEG - a
combination of POSINT and INTEG).  See below for an explanation of
variable types but for now just note that variable i is listed as
POSINTEG which means that its value never went outside the range of
positive integers between 0 and 32767.  The program STOPped at line
280 so what this is really telling us is that i never goes out of
that range in lines 130-230.  The program has not yet explored the
region of lines 1000-5040 so variables SOURCE and DESTINATION are
not even listed yet.

To get a full indication of the variable types we would have to
execute the other two sections of the program separately, eg by
doing the sequence:

Info
RUN
GOTO 1000
GOTO 2000
Info 

If you don't find the waiting times too irksome, you could try this
now but otherwise you can just take our word for it that the
variables I, SOURCE and DESTINATION would all come out listed as
POSINT.  All this is just confirmation of something that we
realised earlier but you can see how it could be useful when
applied to more complex programs.  What Info does is to turn on
another program that keeps watch over the values of the variables
during the BASIC program's execution.  The second Info turns this
off and prints out the results.  Anything you do between the Infos
that affects the variables will be taken account of in the results. 
Thus, if you were to do the sequence: Info, RUN, LET i=1.3, Info,
the variable i could be shown as REAL instead of INTEG.

We must caution you that Info is not an infallible guide to the
types of the variables.  To illustrate this, first get rid of the
existing BASIC by typing Erase.  Do not use NEW as that would wipe
out HiSoft BASIC as well!  Now type in the program:

     10 LET A=0
     20 IF RND >.5 THEN LET A=.5

Do the sequence: Info, RUN, Info and then repeat this sequence a
few times. You will notice that the variable A is listed sometimes
as REAL, sometimes as POSINTEG.  The reason for this is clear -
there is a branch in the program depending on the value of RND and
if RND < .5 the program doesn't realise that A is never non-
integral.  The lesson you should draw from this is that, in using
Info, you should repeat the program enough times with different
inputs to make sure that the whole of the program is explored.  In
this example it would probably be sufficient to do: Info, RUN, RUN,
RUN, RUN, Info.

Now type:

LOAD "EXAMPLE5" [ENTER]

The program is ready to compile, but first LIST it and note the use
of INT after the DATA command.  The variables X and Y are READ from
this DATA list and since they are declared to be of type POSINT by
the REM : INT + directive, it is necessary that the data be stored
in integer format.  This is accomplished by putting an INT after
the DATA.  Compile and RUN this program at your leisure.  Now type:

LOAD "EXAMPLE6" [ENTER]  and RUN it.

You will see that it is a typical example of menu programming.  Try
to compile it and you get line 50 at the top of the screen with the
dreaded NOT SUPPORTED message at the bottom.  Line 50 is what is
called a computed GOSUB statement.  The line number is not given
specifically but must be computed at run-time, so in order to
compile such statements the computer must make a list of all the
line numbers and the corresponding addresses in the compiled code. 
The compiled code for the GOSUB will then search through the list
at run-time to find the address for the line number that is needed.
HiSoft BASIC has the capability to do all this, but since it
results in slower and longer code, we have made it a non-standard
feature that you must select by means of a compiler directive.

Insert a new line:

7 REM : GOSUB :

and it will now compile.

Note that the compiled code is 760 bytes long.  If you look at the
program you'll see that the variable N can be 1, 2 or 3, so the
only line numbers we really need for use in line 50 are lines 100,
200 and 300.  You can tell HiSoft BASIC this by changing line 7 to
read

7 REM : GOSUB 100, 200, 300

and now if you re-compile you'll find the code is 728 bytes long
because we're now keeping only the info for those three lines
rather than all the lines of the program.  For this short program
it's not much of a saving, but for longer programs the saving in
bytes can be enormous, and having a shorter list to search through
can significantly increase the execution speed. 

To see what happens if you omit a relevant line number, delete the
300 from line 7, recompile, and try out the compiled code, 
selecting option 3.  Note also that if you change line 50 to GOSUB
100*N-1, although it would still work in BASIC, the compiled code
wouldn't because lines 99, 199 and 299 don't exist.

The rest of the programs on the tape/disk are ready to compile. You
can LOAD and LIST them to see further examples of the use of
compiler directives. The next program is called SIEVE and is a
standard benchmark program.  It finds all prime numbers less than
twice the number used in line 20.  As it stands, the program will
not work in BASIC because there isn't room for an array of 8192
elements of 5 bytes each.  However, the compiled version with the
array f$ () declared as POSINT (so that each element only takes 2
bytes) works fine and takes only 2.9 seconds.  Add a line:

165 PRINT prime

if you want to see the prime numbers (but this will slow it down a
lot).

To get the program to work in BASIC you will have to change both of
the 8192s in line 20 to something like 7000 or smaller and run it
on an otherwise empty Spectrum.  With 7000 in line 20, the program
takes 418 seconds to finish, compared with only 2.6 seconds for the
compiled code - a speed increase of 161 times!  If you try to
compile this program twice in succession without resetting RAMTOP
(by Clear or CLEAR) in between, you will find that the addresses on
the final screen after SAVE and LOAD differ from each other and you
get a message DO NOT TEST on the bottom of the screen.  This means
that the code is not in its proper position and would have to be
SAVEd and re-LOADed to its proper position before execution, but
beware of over-writing HiSoft BASIC - see MEMORY MAPS in Appendix
3.

The last two programs on the tape are SHELLSOR and QUICKSOR.  Lines
9000 and higher of these two programs contain subroutines that sort
an array x() of numbers into ascending order using two different
algorithms.  The rest of these programs are for testing the speed
of these two algorithms inserting data that is randomly arranged
and data that is already in order. You will find that QUICKSOR is
faster for randomly-arranged data but SHELLSOR is faster for data
that are already almost all in order.  The subroutines can easily
be modified to sort into descending order or to sort strings
instead of numbers.  If you compile them, you will find that the
compiled versions are up to 19 times faster!

HiSoft BASIC in Detail

How to use HiSoft BASIC

1    Using your back-up cassette, Microdrive or disk simply type:

     LOAD"HiBasic" [ENTER]

     128  The loading of HiSoft BASIC will abort if the RAM disk is
          not empty.

     +3   Loading HiSoft BASIC will delete any files on the RAM-
          disk.
 
2    Either type in your BASIC program or LOAD it in from your
     tape, disk or Microdrive.  Note:  You must arrange your BASIC
     program  so that it is possible to execute it by simply
     entering RUN (that is, it must start at the lowest line and
     all the variables must be defined within the program).  For
     example, if you have a BASIC  program which you execute by
     entering RUN 9000 then insert a new line at the beginning that
     says GOTO 9000.
 
3    Make sure your BASIC doesn't include any of the commands or
     functions that aren't supported by HiSoft BASIC (see summary
     of differences from Spectrum BASIC).

4    Insert a new line with the compiler directive REM : OPEN # at
     the beginning of your program.  Other directives are optional.

5    RUN your program to make sure it works.  Try it with different
     inputs to cover all the possibilities and test out all the
     branches of the program.  The compiled code will be designed
     to reproduce the effect of the BASIC (except faster!), so if
     it doesn't work in BASIC, the compiled version won't work
     either.  However, if your BASIC version works, you can expect
     the compiled code to do the same.  It is a good idea to SAVE
     the BASIC program before proceeding.

6    Compile by using Compile (see HiSoft BASIC commands).  Refer
     to ERROR MESSAGES if compilation stops with a message at the
     bottom of the screen

7    SAVE the compiled code.

     The compiled code is just like any other machine code program. 
     To execute it requires the command RANDOMIZE USR xxxxx where
     xxxxx is its address.  The compiled code will return to BASIC
     at points where there was a STOP command or a REM : CLOSE #
     directive or if it reaches the end of the program.  Note that
     you must CLEAR wwwww before LOADing in the code, where wwwww
     is any address less than xxxxx.

HiSoft BASIC Commands

Commands may be typed in upper or lower case.  Execution of the
command is immediate upon receipt of the final character (no
[ENTER] is needed).  If at any time these commands should stop
being accepted, re-initialise the command interpreter by typing: 

     RANDOMIZE USR 23792 [ENTER]

Read APPENDIX 4 to see which key-combinations correspond to each
command on your particular computer.

Compile
     Starts compilation of the BASIC program.  Compiles those
     portions of the program between the compiler directives REM :
     OPEN # and REM : CLOSE #.  The compiled code is placed just
     below RAMTOP and RAMTOP is revised.

     During the first pass, the relative addresses of the entry
     points to the code are given.  Compilation pauses for you to
     hit a key.

     During the second pass: the execution addresses of the entry
     points to the (both in decimal and hexadecimal) are shown. 
     Compilation pauses after these until you press a key.

     At compilation the following is displayed: 
 
     -the number of bytes taken up by the compiled code
     -the number of bytes needed for machine code variables
     -the number of bytes occupied by the BASIC program
     -the commands to be used to SAVE the compiled code and to LOAD
     it back afterwards.
 
     The final screen is also sent to the printer if one is      
     connected.  Other information is available by use of the
     compiler directives REM : LINE and REM : LIST.

Clear
     Does the same as the BASIC command CLEAR 65367 except it
     doesn't do a CLS or a RESTORE.  That is, it sets RAMTOP to
     65367 (just below the usual position of the user-defined
     graphics) and sets up the machine stack there.  In the context
     of HiSoft BASIC, it effectively erases previously-compiled 
     code and thus provides space for new code.  Note  that if you
     have some machine code in high memory that you want to
     preserve then don don't use Clear, instead CLEAR to just below
     your machine code. 

Run
     Executes the newly-compiled code, starting from the first
     entry point.  This command does the same as RANDOMIZE USR
     xxxxx where the address xxxxx is the  one on the final screen
     (except that it doesn't affect the system variable SEED).

Info
     This command is used to find out information about the
     variables used in your BASIC program.  This information can
     then be communicated to HiSoft BASIC by means of compiler
     directives, enabling the compiler to produce more efficient
     code.  The command Info gives the types (see VARIABLES) of the
     simple numeric variables used (no information is given on
     arrays).  It also gives the maximum lengths attained by the
     simple string variables used.  After you first type Info
     nothing will have appeared to have happened, but an interrupt-driven
     program has been turned on and the BASIC variables have been 
     CLEARed.  From then on, until you type Info again, the interrupt-
     driven program polls the BASIC variables (except for arrays) and 
     maintains a file of the variable names and types.  The second Info 
     displays this file and then deletes it.  In between the two Infos you 
     can do anything you normally do in BASIC.  The bottom of the screen  
     will go BRIGHT to show that you are in Info mode.  The normal 
     procedure would be to RUN your BASIC program one or more times (with 
     different inputs) so that the variables cover their whole range of 
     possible values.  There is a small possibility that  Info may give 
     incorrect results because it only looks at the variable entry every 
     1/50 of a second.  For example, with the oneline program

     10 LET I=1 : LET I=.5 : LET I=1

     The sequence Info, RUN, Info will tell you that I is POSINTEG
     when clearly it should say REAL.  So Info is not an infallible
     guide to the variable types, but for most programs it works
     wonderfully.

Erase
     Removes the BASIC program and variables from memory without
     affecting HiSoft BASIC or any machine code above RAMTOP.  Note
     that this is drastically different from NEW which would
     eliminate HiSoft BASIC as well since it lies below RAMTOP.

     48   To prevent you from accidentally doing a new, it has been
          disabled (but if you really want to get rid of
          everything, type a colon and then NEW). 

Break
     48   Pressing both [SPACE] and [CAPS SHIFT] (the normal BREAK
          procedure) will stop whatever program is executing at the
          moment, including machine code programs.  This means that
          you can try out the compiled version of your program and
          stop it at any point (note, however, that the compiled
          code, like most machine code programs, does not include
          any test for Break and so, when you are using the
          compiled code outside of HiSoft BASIC, there is no way to
          stop it before it returns to BASIC).

   +3/128 The [BREAK] key will not normally be noticed when pressed
          within a compiled program.  If you require break checks
          use the REM : BREAK directive.

   +3/128
 
Print
     The Print command diverts the output of the next command to
     the printer (instead of the screen).  Make sure your printer
     is attached and ready if you use this command.  For example,
     Print followed by Compile will send all compiler information
     to the printer (or whatever is on stream 3).

The following commands would normally be used only when directed to
do so by HiSoft BASIC:

CompileData
     As Compile but stores only the code generated by the DATA
     statements.

CompileNonData
     As Compile but does not store the code generated by DATA
     statements, ie it stores all the code except that of DATA
     statements.

     These two commands are used to break up a large program that
     includes DATA statements into two parts: the code proper and
     the data.  It does not matter which of the two commands is
     done first but both must be done.  A flashing D or E will
     appear on the final screen to remind you which part you have
     done.  Unless you are using the REM : USR directive, you must
     reset RAMTOP after doing the first part (either CompileData or
     CompileNonData) so that it is the same for both parts in order
     that both parts will be designed for the same execution
     address. Thus a typical sequence would be: Clear, CompileData,
     SAVE the data part, Clear, CompileNonData, SAVE the code
     proper.  Note that the two parts will form one continuous
     block of code in your final program and so, after you re-LOAD
     the code, you can SAVE it as one block if you desire.


Summary of differences from
Spectrum BASIC 

(See further comments in notes on compiled BASIC)

1    No expressions (except VAL "number") are allowed in DIM or
     DATA statements.

2    If expressions (other than VAL "number") are to be allowed in
     GOTO, GOSUB or RESTORE statements, then the appropriate
     compiler directive must be used.

3    Arrays of three or more dimensions (eg X[I,J,K]) are not
     supported.

4    48   VAL stringvariable (eg VAL A$) is not supported (but VAL
          "expression" is OK).

4  +3/128 Use the REM : VAL compiler directive to allow VAL and
          VAL$ to be used as normal.

5    The system commands CLEAR, CONTINUE, ERASE, FORMAT, LIST,
     LLIST, LOAD, MERGE, MOVE, NEW, RESET, RUN, SAVE and VERIFY are
     not supported (but passing back and forth between BASIC and
     the compiled code is easy so you can incorporate these in your
     programs).

6    INPUT commands are automatically error-trapped 

7    BREAK is disabled

8    The default attributes of PAPER 8; FLASH 8; BRIGHT 8 that
     BASIC uses for PLOT, DRAW and CIRCLE commands are not
     instituted in the compiled code.

9    The printing of a string that contains colour control codes
     may change the default attributes in subsequent PRINT
     statements.

10  48/128     OPEN # only supports the standard Spectrum device
               names "K", "S" and "P".  If you want to use filing
               on a disk system then OPEN and CLOSE the file in
               interpreted BASIC, then use PRINT #, INPUT # etc in
               your compiled programs.

Variables 

HiSoft BASIC allows all of the variable names that BASIC does. The
speed and storage space of the compiled code are (unlike in BASIC)
the same whether you use long or short variable names so
descriptive names are to be encouraged for readability.  HiSoft
BASIC supports numeric and string arrays of up to 2 dimensions. 
Ordinary string variables behave as in BASIC except that they must
not exceed in length the space reserved for them at compile time. 
By default this is 257 bytes (to allow a string of up to 255
characters, plus 2 bytes for the length) but this can be changed by
means of the REM : LEN directive.

Variable Types - General

The type of a variable is an indication of the range of values it
may contain.  In BASIC, variables are of two types: numeric and
string.  HiSoft BASIC has 3 numeric types as well as strings.  The
default is REAL.  Variables may be declared to be INTEG or POSINT
in order to enable more efficient code to be generated (see
Compiler Directives).  If you don't care that much about speed or
the size of the compiled code you can just forget about the integer
types and keep everything as REAL.  All variables used in the
compiled code are stored in their own area just after the code (not
in the BASIC variables area).

The REAL type

This is the same as the BASIC numeric type; ie REAL variables can
take on all floating-point values between -1.7 E 38 and +1.7 E 38
(but note that the smallest non-zero positive number is 2.94 E-39). 
REAL variables take up 5 bytes and are stored in the same format as
in BASIC.

The INTEG type

The values taken by an INTEG variable are restricted to integers
(whole numbers) between -32768 and 32767 inclusive.  INTEG
variables take up 2 bytes of storage in the standard Z80 format
(least significant byte first).

The POSINT type

The values taken by a POSINT variable are restricted to positive
integers (whole numbers) between 0 and 65535 inclusive.  POSINT
variables take up two bytes of storage in the standard Z80 format
(least significant byte first).

Note that the ranges of POSINT and INTEG variables overlap.  If a
variable takes on only integer values between 0 and 32767, you have
the option of declaring it to be either POSINT or INTEG (Info will
show such variables as POSINTEG).  The storage locations of
variables can be obtained by using the compiler directive REM :
LIST.  Note that the current length of an ordinary string variable
(or the length of each string for a dimensioned string variable) is
stored in the first two bytes of the space reserved for that
stringvariable, followed by the text of the string (or strings).

Numerical Constants

Numbers may be in ordinary or in scientific notation.  For the
purpose of storage format and evaluation of expressions, the type
of a number is taken to be in the range in which it falls (see
Variable Types).  The exception to this is if no INT or INT+
directives have been used.  In this case all numbers are taken to
be REAL and stored as 5 bytes.

Conversion between Types

REAL values are rounded to the nearest integer when a POSINT or
INTEG value is required (error Integer out of range if bigger in
absolute value than 65535).

Integer values (between -65535 and 65535) are converted to lie in
the proper range for POSINT or INTEG types by adding or subtracting
65536 as appropriate.

For example, if the value 4000 is assigned to INTEG variable I, it
will end up that I = 4000 - 65536 = -25536, whereas if -4000 is
assigned to I, it will end up that I = -40000 +65536 = +25536. If
the value -3 is assigned to a POSINT variable P, it will end up
that P = -3 +65536 = 65533.

Compiler Directives

Compiler directives are a means of giving instructions to the
compiler.  Each compiler directive is of the form REM : followed by
a Sinclair keyword and each must be on a separately-numbered line
within the BASIC program to be compiled

REM : OPEN #
     Turns compilation on and inserts code to enable an entry from
     BASIC at this point.

REM : CLOSE #
     Inserts code for a return to BASIC and turns compilation off. 
     Optional if compilation right to the end of the BASIC is
     desired (note that this directive is equivalent to a STOP from
     the point of view of the compiled code although of course it
     has no effect on BASIC).

Note: The following compiler directives must precede (ie must
have a lower line number than) the first REM : OPEN #

REM : LEN
     Tells HiSoft BASIC how much space to reserve for non-
     dimensioned string variables.  If a string variable is used
     without being dimensioned or appearing in a REM : LEN
     directive, then the  compiler will reserve space for 255
     characters plus 2 bytes for the length of the string. 
     Examples of use:

     REM : LEN D$ <=5, F$ <=704

     would allow D$ to be up to 5 characters long and F$ to be up
     to 704 characters long (thus, including the 2 bytes for the
     length of the string, it would reserve 7 bytes for D$, 706
     bytes for F$).  Any other non-dimensioned string variable
     would have space reserved for 255 characters.

     REM ; LEN D$ <=5, F$ <=704, $ <=32

     As before but any other non-dimensioned string variable is
     limited to 32 characters (there would be 7 bytes reserved for
     D$, 706 bytes reserved for F$ and 34 bytes reserved for any
     other).  The $ without any letter changes the default max-
     length.  The default max-length cannot be greater then 255. 
     Note that LEN appears only once and that <= is the single
     symbol obtained by [SYMBOL SHIFT]-Q.  Please note that if you
     declare for example that D$ will never be more than 5
     characters long (as above) and then LET D$ = "MISTAKE", the
     extra characters will overwrite something else and the results
     will be unpredictable.  So you should always reserve more
     space than will ever be needed.  The use of Info can help you
     avoid counting.

REM : USR
     Tells HiSoft BASIC where you want the compiled code to start
     HiSoft BASIC always locates the compiled code at the top of
     memory (as given by RAMTOP) but the code can be designed to
     execute from any address you specify.  If you don't include a
     REM : USR directive than the code will usually be designed to
     execute from where it is.  If you do include a REM : USR
     directive then the compiled code must be SAVEd and the re-
     LOADed to its proper address before you try to execute it. 
     For example, REM : USR 30000 would make the compiled code
     executable from address 30000.

REM : INT
     Tells HiSoft BASIC that certain variables will be of type
     INTEG.  For example:  REM : INT A,C,SCORE,A(),B tells the
     compiler that the simple numeric variables A, C, SCORE and B
     will be of type INTEG and that the array A() will also be of
     type INTEG.

REM : INT+
     Tells HiSoft BASIC that certain variables will be of type
     POSINT; eg.

     REM : INT + J, I, LEVEL, K(), K

     tells the compiler that the simple numeric variables J, I,
     LEVEL and K will be of type POSINT and that the array K() will
     also be of type POSINT.

REM : INT FN
     Means that certain defined functions will return values
     restricted to integers (-32768 to 32767). Eg: REM : INT FN A,
     T, B tells HiSoft BASIC that the defined-functions A, T and B
     have INTEG values.

REM : INT+FN
     Tells the compiler that certain defined functions will return
     values restricted to positive integers in the range 0 to
     65535.  For example, REM : INT + FN N, I, P tells HiSoft BASIC
     that the defined-functions N, I and P will have POSINT values.

REM : FN(INT)
     Tells HiSoft BASIC that certain one-letter variables are to be
     considered as type INTEG when used as dummy variables in a DEF
     FN statement.  For example, REM : FN (INT A, B, C) tells
     HiSoft BASIC that the dummy variables A, B and C are to be
     considered of type INTEG in all DEF FN statements.  Note that
     A, B and C might be a different type when they are not used as
     dummy variables.

REM : FN(INT +)
     Dictates that certain one-letter variables are to be
     considered as type POSINT when used as dummy variables in a
     DEF FN statement eg:

     REM : FN (INT + I, J, K)

     tells HiSoft BASIC that the dummy variables I, J, and K are to
     be considered as type POSINT in all DEF FN statements.  Note
     that I, J, and K might be a different type when they are not
     used as dummy variables.

REM : GOTO
REM : GOSUB
     These two directives are completely equivalent.  If you use
     either of them, then GOTO and GOSUB expression (eg GOTO 100*N)
     will be supported and the compiled code will include a list of
     line numbers and the corresponding addresses in the compiled
     code.  There are two forms : If the GOTO or GOSUB is followed
     by : (eg REM : GOTO :) then all of the program's line numbers
     within the region being compiled will be included in the list. 
     If the GOTO or GOSUB is followed by a sequence of line numbers
     (eg REM : GOTO 100, 200, 300) then only those line numbers
     will be included in the list.  In either case, if a line
     number not in the list is needed, the compiled code will
     generate a run time error STATEMENT LOST.

REM : RESTORE
     If you use this directive the RESTORE expression (eg RESTORE
     1000+N) will be supported and the compiled code will include
     a list of the line numbers of DATA statements and the
     corresponding addresses in the data area of the compiled code.

     The two forms of this directive (eg REM : RESTORE : and REM :
     RESTORE 1000, 1001, 1002) have a similar effect to those for
     REM : GOTO except that here only the line numbers of DATA
     statements are put in the list and the addresses are those in
     the data area of the compiled code. 
 
REM : LIST
     Tells HiSoft BASIC to produce a listing of the runtime
     routines used and the machine code variables.  Address are
     given in both decimal and hexadecimal. 
 
REM : LINE
     Tells HiSoft BASIC to print the addresses of the compiled code
     for each line of BASIC starting at a given line number. 
     During the first pass, the relative addresses are given, while
     during the second pass, execution addresses (in both decimal
     and hexadecimal) are given.  For example REM : LINE 200 would
     result in the addresses being given starting with line 200. 
     Note that this is just for interest - entry to the compiled
     code from BASIC should only occur at points with a REM : OPEN
     # directive.

REM : LPRINT  48
     Switches output from LINE and LIST directives over to stream
     3 (the printer).  If this directive is used, HiSoft BASIC does
     not wait for you to press a key before continuing so you may
     want to use it even with no printer attached (or with it
     switched off) just to eliminate the waits for keypresses in
     those cases where you are only interested in the information
     on the final screen.  Spectrum 128 and  +3 owners should use
     the Print command instead. 

REM : BREAK +3/128
     Normally you cannot break out of a compiled program, unlike
     the 48K compiler, so this option makes the compiler generate
     explicit checks for the [BREAK] key throughout your program. 
     This produces slower and larger code but it is particularly
     useful whilst debugging. 
 
REM : VAL +3/128
Used to allow greater support of VAL and VAL$.  See the next
section for more details and an example of its use. 


Notes on Compiled BASIC

The following notes explain the differences between Sinclair BASIC
and compiled HiSoft BASIC.  They are somewhat technical and can
often be ignored until a problem becomes evident. 
 
Arithmetic Operators (+, -, *, and /)

1    If one of the operands is REAL, the other is converted to
     REAL.  If not so already. 
 
2    Division (/) is the ordinary floating point operation  
     operands converted to REAL), not integer division, even if
     both operands are of integer type!  Thus 5/3 = 1.677 instead
     of the 1 you would get with integer division (but see note in
     Tips on Efficiency).

3    The minus sign (-), whether unary or binary, converts a POSINT
     value into an INTEG value.  Thus if P1 and P2 are both of type
     POSINT (P1-P2) is of type INTEG as is (-P2).  Note that since
     POSINT can be as large as 65535 while the most negative INTEG
     is -32768, you can get some out or range values this way.  No
     error messages will be generated - you may just get some
     strange results (see Conversion Between Types).  So beware if
     you are subtracting POSINT values larger than 32768 (not that
     likely). 
 
4    Apart from the exception of note 3, the type of the result
     from a sum, difference or product is the dominant type of the
     two types involved.  REAL dominates both INTEG and POSINT
     while INTEG dominates POSINT.  Thus, for example, if R, I and
     P represent quantities of the types REAL, INTEG and POSINT
     respectively then (I*R) is of type REAL, while (I+P) is of
     type INTEG. 
 
5    Because (P+I) and (I+P) are type INTEG, there may be a slight
     difficulty (similar to that of note 3) if the POSINT value is
     greater than 32767.  For example, if P=65535 and I=-1, the 
     you might expect (P+I) to be 65534 but since it is supposed to
     be INTEG, the value you would obtain (see Conversion Between
     Types) if you did PRINT (P+I) would be -2. 

     Since -2 has the same internal representation as 65534 (it's
     only the type declarations that differentiate them!) this
     would be okay if you were using (P+I) to specify a memory
     address.  A way out of all this confusion would be to use 
     POSINTs greater than 32767 only for addresses or as FOR/NEXT
     loop variables where the difficulties are taken care of
     automatically.  An alternative is always to assign such
     problematical expressions to a POSINT variable (if they are
     supposed to be positive) before using them.  Thus: LET P=P+I
     : PRINT P gives 65534 as desired 

Comparison operators 

1    If one of the operands is REAL, the other is converted to
     REAL, if not so already. 

2    The results of a comparison between REAL values is of type
     REAL.  The results of all other comparisons are of type
     POSINT. 

3    Note that if P is a POSINT quantity, P>=0 is always true, so
     something like LET P=P+1 : IF P>=0 THEN GOTO 100 doesn't make
     sense. 
 
AND, OR

The results of these operations is of the same type as the first
operand.

LET

When an expression is assigned to a variable, the value of that
expression is automatically converted to the type appropriate for
that variable.  Error messages or erroneous results may be
generated if you try to assign an out-of-range value to a variable.

For example, suppose I and R are respectively INTEG and REAL
variables.  LET R=I would (as you'd expect) give R the value of the
variable I.  LET I=R would round the value of R to the nearest
integer before assigning it to I; eg. if R=3.14 then I would be 3. 
If the value R is bigger in absolute value than 65535 (so it won't
fit into two bytes) you would get the error message Integer out of
range.  However, LET I=4000.3 (a REAL value) or LET I=40000 (a
POSINT value) would generate no error message even though the value
it too big for the INTEG range.  Instead, the result would be that
I would get the value -25536 (see Conversion between Types).

INPUT

1    INPUT statements are error-trapped and any error in input will
     result in a restart of the INPUT statement.  This means that
     there is no way out of an INPUT statement other than giving
     input of the type requested.

2    The evaluation of numeric inputs is done via the BASIC VAL
     routine so expressions involving existing BASIC variables (as
     opposed to machine code variables) are accepted as input.

3    See the note under LET concerning out-of-range values.

FOR/NEXT

Be careful that, in a FOR/NEXT loop, the loop variable does not try
to go out of range for its type.  For example:

10 REM : INT + P
20 REM : OPEN #
30 FOR P=100 TO 0 STEP -1
40 body of loop
50 NEXT P

Here P is declared as POSINT (and there's nothing wrong with having
a negative STEP value with a POSINT loop variable!) so that after
what should be the last iteration (with P=0), the NEXT P statement
tries to decrease P but instead of becoming -1, P becomes 65535 so
the loop will never terminate.  The problem lies in not recognising
that loop variables always end up one STEP past their limit value.

Each FOR must have one, and ONLY one, matching NEXT statement.

GOTO, GOSUB

1    Unless one of the compiler directives REM : GOTO or REM :
     GOSUB is being used, the line number must be given explicitly
     as a number or as a VAL "number".

2    The line referred to must actually exist within the portion of
     BASIC being compiled (if you have trouble with this, put in a
     REM statement with the appropriate line number).

RUN

RUN is not supported but can be replaced by a GOTO the first line
of your program.  You may want to put a CLS statement at the
beginning.

RETURN

Be careful not to allow the execution of a RETURN from a subroutine
without having done the corresponding GOSUB.  This would cause an
error Return without GOSUB in BASIC but it may crash the compiled
code.

STOP

STOP causes a return to BASIC from the compiled code.  Note that
the REM : CLOSE # directive is effectively the same as a STOP from
the point of view of the compiled code.

DIM

1    HiSoft BASIC supports both numeric and string arrays of up to
     2 dimensions.  The dimensions of the array as given in the DIM
     statement are fixed at compile time and must be given as
     explicit numbers or as VAL "number" (no expressions allowed).

2    The dimensions of an array must be the same throughout the
     program.  That is, you cannot re-dimension an array with
     different numbers than those used in the first DIM.

3    you must not use a string variable as an ordinary string
     variable at the beginning of a program and then later change
     it to a dimensioned string variable with a DIM statement.

4    There are no checks within the compiled code to ensure that an
     array index is within range.

CLEAR, CONTINUE, ERASE, FORMAT, LIST,
LLIST, LOAD, MERGE, MOVE, NEW, RESET,
SAVE, VERIFY

None of these system commands are supported by HiSoft BASIC but you
can still use any of them by going back and forth between BASIC and
the compiled code.  These commands are often inappropriate or
inconvenient in a purely machine code program.

VAL, VAL$

     48

1    VAL$ is not supported

2    VAL "Expression" is the only form that is supported.  Here,
     Expression must be a numeric expression.  The form VAL string
     variable (eg VAL A$) is not supported because to do so would
     have entailed either a great loss in efficiency overall or
     would have required the whole of the compiler to be present
     with the compiled code at execution time.

3    Sometimes the unsupported form VAL A$ can be replaced by a
     series of statements eg.

     IF A$ = "1" THEN LET A = 1
     IF A$ = "2" THEN LET A = 2

     Note also that if A$="0", "1",..., "9" then VAL A$ can be
     replaced with (CODE A$ 48) which is supported.


     128/+3

     By default the compiler works exactly as described above,
     supporting only VAL with strings in quotes and not supporting
     VAL$.  However, the directive, REM : VAL changes the way the
     compiler works, and allows VAL and VAL$ to be used in similar
     ways to the interpreter.  For example

     10 REM : VAL
     20 REM : OPEN #
     30 LET a$="40+2"
     40 PRINT VAL a$

     will compile and run in exactly the same way as the
     interpreter.  One important difference is that any variables
     referred to in the strings will be the interpreter's idea of
     that variable, not the compiler's.  This can be used to pass
     values from interpreted code to compiled code.

RESTORE, READ, DATA

1    On entry to the compiled code (at every REM : OPEN #) an
     automatic RESTORE is done to set the data-pointer to the first
     of the machine code DATA items.

2    Unless a REM : RESTORE directive is used, the line number is
     under is under the same restrictions as that of a GOTO.

3    If you READ from a DATA list into a POSINT or INTEG variable
     then that DATA list must be prefaced with the INT keyword to
     tell HiSoft BASIC to store the data in integer format (2 bytes
     per number).  For example:

     10 REM : INT +X,Y,I
     20 REM : OPEN #
     30 FOR I=1 TO 50
     40 READ X,Y : PLOT X, Y
     50 NEXT I
     60 DATA INT 127, 87, 126, 85,.....
     70 DATA INT 26, 50, 29, 51,...

4    Only numbers, VAL "number" and explicit strings are allowed in
     DATA statements.  Variables and other expressions in DATA
     statements are not supported.  Instead of READing an
     expression into a variable you can do it (albeit less
     conveniently) by a direct LET statement.

5    The machine code data pointer is stored at 23660 in the space
     normally used for the BASIC system variable S-TOP.

PLOT, DRAW, CIRCLE

In BASIC, the commands PLOT, DRAW and CIRCLE have as default
attributes PAPER 8: FLASH 8: BRIGHT 8 so that the only attribute
changed by these commands is the INK.  For efficiency's sake, the
setting of these default attributes is omitted in the compiled
code.

This means that (in odd cases where it makes any difference) to get
the same effect as in BASIC you would put these attributes in
explicitly; for example: you could use PLOT PAPER 8; FLASH 8;
BRIGHT 8; X,Y instead of merely PLOT X,Y.  An artificial example of
a case where it makes a difference is the following:

INK 6 : PAPER 4 : CLS : PAPER 7 : PLOT 127,87

DEF FN

If a defined function has more than 4 arguments, then the arguments
must be all of the same type.

Including other machine code

A compiled program may call other machine code routines.  Of
course, the machine code must not overlap with the compiled code -
use the REM : USR directive if necessary to avoid this.  If you
want to test the compiled code together with your machine code
while HiSoft BASIC is still present, then the other machine code
must not overlap HiSoft BASIC (See memory maps).

Note that the machine code which relies on the structure of the
BASIC program or the location of BASIC variables will not work when
called from the compiled code (the same is true of POKEs from
BASIC, eg POKEs to system variables NEWPPC and NSPPC).  Note that
when HiSoft Basic is present, BASIC programs are higher up in
memory than usual.

Compiling large programs

Before compiling a large program, type clear to clear out any junk
that may be cluttering up high memory (in extreme cases, where you
need every last byte, it may be necessary to CLEAR 65535 and say
goodbye to the user-defined graphics).

Note that by means of the REM : USR directive, the compiled code
can be designed to start low enough (ie within the space now
occupied by HiSoft BASIC) to allow you to still use other machine
code and user-defined graphics in the final program (it's just
during the compilation process that we need the space).

If there is enough room below RAMTOP for the compiled code and the
machine code variables then space is reserved for the machine code
variable, and the code can be tested in place.  However, if there
is space only for the compiled code, then the variables are omitted
and the message DO NOT TEST is given.  If HiSoft BASIC finds there
is not enough space below RAMTOP for the compiled code, it will
check if there would be enough space if the BASIC program were
deleted.  If so, you will be asked for permission to delete the
BASIC, and if you allow this, HiSoft BASIC will proceed to
overwrite the BASIC with the compiled code.

If you are trying to compile a large program that includes DATA
statements, HiSoft BASIC may direct you to use *D, *E.  This means
that it is not possible to compile the program in one go.  You must
use CompileData (*D) and CompileNonData (*E) to compile the data
separately from the rest of the code (note that it is better to do
the smaller of the two parts first to lessen the chance that the
BASIC will have to be deleted and re-loaded before doing the second
part.  This usually means doing the CompileData before
CompileNonData).

If you get the message not enough room for M/C and you haven't just
refused permission to delete the BASIC, then you must somehow
reduce the length of the compiled code to get it to fit.  Using
INTEG or POSINT variables instead of REAL can save a lot of bytes. 
Try to find out (by using REM : LINE) which parts of the program
use the most bytes and replace as many statements as possible by
GOSUBs to subroutines.  Try to make your assignments to variables
via READing from DATA statements instead of with LET.  If you are
using computed GOTO, GOSUB or RESTORE, then direct HiSoft BASIC to
include in the list only those line numbers actually needed.

If your BASIC program breaks up naturally into independent parts
(ie parts that don't share variables) then you could compile these
parts separately.

However, you should be aware that doing this involves a certain
amount of duplication of runtime routines (just how much
duplication can be ascertained by using the REM : LIST directive).

Tips on Efficiency

1    Use integer variables (POSINT or INTEG) wherever possible,
     especially in FOR/NEXT loops (POSINT FOR/NEXT loops are
     slightly more efficient than INTEG ones).  Sometimes programs
     involving REAL variables can be re-written (perhaps by scaling
     everything up by a factor of 10000 etc) to enable them to be
     expressed in terms of integer variables.

2    If you use defined functions, try to arrange it that their
     arguments are integer variables and that the function returns
     an integer value.  Then tell HiSoft BASIC this by means of the
     appropriate compiler directives.

3    In expressions, try to put the simpler of the two operands on
     the right side of the operator.  If you do this, HiSoft BASIC
     will be able to recognise many of the simple cases and code
     efficiently for them.  For example, LET A=B*2 will result in
     far faster code than LET A=2*B because in the first case,
     HiSoft BASIC recognises the simple case of multiplication by
     two.  A number is simpler than a simple numeric variable which
     is simpler than an array or a function (but note that an array
     indexed by a number (instead of a variable) is equivalent to
     a simple variable because HiSoft BASIC recognises the simple
     case and computes its address at compile time).  Some of the
     simple cases recognised by HiSoft BASIC:

     *    Multiplication or division by a power of 2
     *    changing an integer variable by 1, 2 or 3
     *    operations with simple numeric variables (or array
          variables with constant indices)

     Thus:

          P+3 is better than 3+P
          (I1 + I2) *P is better than P* (I1 + I2)
          IF A(I)>P is better than IF P<A(I)

4    HiSoft BASIC recognises the two cases of squaring and cubing
     and codes to do these by means of multiplication rather than
     using the general to-a-power  routine (which is very slow). 
     That is, it effectively replaces X2 by X*X and X3 by X*X*X,
     however it does this in such a way as to generate more
     efficient code than if you were to make these replacements in
     the BASIC.  Here X can be any quantity - not just a variable. 
     Unfortunately, BASIC cannot cope with X2 or X3 if X is
     negative, so you would have to modify things (eg to (ABS X)2)
     just to get the BASIC to work but otherwise it is best (from
     the point of view of efficiency in the compiled code) to leave
     it as X2.

5    As mentioned above (notes on compiled BASIC) division is
     always considered as floating-point division.  However, in one
     special case, HiSoft BASIC recognises that it can code for
     integer division and get the same answer as if it did
     floating-point division.  This is the case where the division
     occurs inside an INT in the form INT (F1/F2) where F1 and F2
     are both integer factors. F1 and F2 don't have to be
     variables, they can be complicated factors (eg (2+I2)2) as
     long as they are either POSINT or INTEG.  But the expressions
     inside the INT must be of the form of something divided by
     something else and nothing more.  Thus INT (5 + F1/F2) and INT
     (F1/F2 + 5) would be coded using floating-point division while
     5 + INT (F1/F2) would gain the advantage of integer division
     if F1 and F2 are integer factors.  Similarly, INT (5*F1/F2)
     would be coded using floating point division, but INT
     ((5*F1)/F2) would gain the advantage of integer division.  So
     if you want the truncated effect of integer division (or
     simply don't care about the fractional part of a division) put
     your divisions inside INT.  Note that none of this affects the
     answer you get from a division - the compiled code will always
     give the same answers as BASIC - it just makes the compiled
     code more efficient.  Note also that the use of the REM : LIST
     directive will tell you which form of division is being used.

6    If you assign a REAL value to an integer variable, the value
     is automatically rounded to the nearest integer.  This means
     that in many cases, INT is redundant and inefficient.  Note
     that this often true in BASIC as well, since all the functions
     and commands that require integers automatically round values
     to the nearest integer.

7    In general, using string is less efficient than using integer
     variables, so avoid using strings where possible.  A common
     example is in testing to see which key is pressed.  A poor way
     to do this (assuming we are in upper case; POKE 23768,8 to
     ensure this!):

     IF INKEY$ = "A" THEN ...
     IF INKEY$ = "B" THEN ...

     Better is:

          LET K = CODE INKEY$ (where K is POSINT)
          IF K = CODE "A" THEN ...
          IF K = CODE "B" THEN ...
     because it avoids the repeated INKEY$

     HiSoft BASIC recognises the form CODE "A" and converts this at
     compile time to the numeric code 65 but the CODE function does
     get in the way of other optimisations.  An even more efficient
     way would be to find out (either from the Appendix in the
     Spectrum manual or by PRINT CODE "A" etc) what numeric code
     for each letter is and use this (instead of CODE "A") in your
     program.

     Thus, best is:

     LET K = CODE INKEY$
     IF K = 65 THEN .... :REM "A"
     IF K = 66 THEN .... :REM "B"

     (But see note 11)

8    Even if you aren't using any integer variables you may want to
     declare a fake integer variable in order to save bytes.  If
     you don't have any INT or INT+ directives than all numbers
     that occur in the program will be stored in 5 byte format even
     if they are small integers.  By declaring a fake integer
     variable (use a variable name not in use in your program) you
     will change this so that numbers are stored in 2 bytes where
     possible.  But note that arithmetic operations between 2-byte
     numbers is integer arithmetic so that PRINT 0-65535 would give
     1! (See Conversion between Types).

9    It is more efficient to put all numbers in the program in
     their decimal form, for example use 0.5 or .5 instead of 1/2
     because the latter form would get coded as 1 divided by 2 and
     the division would have to be performed each time the number
     is used (this is also true of BASIC).

10   To reduce the number of bytes taken up by the compiled code,
     use subroutines as much as possible.  If even the simplest
     statement is used more than once, it will save bytes if you
     make it a subroutine (but you lose a tiny bit of execution
     speed).

11   HiSoft BASIC recognises and codes efficiently for the common
     forms IF...THEN GOTO and IF...THEN GOSUB in the case where
     only one statement (the GOTO or GOSUB) comes after the THEN. 
     Multiple statements after the THEN negate these optimisation
     so

     IF K=65 THEN GOSUB 1000 :REM "A"

     results in less efficient code than 

     IF K+65 THEN GOSUB 1000

12   If you are using computed GOTO, GOSUB or RESTORE statements
     (eg GOTO 100*n) then try to determine which lines are actually
     needed (eg if n is 1, 2 or 3 then only lines 100, 200 and 300
     are needed for the above example) and tell HiSoft BASIC this
     by means of the appropriate compiler directive.  This will
     save on bytes as well as making the compiled code run faster
     because there is a shorter list of line numbers to search
     through.  Even if you are including all the line numbers (eg
     with REM : GOTO : directive) you can speed up the search for
     the most commonly used line numbers by including an additional
     directive with the commonly used line numbers listed (eg if
     you have a REM : GOTO : directive but lines 3000 and 4000 are
     used frequently, then adding a REM : GOTO 3000, 4000 directive
     would be beneficial as then these line numbers would be at the
     start of the list).

What if it doesn't work?

If the compiled code doesn't do what it is supposed to do, the
first thing you should do is to remove all the compiler directives
relating to integer variables (just putting another REM in front of
them effectively removes them).  Then re-compile and try again.  If
it works now, you will know that the problem was to do with an
integer variable being assigned an out of range value or a DATA
statement lacking an INT.

If it still doesn't work, you should go back to the BASIC version
of the program (always keep the BASIC version!) and RUN it, doing
exactly the same things you did when trying the compiled version. 
If the BASIC version performs okay in the same situation then you
probably have found a bug in the compiler!

Error Messages

Invalid Compiler Directive

Check the section on compiler directives for the correct syntax.
You will get this message if you try to declare a variable twice.

Expecting a number

See the appropriate command in notes on compiled BASIC

Expecting an integer

You have used INT at the beginning of a DATA statement in which
there are non-integer values (see notes on compiled BASIC).

Not supported

See the appropriate command in Notes on compiled BASIC.

Non-existent line

You have a GOTO, GOSUB or RESTORE referring to a line that is
either non-existent or outside the region to be compiled.

Too many variables

The maximum number of simple numeric variables is 255.

No more space

This is a multi-purpose error message.  If you are using one of the
REM : GOTO, GOSUB or RESTORE directives then you will get this
message if storage of more that 450 line numbers is needed. 
Similarly, you may get this message with a GOTO, GOSUB or RESTORE
followed by an explicit line number if there is no more space for
storage of line numbers.  If you get this message with an IF
statement, it means that you have too many nested Ifs (maximum is
10).  If you get this message in reference to a variable, it means
that there is no more space for the storage of variable names.  In
this case you could remedy the situation by using shorter names.

Use *D *E

See Compiling large programs

Not enough room for M/C

The compiled code will not fit in memory below RAMTOP.  See
Compiling large programs.

Exec. address too high

The execution address you have specified (by means of a REM : USR
directive) does not leave enough room for the compiled code and its
variables to fit below RAMTOP.  Perhaps RAMTOP can be raised (by
means of CLEAR or Clear) to enable the code to fit.

DO NOT TEST

This message is a warning to you that the compiled code is not in
its proper position and hence you cannot execute it without first
saving it and then re-loading it to its proper address.  Note that
the code in its proper position may overlap HiSoft BASIC (making
HiSoft BASIC inoperable) - check the Memory maps.

No file space

This message occurs during the use of the Info command if either
there are too many variables (or too many long variable names) to
fit in the allocated space (unlikely) or (more likely) if there is
not enough space to create the file in the first place.  The file
takes up 2000 bytes in a fake BASIC line at the end of the program
area.  It is also possible that a BASIC error Out of Memory will be
encountered during the execution of a BASIC program (while Info is
in effect) because of the extra space taken up by the file.

The meaning of the dots and
colours

HiSoft BASIC makes three passes through the BASIC.  The first pass
(with magenta border) checks merely for unsupported commands and
picks out all the compiler directives and DIM statements.  The
second pass (with cyan border) is a dry run that determines how
long the compiled code will be and locates the destination address
of the GOTOs and GOSUBS etc.  The third pass (with white border) is
when the actual machine code is generated.

During compilation, HiSoft BASIC uses the display file (the area of
memory usually used to store the TV picture) to store its own
variables and other information such as the files of BASIC
variables and line references.  This information appears as dots on
your screen.  Furthermore, the calculator stack and the machine
stack are relocated to the attribute file (where the colours are
stored) during compilation.  The calculator stack appears at the
top of your screen while the machine stack starts one third of the
way from the bottom (these two stacks grow towards each other and
HiSoft BASIC is headed for trouble if they meet!)  So what you see
in the changing colours is actually in some sense the compiler's
thought process while the dots are its memory!

Appendix 1
Spectrum 128 and
Spectrum Plus 2 Version 

There are a number of differences between the Spectrum 128/Spectrum
Plus 2 and the 48K versions of HiSoft BASIC.  The 128 version
supports the PLAY command and additional VAL options, although the
code it produces will run on any Spectrum, whether 48K or 128K (on
a 48K machine the PLAY code simply is ignored).  The 128 version of
HiSoft BASIC takes up fewer than 500 bytes of user memory since
most of the compiler sits in the RAM disk.  This means that it is
possible to compile BASIC programs up to 40K in length.

Because of the different way the compiler commands are implemented,
it is not possible to break into machine code programs as in the
48K version, so you should be sure to provide an exit from your
program (eg IF INKEY$=S THEN STOP) before you test-run the compiled
code, or use the REM : BREAK directive.

It is worthy of note that if you are in the middle of editing a
program line pressing [TRUE VIDEO] and [INV VIDEO] together,
followed by [ENTER] will return the line to what it was before. 
The effect is similar to that of the EDIT key when in 48K mode. 
Note also that it is useful to switch to the lower screen (via the
128 mode EDIT key) before compiling so that you can issue the
command to SAVE your compiled code without the information
disappearing from the final screen.  Don't forget the usefulness of
the SAVE! and LOAD! commands, especially when you get the DO NOT
TEST message because the compiled code needs to be re-positioned.

You should note that there is a limit to the size of each line of
BASIC in the 128 version of HiSoft BASIC.  No line may exceed 1K in
length; this should not prove to be problematic.

An additional feature of the 128 version is that all of the extra
editing keys on the optional (and very rare) keypad have been
implemented on the standard keyboard (thanks to Toni Baker for her
original program, which appeared in ZX Computing Monthly), allowing
improved cursor movement when editing.  In the following table, TV,
IV and SS represent [TRUE VIDEO], INV VIDEO] and [SYMBOL SHIFT].

               Key-combination               Function

               IV and                  word left
               IV and                   word right
               IV and                  10 lines up
               IV and                  10 lines down
               TV and                  beginning of line
               TV and                   end of line
               TV and                  top of program
               TV and                  end of program
               SS and                  delete character left
               SS and                   delete character right
               IV, SS and              delete word left
               IV, SS and               delete word right
               TV, SS and              delete to start of line
               TV, SS and               delete to end of line

Furthermore, the 128 version institutes a keyboard buffer which
goes a long way towards solving the irritating problem of the
missed keypresses when you start typing after a message has been
displayed.
