Title: HiSoft Basic +3 Producer: HiSoft Machine: Spectrum +3 Format: DSK Version: 1.2 Instructions: HiSoft BASIC FAST, FLOATING-POINT COMPILER for all ZX Spectrum Computers -- HiSoft Basic Fast, Floating-Point ZX BASIC Compiler ====================================== System Requirements: Sinclair ZX Spectrum Computer Copyright (c) HiSoft 1986,88 1st Edition October 1986 2nd Edition February 1988 HiSoft BASIC was written by Cameron Hayne with routines by Andy Pennell Set using an Apple Macintosh(tm) and LaserWriter Plus(tm) with Aldus Pagemaker(tm) and Microsoft Word(tm). All Rights Reserved Worldwide. No part of this publication may be reproduced or transmitted in any form or by any means, including photocopying and recording without the written permission of the copyright holder. Such written permission must also be obtained before any part of this publication is stored in a retrieval system of any nature. It is an infringement of the copyright pertaining to HiSoft BASIC and its associated documentation to copy, by any means whatsoever, any part of HiSoft BASIC for any reason other than for the purposes of making a security back-up copy of the object code. -- Table of Contents ================= Getting Started and Tutorial 1 -------------------------------------------------- Introduction 1 Which Spectrum? 2 Making a back-up copy 2 Cassette Version 2 +3 Version 3 General 3 Tutorial - Try this First 3 HiSoft BASIC in Detail 13 -------------------------------------------------- How to Use HiSoft BASIC 13 HiSoft BASIC Commands 14 Summary of differences from Spectrum BASIC 17 Variables 18 Variable Types - General 18 The REAL type 19 The INTEG type 19 The POSINT type 19 Numerical Constants 19 Conversion between Types 19 Compiler Directives 20 REM : OPEN # 20 REM : CLOSE # 20 REM : LEN 21 REM : USR 21 REM : INT 22 REM : INT+ 22 REM : INT FN 22 REM : INT FN+ 22 REM : FN(INT) 22 REM : FN(INT +) 22 REM : GOTO 23 REM : GOSUB 23 REM : RESTORE 23 REM : LIST 23 REM : LINE 23 REM : LPRINT 24 REM : BREAK 24 REM : VAL 24 Notes on Compiled BASIC 24 Arithmetic Operators (+, -, * and /) 24 Comparison operators 25 AND, OR 25 LET 26 INPUT 26 FOR/NEXT 26 GOTO, GOSUB 27 RUN 27 RETURN 27 STOP 27 DIM 27 VAL, VAL$ 28 RESTORE, READ, DATA 29 PLOT, DRAW, CIRCLE 29 DEF FN 29 Including other machine code 30 Compiling large programs 30 Tips on Efficiency 31 What if it doesn't work? 34 Error Messages 34 Invalid compiler directive 34 Expecting a number 35 Expecting an integer 35 Not supported 35 Non-existent line 35 Too many variables 35 No more space 35 Use *D, *E 35 Not enough room for M/C 35 Exec. address too high 36 DO NOT TEST 36 No file space 36 The meaning of the dots and colours 36 Appendix 1 Spectrum 128 & Plus 2 Version 37 -------------------------------------------------- Appendix 2 Spectrum +3 Version 39 -------------------------------------------------- File Handling 39 Introduction to Serial Files 39 Write Files 40 Read Files 40 Notes on PRINT and INPUT 41 Supplementary BASIC Reference 41 Run-time Only version 42 Appendix 3 Technical Notes 43 -------------------------------------------------- Compiled Programs 43 Saving and Loading Arrays 43 Changes since version 1.0 44 Memory Maps 45 Appendix 4 The HiSoft BASIC Commands 46 -------------------------------------------------- -- 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 decimals, 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 than 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 compiled 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. -------------------------------------------------------------- Spectrum BASIC User Manual Page 1 -- 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 the +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 describes all versions of HiSoft BASIC; where there are machine-dependent features they will be marked with in the following way: |48 applicable to 48K machines only. |128 applies to the 128K 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. Whenever we need to refer to any of these keys they will be shown like this: #Compile# which means press the key-combinations 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 then press a key. -------------------------------------------------------------- Spectrum BASIC User Manual Page 2 -- 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 the 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 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 our master disk and type: COPY "A:*.*" TO "M:" [ENTER] Now insert a blank disk and type: FORMAT "A:" [ENTER] COPY "M:*.*" TO "A:" [ENTER] ERASE "M:" General In all cases, store your original cassette or disk in a safe, dry place away from heat, magnetic fields and kids! Only use the backup copy so that you can always make a new backup 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 let's start 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 a cassette version, load HiSoft BASIC by putting your backed-up tape in your tape recorder and type: LOAD "" [ENTER] and press PLAY on your tape player. -------------------------------------------------------------- Spectrum BASIC User Manual Page 3 -- If you have a disk copy of the compiler, insert your backup 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 that 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 (f you don't know what to do to #Compile# then look in Appendix 4). During compilation, HiSoft BASIC will pause twice, showing 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 in 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 compilers command #Run# detailed in Appendix 4 not the interpreter command RUN). -------------------------------------------------------------- Spectrum BASIC User Manual Page 4 -- You can test out the machine code now if you like. By the way, don't be alarmed at the fact that his 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 ratio 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 placed at what the Spectrum considers to be the top of your memory space (i.e. just below RAMTOP) and the RAMTOP is changed to be 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, NT2. 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 it's 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 in 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 (#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 L, 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 call tell HiSoft BASIC how much space to reserve for a string variable. -------------------------------------------------------------- Spectrum BASIC User Manual Page 5 -- 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: 8 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, and 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. It will work this time. Try out the newly-compiled machine code and you will see the spiral drawn more than 3 times faster. A few asides on the program: note, in lines 20-50, that 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 whenever 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 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: -------------------------------------------------------------- Spectrum BASIC User Manual Page 6 -- 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 (i.e. below) xxxxx. Now type: LOAD "EXAMPLE4" [ENTER] and LIST it. You will see that is is the same as EXAMPLE 3 but with additional lines at the end. We've already put in 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 ignore (although they do figure in the number given by 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 45000; 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 simply to redraw 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 the 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 1999 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 the 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 - i.e. 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. -------------------------------------------------------------- Spectrum BASIC User Manual Page 7 -- Remember that if you miss some information during compilation, you can always re-compile (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, then CLS and finally execute the third part of the compiled code to recall the spiral screen. Note that #Run# works only for the first entry point. You should find that now it 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 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 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 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 stopwatch 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 will see 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 POSTINT 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 is value never went out of the range of positive integers between 0 and 32767. The program STOPped at line 280 so what this is really telling us is that the variable i never goes out of that range in the 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. -------------------------------------------------------------- Spectrum BASIC User Manual Page 8 -- 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 long 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 we realised earlier but you can see how it could be useful when applied to more complex programs. When #Info# does is 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 the results. Anything you do between the #Info#s that affect 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 POSINTEG. 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 following program: 10 LET A=0 20 IF RND > .5 THEN LET A = .5 Do the sequence: #Info#, RUN, #Info# and then repeat the sequence a few times. You will notice that the variable A is sometimes listed 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 ever 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 program is explored. In this example, it would program suffice to do: #Info#, RUN, RUN, RUN, RUN, #Info#. Now type: LOAD "EXAMPLE5" [ENTER] This 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. -------------------------------------------------------------- Spectrum BASIC User Manual Page 9 -- You will see that it is a typical example of menu programming. Try to compile it and you will get line 50 at the top of your screen and the dreaded Not supported message at the bottom. Line 50 is what is called a computed GOSUB statement. This line number is not given explicitly but must be computed at run-time, so in order to compile such statements the compiler 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 this 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 correctly. 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 need for use in line 50 are the 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 recompile you'll find the code is 728 bytes long because we're now keeping only the info for those 3 lines rather than for all the lines of the program. For this short program it's not much of a saving, but for longer programs the savings 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 work 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 the 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 find 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). -------------------------------------------------------------- Spectrum BASIC User Manual Page 10 -- To get the program to work in BASIC you will have to change both of the 8192s to 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 executing it. 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 in sorting 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 to sort strings instead of number. If you compile them, you will find that the compiled versions are up to 19 times faster! -------------------------------------------------------------- Spectrum BASIC User Manual Page 11 -- -------------------------------------------------------------- Spectrum BASIC User Manual Page 12 -- HiSoft BASIC in Detail ====================== How to Use HiSoft BASIC ----------------------- 1. Using your backup 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 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 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 compiler directives are optional. 5. RUN your program to make sure that 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 and may even crash. Conversely, if you program works in BASIC, you can expect the compiled code to do the same. It is a good idea to SAVE your 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. -------------------------------------------------------------- Spectrum BASIC User Manual Page 13 -- 7 SAVE the compiled code. The compiled code is 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 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 ay 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 code (both in decimal and hexadecimal) are shown. Compilation pauses until you press a key. At completion 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 in 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 -------------------------------------------------------------- Spectrum BASIC User Manual Page 14 -- #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'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 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 appear 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 #Info#s you can do anything you normally do in BASIC. The bottom of the screen will go BRIGHT to short 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 values. There is a small possibility that #Info# may give incorrect results because it only looks at the variable every 1/50 of a second. For example, with the one-line 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. -------------------------------------------------------------- Spectrum BASIC User Manual Page 15 -- #Erase# Removes the BASIC program and variables from memory without affecting HiSoft BASIC or any machine code program 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 a NEW). #Break# |48 Pressing both [SPACE] and [CAPS SHIFT] (the normal BREAK procedure) will stop whatever the 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, i.e. 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 continuos 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 (e.g. X[I,J,K]) are not supported. |48 4. VAL stringvariable (eg. VAL A$) is not supported. (but VAL "expression" is okay). |+3 |128 4. Use the REM : VAL compiler 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, 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). -------------------------------------------------------------- Spectrum BASIC User Manual Page 17 -- 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. |48 |128 10. 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 program. 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 variables 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 amount of storage 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 it 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, variable are of two types: numeric and string. HiSoft BASIC has 3 numeric types as well as strings. The default type 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). -------------------------------------------------------------- Spectrum BASIC User Manual Page 18 -- The REAL type This is the same as the BASIC numeric type; i.e. REAL variables can take on all floating-point values between -1.7 E 38 and +1.7E 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 BASIC. The INTEG type The values taken by an INTEG variable are restricted to integers (who 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 to positive integers (whole numbers) between 0 and 65535 inclusive. POSINT variables take up 2 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 use of the 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 string variable, followed by the text of the string (or strings). Numerical Constants ------------------- Number may be in ordinary of in scientific notation. For the purpose of storage format and evaluation of expressions, the type of a number is taken to be that of 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 than 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. -------------------------------------------------------------- Spectrum BASIC User Manual Page 19 -- For example, if the value 40000 is assigned to INTEG variable I, it will end up that I = 40000 -65536 = -25536, whereas if -40000 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. The logic of all this is shown by the diagram below which shows the different interpretations one can put (depending on type declaration) to a given internal representation of a number (shown in hexadecimal). {Unable to reproduce image in ASCII} 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 is 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 (i.e. must have a lower line number than) the first REM : OPEN # -------------------------------------------------------------- Spectrum BASIC User Manual Page 20 -- 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 than 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 then 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 then 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. -------------------------------------------------------------- Spectrum BASIC User Manual Page 21 -- 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. E,G, 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 type POSINT. REM : INT FN Means that certain defined functions will return values restricted to integers (-32768 to 32767). E.g. REM : INT F, 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 +) Tells HiSoft BASIC that certain one-letter variables are to be considered as type POSINT when used as dummy variables in a DEF FN statement. E.g. REM : FN (INT + I,J,K) tells HiSoft BASIC that the dummy variables I, J and K are to be considered of 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. -------------------------------------------------------------- Spectrum BASIC User Manual Page 22 -- REM : GOTO REM : GOSUB These two directives are completely equivalent. If you use either of them, then GOTO and GOSUB expression (e.g. 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 : (e.g. 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 then RESTORE expression (e.g. 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 (e.g. 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. Addresses are given in both decimal and hexadecimal. REM : LINE Tells HiSoft BASIC to print the address 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. -------------------------------------------------------------- Spectrum BASIC User Manual Page 23 -- |48 REM : LPRINT 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. |+3 |128 REM : BREAK 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 is particularly useful whilst debugging. |+3 |128 REM : VAL 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.667 instead of the 1 you would get with integer division (but see note 5 in Tips on Efficiency). -------------------------------------------------------------- Spectrum BASIC User Manual Page 24 -- 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-of-range values this way. No error message will be generated - you may just get 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 of 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 then 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 POSTINT. 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. -------------------------------------------------------------- Spectrum BASIC User Manual Page 25 -- 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 I. LET I=R would round the value of R to the nearest integer before assigning it to I; e.g. if R=3.14 then I would be 3. If the value of R is bigger in absolute value than 65535 (so it won't fit in two bytes) you would get the error message Integer out of range. However, LET I=40000.3 (a REAL value) or LET I=40000 (a POSINT value) would generate no error message even though the value is 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 expression involving 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. -------------------------------------------------------------- Spectrum BASIC User Manual Page 26 -- 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 VAL "number". 2. The line being referred to must actually exist within the portion of BASIC being compiled (if you have trouble with this, put a REM statement with the appropriate line number). RUN RUN is not supported but it can be replaced by a GOTO the first line of your program. You may want to put in a CLS 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 the 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. -------------------------------------------------------------- Spectrum BASIC User Manual Page 27 -- 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 e.g. | | 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. |+3 |128 | By default the compiled 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 a similar way 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 in the strings will be the interpreters idea of that variable, not the compiler's. This can be used to pass values from interpreted code to compiled code. -------------------------------------------------------------- Spectrum BASIC User Manual Page 28 -- 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 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 23360 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 settings of these default attributes is omitted in the compiled code. This means that (in the 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. -------------------------------------------------------------- Spectrum BASIC User Manual Page 29 -- 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 other machine code while HiSoft BASIC is still present, then the other machine code must not overlap HiSoft BASIC (see Memory Maps). Note that 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 also 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 high memory (in the 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 (i.e. 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 variables, and the code can be tested in place. However, if there is space for only the compiled code, then the variables are omitted and the message DO NOT TEST is given. If HiSoft BASIC finds that 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 #CompileData# before #CompileNonData#). -------------------------------------------------------------- Spectrum BASIC User Manual Page 30 -- 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 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 (i.e. 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 that 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 used 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 directive. 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 this case and computes its address at compile-time). Some of the simple cases recognised by HiSoft BASIC: o multiplication or division by a power of 2 o changing an integer variable by 1, 2 or 3 o 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 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 \/ bottom 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 to solving the irritating problem of the missed keypresses when you start typing after a message has been displayed. -------------------------------------------------------------- Spectrum BASIC User Manual Page 38 -- Appendix 2 Spectrum +3 Version =================== This appendix details the additional features of HiSoft BASIC for the Spectrum +3 and should be read in conjunction with the rest of the manual. Some of the extra features have already been mentioned in the previous Appendix. This version takes up around 850 bytes of user memory, the rest of the compiler sits in the RAMdisk, deleting whatever files were originally there. Don't forget the usefulness of the RAMdisk for saving and loading after the compiler is initialised especially when you get the DO NOT TEST message because the compiled code needs to be re-positioned. The +3 version of HiSoft BASIC supports the PLAY command and additional editing keys as described in the previous appendix. File Handling ------------- Unfortunately Plus 3 BASIC does not allow file handling, as available with microdrives and many disk interfaces. HiSoft BASIC allows compile programs to open and close serial access files and read & write data to them. It uses the concept of streams, discussed in Part 22 of the Plus 3 manual, but extends them to include disk streams. HiSoft BASIC file handling uses stream numbers 4 to 15 inclusive and each stream has the potential to be open for input or output. Introduction to Serial Files File handling under HiSoft BASIC is very similar to that with microdrives - if you are already familiar with those devices you can probably skip this section. A file is a block of data that can have items added to it, and read from it. The type of files available from HiSoft BASIC are serial files, with which you must read and write in a particular order. For example, if you wanted the 10th item, you must read the first 9 items of data before you can read the 10th. To store data on a +3 disk, other than by saving it as an array (not supported by the compiler) a special channel name is used in the OPEN statement. OPEN #s,f$ which will create a new stream associated with the file f$. The type of the stream opened depends on the first character of f$. -------------------------------------------------------------- Spectrum BASIC User Manual Page 39 -- Write Files These are denoted with filenames starting with a - sign, and will be deleted if any existed previously, or a new file created. The main command for sending data to a disk file is the PRINT# statement, which works in a similar way to the normal PRINT statement, but prints the data to the disk. For example compile and run this program 10 REM : OPEN # 100 OPEN #4,"-testfile" 110 FOR i=1 TO 500 120 PRINT i;" "; 130 PRINT #4;i 140 NEXT i 150 CLOSE #4 You may have expected the disk drive to operate most of the time, as it prints each number, but it doesn't. After the OPEN# command the drive will be accessed while it checks for that particular file. What it also does is to create an area of memory called a buffer, which it uses as a temporary store for characters. When it comes across a PRINT# statement it will store the characters in the buffer, but it won't write them to the disk unless the buffer is full. When it does fill the drive will operate as it writes the buffer onto the disk and clears the buffer, ready to wait for some more. A CLOSE# statement is absolutely essential when using write files because, if the buffer is not full and you don't do a CLOSE#, the last data you thought you sent with a PRINT# will never get onto disk, and the file will be incomplete. A CLOSE# will send what is left in the buffer to disk, then remove the buffer from memory. Read Files Once a write file that has been created with OPEN# and has had data sent to it and been CLOSEd, it becomes a read file and is visible on the disk CATalog. To read data from it, it must be opened, again using OPEN' with the same syntax as before, but with the first character of the filename being a + sign. The actual reading of data is usually done with the INPUT# statement. For example, to read in the file created by the previous program, enter the following 10 REM : OPEN # 100 OPEN #4,"+testfile" 110 INPUT #4,i 120 PRINT i;" "; 130 GOTO 110 This initially creates a read file on stream 4. The variable i is read from the file (line 110) and printed on the screen. The program then loops back but will eventually stop with an End of file error. -------------------------------------------------------------- Spectrum BASIC User Manual Page 40 -- There is another way of reading characters from a file, apart from using INPUT# - the INKEY$# function. Unlike INPUT#, which assembles a sequence of characters until it reaches the end of a line and then copies it into a variable, INKEY$#n just reads a single character. Unlike the normal INKEY$ function it will 'wait' for a character - it will not normally return a null string ("") as a result. When you have finished reading a file, it should be ended with a CLOSE# statement. If you leave a stream open you will not be able to OPEN# it again, instead the error Invalid stream will occur. Notes on PRINT and INPUT You should be very careful when using PRINT# and INPUT# statements with disk files because of the effect of the separators used between arguments. When printing to the screen or printer, a comma (,) will take you to the start of the next half-line, and an apostrophe (') will take you to a new line. However, there are no lines in disk files and printing a comma to a file (e.g. PRINT#4,A,B) will actually send the control code for comma i.e. a CHR$ 6. Similarly an apostrophe will send a CHR$ 13 (a new-line code). Character code 6's can confuse then +3 when you try to use INPUT# (but not INKEY$#). Separators in INPUT# statements can also create unexpected problems - if you try 100 OPEN #4,"+testfile" 110 INPUT #4;a$,b$ (assuming the testfile exists) you will get an error as the file was opened for read, but the comma in line 20 will try to send a CHR$ 6 to the file, producing the error File not open (or wrong access). The interpreter can also get confused if you use quote marks (") in data files, but the compiler doesn't as it always uses the INPUT # LINE form. Supplementary BASIC Reference OPEN #stream,file$ This will attempt to open the given stream with a particular name, and the stream must not already be open. If stream is less than 4 then only the names "K", "S" and "P" will be accepted, but if it is greater than 3 then +3DOS filenames are allowed, preceded with a - sign for a write file or + sign for a read file. CLOSE #stream This statement should be used after a files contents have been read or written to. If a stream number of 0 is specified then all open files will be closed, useful for putting at the beginning of programs as attempting to open a stream which is already open will give an error. If you attempt to CLOSE# a stream greater than 4 from the interpreter you will crash the machine. -------------------------------------------------------------- Spectrum BASIC User Manual Page 41 -- PRINT #stream;items This works in a similar way to the normal PRINT statement but sends the data to a particular disk file, which must be a write file. INPUT #stream;items This reads variables of any type from the given stream, which must be a read file. All variables should be separated only by semi-colons in the statement. INKEY$ #stream This is a function which will return the next character from a disk file, which must be a read file. If you reach the end of the file it returns a null-string ("")m which is different to the result with microdrives. If you try to read past the end of the file an error will occur. Run-time Only Version A compiled program that uses OPEN# or CLOSE# will not run on a +3 without the compiler itself being present - it will crash. If you wish to sell or distribute a compiled program that uses either of these commands you can supply the file RUNTIME which contains the necessary routines. This is a CODE file that should be loaded and executed at location 60000, and can be overwritten afterwards as it copies itself into low memory, occupying around 200 bytes before the CHANS area. A compiled program can then be loaded and executed. -------------------------------------------------------------- Spectrum BASIC User Manual Page 42 -- Appendix 3 Technical Notes =============== Compiled Programs ----------------- Programs compiled with this version of HiSoft BASIC will run on any of the Spectrums listed below, unless they use the OPEN# or CLOSE# statements. We cannot guarantee that compiled programs using the PLAY or COPY statements will run on future Spectrum machines as they use illegal entry points into the +3 ROMs which may change in the future. Machines currently supported are: Spectrum 48k (and 16k subject to memory); Spectrum Plus; Spectrum 128; Spectrum +2; Spectrum +3. Saving and Loading Arrays ------------------------- The SAVE and LOAD commands are not implemented in HiSoft BASIC but arrays of compiled variables can be saved and loaded using the following method: The method is to use the REM : LIST directive to reveal the address of the array you wish to save or load, and do the command from within the interpreter but saving the array as a CODE file. Here is an example of a program which creates an array of reals, fills it with silly numbers, then saves it onto microdrive: 10 REM this is interpreted 20 RANDOMIZE USR addr1 30 SAVE *"m";1;"array"CODE addr2,20*5 40 STOP 100 REM this is compiled 110 REM : LIST 120 REM : OPEN # 130 DIM a(20) 140 FOR i=1 to 20 150 LET a(i)=i 160 NEXT i -------------------------------------------------------------- Spectrum BASIC User Manual Page 43 -- addr1 should be replaced with the calling address of the compiled code, and addr2 should be the address given by the compiler for the array a(). Line 30 has as the file length the expression 20*5 - the 20 is the size of the array, and the 5 is because each real number is 5 bytes. If the array was of integers or posints the length would be 20*2, as they each take 2 bytes. String arrays are more complex - the length would be the product of the two dimensions plus 2, so if the array was a$(10,5) then length would be 10*5+2. Here is a program to read the same away and print its contents: 10 REM this is interpreted 20 LOAD *"m";1;"array"CODE addr3 30 RANDOMIZE USR addr4 40 STOP 100 REM this is compiled 110 REM : LIST 120 REM : OPEN # 130 GOTO 140: DIM a(20) 140 FOR i=1 TO 20 150 PRINT a(i), 160 NEXT i In this program addr3 is the address of the a() when compiled this time, and addr4 is the execution address of this compiled program. The GOTO on line 130 is to stop the DIM statement being executed, as it zeros the array. The DIM is still required so the compiler knows the size of the array. There are a few things to be careful when using this method: Firstly you have to change the addrs every time you re-compile as they will change each time. Secondly the DIM statements must be the same in both programs. Thirdly the types of the arrays (real, integer, posint) must also be the same. Changes since version 1.0 ------------------------- The following bugs have been fixed since version 1.0: Better error detection to do with mis-matched FOR-NEXT loops; Occasional programs in AND, OR and SGN operators; Certain array element comparisons; Keyboard scanning Saga-compatible; IF-statement sometimes eating memory; 48k version now Disciple compatible; Printer support with interfaces not properly supporting TAB control codes; When making a back-up note that the 48s referred to in the original manual are not relevant to this version. The REM : VAL option allows greater control over VAL and VAL$ on 128k machines. -------------------------------------------------------------- Spectrum BASIC User Manual Page 44 -- Memory Maps ----------- |-----------------------| | User-defined graphics | |-----------------------|-65367 |----------------------| | your machine code | | string arrays | |-----------------------|-RAMTOP |----------------------| | compiled code | | simple string arrays | | | |----------------------| |-----------------------| | numeric arrays | | your BASIC program | |----------------------| | | | FOR/NEXT limit & | |-----------------------|-PROG | STEP values | | channel info | |----------------------| |-----------------------|-CHANS | simple numeric Real| | microdrive maps | | variables Integer| /|\ |-----------------------| |----------------------| | m/c variables | HiSoft BASIC | | DATA items | | | |----------------------| |-----------------------|-23792 (48/128) | Runtime routines | | system variables |\23734 (+3) |----------------------| |-----------------------| | Main code | | printer buffer | | | /|\ |-----------------------| |----------------------| | m/c | attribute file | |-----------------------| | display file | |-----------------------| | | | ROM | | | |-----------------------|-0 At Compilation Time At Run Time -------------------------------------------------------------- Spectrum BASIC User Manual Page 45 -- Appendix 4 The HiSoft BASIC Commands ================ The various commands (such as #Compile#, #Info#) used throughout the manual are reached in differing ways on the 48K and 128K (including +3) computers. On the 48K machines you type an asterisk (*) followed by a command letter whilst on the 128K computers you type [TRUE VIDEO] [INV VIDEO] together and this brings up a menu from which you select the appropriate command. For quick reference here are the commands available and how to reach them ([TV IV] means press [TRUE VIDEO] [INV VIDEO] together and wait for the menu): Command |48 |128 and |+3 --------------------------------------------------------- #Compile# *C [TV IV] C #Run# *R [TV IV] R #Clear# *X [TV IV] X #Info# *T [TV IV] T #Erase# *ERASE [TV IV] E #CompileData# *D [TV IV] D #CompileNonData# *E [TV IV] E #Print# n/a [TV IV] P -------------------------------------------------------------- Spectrum BASIC User Manual Page 46 --