Writing Programs

There have been many books and magazine articles written about how to write SuperBASIC programs, so we do not intend to cover the basic principles here. The main section detailing the various keywords available to the SuperBASIC programmer contains many useful examples and we suggest that you work through those examples, making sure that you understand how they work and trying to improve them if possible.

In this section, we look at some of the major problems which can face the SuperBASIC programmer and hope to provide some guidance as to how you can overcome those problems and ensure that the programs which you write can be used successfully on all implementations of the QL’s operating system.

Compiling SuperBASIC Programs

Digital Precision’s Supercharge and Turbo compilers are not able to compile some keywords` (eg. those which allow arrays as parameters) while Liberation Software’s QLiberator can compile every additional keyword which makes sense in a compiled program. Although programs compiled with Qliberator will be slower than those compiled with Turbo, the fact that Turbo has not been updated for a number of years (and still contains certain bugs) means that Qliberator may be a better option. This is a matter of fact which we consider worth mentioning for the benefit of Supercharge and Turbo users, it is not intended as a hidden advertisement for QLiberator.

Note

While the above was certainly true when the first paper version of this manual was printed, progress has been made, as the following correction from George Gwilt explains:

For versions of TURBO earlier than 4.21 machine procedures or functions that modify their parameter values, process arrays (other than single strings), manipulate the stored program text, or rely on other interpreter data structures (such as the name table and name list) will not work when compiled. The majority of add-on commands do not do this, and consequently work perfectly.

For TURBO v4.21 and later none of these restrictions apply except for the reliance on the name list.

On the other hand, if you are a Minerva freak, use the Pointer Environment in your programs or want to ensure that your programs will run on SMSQ/E, then you might just find that QLiberator is the better compiler.

As you look through the book, you will find that many keywords act differently on the various implementations of SuperBASIC. To overcome this problem, we suggest that programs which are designed to run on various implementations of the QL, should be compiled, as subject to the comment below, Compilers ensure that programs will run on any system (provided that all keywords used by the program are available on that implementation).

If you want to write programs which use built-in functions only if they are available, you will need to use Qliberator which does not report an error until you try to use an undefined keyword (Turbo and SuperCharge both refuse to load the program in this instance).

Together with the use of the VER$ function, you can easily write programs which work on all implementations of the QL making use of extra facilities which may be available on some versions of the operating system.

One thing of which you must be careful when compiling programs to run on other implementations is the various notes and warnings given for some keywords. Some keywords cannot be compiled and others may have bugs on various implementations which are not fixed by the compiler (refer to the compiler’s manual to see if they rectify the bugs). One of the main culprits of this is the CURSOR command which will reject the use of five parameters on pre-MG ROMs.

Turbo does tend to include its own code to overcome any incompatibility problems with standard QL ROM keywords, whereas Qliberator tends to use the native routines on the operating system on which it is running.

Another thing to bear in mind when writing programs for use with a compiler is that you should really ensure that the program opens all of its own windows and does not assume that any channels are already open. You will also need to remember to DIMension any strings used by the program to ensure that the program works the same way as it does under the Interpreter when compiled (see DIM for an explanation of the way in which strings are treated in various circumstances).

It is also useful to include your own error trapping routines in a compiled program (such as WHEN ERRor) - most compiler error messages are very unhelpful when seen by a user of a program and in particular, there is a problem with programs compiled with Turbo and Supercharge in that they do not wait for the user to press a key after reporting an error before stopping the compiled program. This is fine on a standard QL, as the final display of the program is left on screen - however, under the Pointer Environment, unless the program is started with the command EXEP flp1_test_obj,u (or similar), then the Pointer Environment removes every last trace of the program from the screen when it stops. Also, if any machine code Procedures or Functions report an error, then the error may not be reported and the program may just ‘hang’ if #0 is not open.

One of the main problems with compiled programs is the Cache provided with 68020 processors (and faster). Caches cause problems with machine code which modifies itself (normally to enhance speed). Whereas programs compiled with Qliberator should be okay (depending on the toolkits used within them), TURBO compiled programs are normally self- modifying. However, provided that a program is compiled under TURBO with the BRIEF directive then it will run provided that the cache is switched off for the first 0.3 seconds after EXECing the program - for example, if a program causes problems, use:

10 CACHE_OFF
20 EX flp1_PROGRAM_exe
30 PAUSE 15
40 CACHE_ON

Refer to CACHE_ON and CACHE_OFF for further details.

One of the other differences between TURBO compiled programs and Qliberator compiled programs is that the former all make assumptions about the start of the screen and system variables. However, there is a public domain program by Davide Santachiara called Turbostart which resolves these problems by altering compiled programs.

Writing Programs to Run Under the Pointer Environment

It is not as difficult as it first may seem to write programs to run under the Pointer Environment, unless you intend your program to use the Pointer and Menu facilities provided by the Pointer Environment.

Basically, any program which has been written with the intention of multitasking will work under the Pointer Environment. However, the program should not attempt to tie up the QL’s resources unless it is using them (for example, do not open a printer channel until you need to send output to it) and then close the channel once all output has been sent. It is also useful to allow the user to add their own facilities such as a mouse through amending the boot program.

A SuperBASIC program will of course only multitask if it is compiled (see Section 4.1) or if the user has Minerva or SMSQ/E which provide multitasking SuperBASIC interpreters.

Using the Pointer and Menu Facilities

If you intend to use the Pointer and Menu facilities provided by the Pointer Environment, then you will need to use either QPTR (from Jochen Merz Software) or EasyPTR (also from Jochen Merz Software). The latter is easier to get to grips with and use from SuperBASIC, but is less flexible than QPTR. You may also want to use QMenu (also from Jochen Merz Software) which provides various ready made menus which can be easily added to BASIC programs in order to provide standard facilities such as getting the user to select an existing filename.

Your program will need to set itself an Outline (see OUTLN) and also check on the screen display size (see SCR_XLIM and SCR_YLIM). You may also want to check that the Pointer Environment is available (see P_ENV).

If a program does not define an OUTLN properly, then you may notice that some parts of the program’s display disappears - the reason for this is that when the program is first loaded, the Pointer Environment uses the OUTLN of the calling Job to define the maximum size of the windows which the program may use - this may be too small and your own program should therefore define its own OUTLN.

If the OUTLN setting is too small, you may notice that some EasyPTR menus will not appear on screen - this is because if you try to OPEN a window which appears partly outside the OUTLN setting, then that window will be OPENed to be the same size and position as the OUTLN setting. If you try to use WINDOW to position an existing window so that any part of it would fall outside of the OUTLN setting, then an error will be reported.

Other problems will occur if you CLOSE the window which has the OUTLN defined - the OUTLN will become the smallest area possible which encompasses all currently OPEN windows - and will become attached to the smallest existing channel number - this unfortunately means that the contents of any windows which have been CLOSEd where those windows (or part of them) fall outside the new OUTLN, will disappear!! This can result in some programs losing parts of their display.

An example of this can be seen with the program:

5 OPEN #0,con
10 OUTLN #0,448,200,32,16:PAPER #0,0:CLS#0:INK #0,4:PRINT #0,'This is #0'
20 OPEN #1,con_400x160a40x40:PAPER 2:CLS:INK 7:PRINT 'This is #1'
30 OPEN #2,con_300x100a80x70:PAPER#2,7:CLS#2:INK#2,2:PRINT #2,'This is #2'
40 PAUSE #0
50 CLOSE #0
55 PAUSE #1
60 OPEN #0,con_448x200a32x16:PRINT #0,'This is #0'

Try compiling this program as flp1_test_obj and then enter the command EX flp1_test_obj - see what happens when #0 is CLOSEd?

Compare the result if you changed line 50 to CLOSE #2.

One of the solutions to this problem is to use the G option on the EXEP command to define a Guardian Window - try the command: EXEP flp1_test_obj,g,512,256,0,0. The other answer is to OPEN another channel (for example #3) to be the OUTLN channel before any windows are OPENed - change line 5 and 10 thus:

5 OPEN #3,con_:OUTLN #3,448,200,32,16
10 OPEN #0,con_448x200a32x16:PAPER #0,0:CLS#0:INK #0,4:PRINT #0,'This is #0'

These problems with defining OUTLN’s will become apparent if you test programs under an SBASIC interpreter and then after the program has been compiled - if you use the SBASIC command to start up a Multiple BASIC (or similar on Minerva) and then LRUN your BASIC program, the OUTLN is always set to OUTLN 512,256,0,0 whereas if you EXECute a compiled program (or even if you use a command such as EX flp1_program_bas to start up a BASIC program under SMSQ/E), the OUTLN will be that set in the calling program (unless defined in the program itself).

Multitasking Programs

If you write a program which is to run under the Pointer Environment, it is useful to remember some rules:

  • There is no need to activate the cursor on the program - when the program is PICKed by the user, then any open con_ channel is automatically activated. You may however, still wish to do this if the program is to be able to run without the Pointer Environment.
  • If any part of the job’s OUTLN is overlapped by other programs, then the job will not be able to access and scr_ or con_ channels (it will wait until the program is activated). This can be overcome with PIE_ON / PEON or by starting the program with EXEP (using the U parameter). You can check if a program can write to a screen channel with PEND.
  • As soon as the program ends (with STOP or RJOB) then all of its windows will be removed from the screen, again unless you have used EXEP with the U parameter.