LASER BASIC COMPILER by Chris Melling INTRODUCTION The Laser BASIC Compiler is aimed at the user who has written a program with Laser BASIC and wishes to produce a stand alone program that will run independently of the extended interpreter. Programs compiled with this compiler can be marketed in any way chosen and there are no restrictions or royalties to pay. The compiler can be used to compile normal Spectrum programs which do not use any Laser BASIC commands but the increase in speed will probably only be about a factor of 2 or less. The compiled code is more compact than the BASIC source but there is an overhead due to the run-time code. Laser BASIC programs which will run under the extended interpreter will never be too large to compile. TAPE MAP Filename Loading Instructions Side A: "COMPCODE" LOAD ""CODE "LOADER" LOAD "" "RTCODE" LOAD "RTCODE"CODE Side B: "DEMO" LOAD "" COMPILING A PROGRAM To compile a program, only the Compiler and the program to be compiled need to be in memory. Moreover, it is essential to ensure that Laser BASIC is not resident at the time. Thus, if a Laser BASIC program has been typed in, then it must first be saved, the machine re-set, and the program re-loaded. N.B. No changes may be made to a line of a program containing Laser BASIC commands, without the extended interpreter resident. Before loading the Compiler, it is first necessary to force the top of BASIC memory to below its loading position with the command: CLEAR 59799 The Compiler may then be loaded by typing: LOAD ""CODE or LOAD "COMPCODE"CODE If the program to be compiled is not already in memory, then it must be loaded in the normal way. Compilation may then be initiated with the command: RANDOMIZE USR 59800 Whilst the Compiler is executing, erroneous lines and colours will appear on the screen; this is designed to give you heart failure and to make you believe that it has crashed! Not so. The effect is due to the Compiler using screen pixel and attribute memory as temporary work space. When the process is complete, the screen will clear and the familiar OK message will be displayed, together with the last line and statement number which was compiled. THE FORM OF A COMPILED PROGRAM As the Compiler runs, it over-writes your BASIC program with compiled code, and upon successful completion of compilation the compiled code appears to BASIC exactly like any other BASIC program. Thus it may be SAVEd and LOADed in exactly the same way as any ordinary BASIC program. Do not attempt to alter in any way a compiled program: by typing new lines, deleting lines or editing lines; or by MERGEing two compiled programs - this may be affected by MERGEing two BASIC programs, and compiling the whole. If you try to LIST the compiled program, you will only see a few lines of BASIC before the listing stops. The first two of these are inserted by the Compiler, and appear irrespective of the program which was compiled. The first should appear: 0 PRINT 0 This statement, which is never executed, contains some hidden information which the run-time package needs for the successful execution of your program. The second line should appear: 1 RANDOMIZE USR 59800 and is there to facilitate the automatic running of a compiled program - more of which later. If your program contained any user-defined functions, then you would observe a list of them on a third line, numbered arbitrarily 0. They will be all squashed together on the same line. Note that this does not apply to Laser BASIC procedure definitions, which are compiled separately. In addition, if your program contained any DATA statements, then these would appear on a further line 0, after any user-defined functions. All DATA Items are strung together as one large DATA statement, but note that this does not affect the operation of any RESTORE statements, which will work as intended. RUNNING THE COMPILED PROGRAM With the compiled program still in memory (or it may be loaded now or later), the Run-time package must now be loaded. Assuming the CLEAR 59799 is still operative (do it now if in doubt), this is performed with: LOAD "RTCODE"CODE At this stage, if the program requires any SPRITES to be pre-loaded, then these should be LOADed exactly as would be done for a normal LASER BASIC program. If the compiled program is not already resident, then it should now be loaded (exactly like a normal BASIC program). The program is now ready to be executed. This is done with the command: RANDOMIZE USR 59800 or GOTO 1 remembering line 1 of the program mentioned above. The program will now execute, but note that it cannot be broken into using the BREAK key (apart from during a tape or microdrive operation). PRODUCING A STAND-ALONE PROGRAM If you wish to market a program which you have written in Laser BASIC then it must be compiled. In addition, you would normally wish it to be a stand alone program, so the end user just has to type LOAD "" or whatever, and the whole things runs automatically. This is achieved using a loader program (which auto-runs), which loads in the run-time package, any sprites, and finally the compiled program, which can also be made to auto-run. Such a loader program, which you can use to build on, is included on side one of the Compiler tape. It appears below: 10 CLEAR 59799 20 LET spst= 30 LOAD "RTCODE"CODE 40 LOAD "SPRITES"CODE spst 50 LET t=INT (spst/256): POKE 62464,spst-256*t: POKE 62465,t 80 POKE 62466,255: POKE 62467,220 70 POKE 56575,0 80 LOAD "" Line 10: Executes the necessary CLEAR to make room for the run-time package. Line 20: For you to set the appropriate value for the start of sprites, as told by the sprite generator program. Line 30: LOADs the run-time package. Line 40: LOADs the sprites. Line 50: POKEs in the start of sprites. Line 80: POKEs in the end of sprites. See Laser BASIC manual. Line 70: POKEs in the end of sprites character. Line 80: LOADs the final compiled program. NOTE: If no sprites are used, then lines 20,40,50, 60 and 70 can be omitted. It merely remains for you to fill in the correct value of spst, and to SAVE the necessary set of files on the tape, ensuring that the loader and the compiled program are SAVEd to auto-run. For the loader this will entail: SAVE "loader"LINE 10 and for the compiled program: SAVE "" LINE 1 - you will recall that line 1 is the compiler generated line, which calls the run-time package to start execution of the compiled program. An example of this process appears later in the manual when instructions are given for you to produce a compiled version of the Invader Cube game. RESTRICTIONS ON THE BASIC TO BE COMPILED The design of this Compiler has meant that certain restrictions have had to be imposed, and some assumptions made. These are itemised below: a) Computed GOTO, GOSUB, RUN and RESTORE statements should not be attempted, as the following expression as evaluated at compile-time will be used. i.e. all numeric variables will have value zero. and all strings length zero. b) An attempt has been made to accommodate computed DIM statements, and those with dimensions which evaluate to zero (an impossible value under Sinclair BASIC) will not be declared until run-time. This means. however, that statements such as DIM a(n+12) will not be declared as might have been envisaged. as the dimension expression will be evaluated to 12 and hence the array will be declared as such. c) There is no restriction on the number of times an array may be redeclared, but you will appreciate that the first declaration is done during compilation, and therefore that statement is not copied to the compiled program. Thus if a jump is made back to the start of a program to before the place where your first DIM was, for a particular array, it will not be there to be executed on the second pass. This can be got round (if you must use array redeclaration) by having an extra declaration of that array right at the beginning of the program, before any "re-start" jumps. d) CLEAR statements are not allowed at all and produce a compiler error message - see below. e) RUN statements operate as in BASIC except that they do not clear the variables. f) MERGE, CONTINUE, LIST and LLIST commands are not allowed and produce a Compiler error message - see below. g) Do not try and CONTINUE a crashed or STOPped compiled program. h) OPEN# and CLOSE#. These will only work to streams "S", "K" and "P". Microdrive datafile handling is not supported. i) LOAD, SAVE and VERIFY. These all work as normal, except if a BASIC program is LOADed then it is assumed that it will be a compiled program and the run-time package will attempt to execute it from the start. USING THE COMPILER WITH MICRODRIVES The Compiler and run-time package are both compatible with microdrives, except that microdrive datafiles are not supported in compiled programs. All the examples shown In this manual may be converted to microdrive just by adding the infamous " *"m";; " between the command and the filename. ERROR MESSAGES Illegal statement found This is produced by the Compiler if one of the statements CLEAR, MERGE, LIST, LLIST or CONTINUE is found. Procedure definition nesting error This is produced by the Compiler if two procedure definitions are found without an intervening .RETN. You cannot nest procedure definitions. .RETN without DEF FN #error This is produced by the Compiler if a .RETN statement has been found without a corresponding procedure definition. Procedure not defined This is produced by the compiler if a call is found to a procedure that has not been defined. Note that you don't have to declare a procedure before it is used. Program not compiled This is produced by the run-time package if, when it is first called, the beginning of a program is not recognisable as a compiled program. Execution of a compiled program is as susceptible to the normal run-time errors as any BASIC program. However, the run-time package for the compiler is unable to tell you where any such error occurs when it happens. Further, you should ignore the numbers which appear alongside a Sinclair error message. Thus you should ensure that a program is fully tested and debugged before you compile it. After receiving any error message, the program will be partially or completely compiled, and so you must re-LOAD the original BASIC program to make the necessary alterations before re-compiling. MEMORY MAP The Compiler and run-time package both LOAD in at 59800. The run-time package occupies memory from that point right up to the top of memory, and once you have LOADed it, you may SAVE it with: SAVE "RTCODE"CODE 59800,65536-59800 The Compiler ends at 62856, occupying some 3057 bytes, and you may re-SAVE it with: SAVE "COMPCODE"CODE 59800,3057 Hence, when using either package, the need for the CLEAR 59799. A compiled Laser BASIC program will appear in memory as follows: +------------+--------------+- - - - -+------------+--------------+-----------+ | Program | Variables | | SPRITES | m/c stack | RTCODE | +------------+--------------+- - - - -+------------+--------------+-----------+ PROG VARS ELINE spst spnd-56575 59800 65535 Note for machine code programmers Simple arithmetic will tell you that the gap between the top of sprites and the run-time code is some 3k or more. Now under normal circumstances the stack will need less than 256 bytes, and so the rest of this space may be used for machine code routines, either by working up from the top of sprites, or by CLEARing nearer the sprites and using the area after that point up to the run-time code. Obviously there is nothing to stop you from CLEARing under the sprites and using the whole gap: and so on. INSTRUCTIONS FOR PRODUCING A STAND-ALONE COMPILED PROGRAM FOR THE INVADER CUBE GAME On tape two side two of your Laser BASIC package is a game entitled Invader Cube. Here follows the instructions for you to produce a compiled stand-alone version. The tape map of the standard game is as follows: The loader (to auto-run) The loading screen Three programs comprising Laser BASIC The sprites for the game The game program itself (to auto-run) For a compiled version, the tape map will need to be: The loader (to auto-run) The loading screen The Run-time package The sprites The compiled game program (to auto-run) To produce this you will need a blank side of a cassette. First the loader program has to be designed. We will use the loader provided as part of the compiler package as a starting point. At present it appears: 10 CLEAR 59799 20 LET spst= 30 LOAD "RTCODE"CODE 40 LOAD "SPRITES"CODE spst 50 LET t=INT (spst/256): POKE 62464,spst-256*t: POKE 62465,t 80 POKE 62466,255: POKE 62467,220 70 POKE 56575,0 80 LOAD "" Now, the value of spst needs setting to that required for the game sprites which is 53780. Also, an extra line needs to be inserted to LOAD the loading screen: 25 LOAD "scrn"CODE In addition, to prevent the destruction of the screen whilst loading is in progress, the following line needs to be added at the start: 5 INK 0: PAPER 0: BORDER 0: CLS and PRINT AT 0,0;: inserted before each of the load statements. As you are altering the final line - that to load the game - you can insert the name which it will be saved as, say "cgame": 80 PRINT AT 0,0;: LOAD "cgame" So the final loader appears: 5 INK 0: PAPER 0: BORDER 0: CLS 10 CLEAR 59799 20 LET spst=53780 25 PRINT AT 0,0;: LOAD "scrn"CODE 30 PRINT AT 0,0;: LOAD "RTCODE"CODE 40 PRINT AT 0,0;: LOAD "SPRITES"CODE spst 50 LET t=INT (spst/256): POKE 62464,spst-256*t: POKE 62465,t 80 POKE 62466,255: POKE 62467,220 70 POKE 56575,0 80 PRINT AT 0,0;: LOAD "cgame" This should now be saved on your new tape so that it will auto-run: SAVE "loader"LINE 5 From looking at the tape map above, and the loader which you have just SAVEd, it is clear that the next thing which needs SAVEing after the loader is the loading screen. This must be got from the original game tape. To do this you must LOAD the second file, called "SCREEN": INK 0: PAPER 0: CLS LOAD "SCREEN"CODE When it is in, and the OK message appears, type: SAVE "scrn"CODE 16384,6872 to SAVE the screen after the new loader on your new tape. STOP the tape when the operation is complete, and reset the machine. Referring again to the tape map, we see that the run-time code must now be SAVEd. So this must be LOADed from the compiler tape - not forgetting the CLEAR: CLEAR 59799 LOAD "RTCODE"CODE Now this may be SAVEd to the new tape after the screen with: SAVE "RTCODE"CODE 59800,65536-59800 STOP the tape when this is complete. Back to the tape map, and we see that the sprites for the game must be next SAVEd. Again these must be got from the original game tape, and you will see from that map that they are situated after the three CODE files which comprise Laser BASIC. The command: LOAD "SPRITES"CODE 53780 will skip through these and LOAD the sprites. Then SAVE them to your new tape after the run-time code with: SAVE "SPRITES"CODE 53780,56576-53780 Finally, the game itself must be compiled, and SAVEd after the sprites. Ensuring that Laser BASIC is not resident, LOAD the game with: LOAD "INVADER" It will auto-run once it is in, but will soon crash out when it comes to a Laser BASIC command, which of course it cannot understand without the Extended BASIC resident. When this occurs, LOAD the Compiler in the normal way: CLEAR 59799 LOAD "COMPCODE"CODE Compile the program with: RANDOMIZE USR 59800 and SAVE the result on your new tape with: SAVE "cgame" LINE 1 remembering the auto-run facility. You can now rewind the tape, reset the machine, and type: LOAD "" and the compiled version of Invader Cube will LOAD and RUN. Microdrive users should note that this procedure will work equally well on that media, just by changing all the SAVE commands to SAVE *"m";1; and the LOADs in the loader program to LOAD *"m";1; .