Introduction |
|||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
A few years back I had the idea to jot down all of the little noteworthy things I'd found out about BBC BASIC. Pretty soon I had a flaky set of notes, written on bits of torn off paper and scribbled down hastily in notebooks. Some time later, whilst searching for useful things to fill up my web space with, I realised that these notes would be of value to others as well as myself and decided to turn them all into a web page, which you find here. These tips and tricks are intended for the more experienced BASIC coder - if you are new to programming, or unfamiliar with BBC BASIC V, then you could find that you will confuse yourself by reading these pages. Sometimes most of the help you need on a BASIC keyword can be found out from the HELP command, which provides some simple help on most of BASIC's keywords. Although this page is not intended to replace Acorn's own BASIC reference manual, there will be overlap at some point. |
|||||||||||||||||||||||
The DIM Statement
|
|||||||||||||||||||||||
The DIM statement is often used incorrectly, for example consider ' Remember that the first element of an array or DIMmed block is at zero, not one. |
|||||||||||||||||||||||
Negative DIM statement |
|||||||||||||||||||||||
Using a statement such as ' Other negative values in DIM statements will generate an error. |
|||||||||||||||||||||||
Avoid DIMs |
|||||||||||||||||||||||
Speaks for itself really. For any serious work, you're often better off just writing some simple memory management routines. |
|||||||||||||||||||||||
Useful Statements
|
|||||||||||||||||||||||
TRACE is a very useful statement. It is used as follows:
By using |
|||||||||||||||||||||||
Formatting using LISTO |
|||||||||||||||||||||||
LIST [<line number>][,[<line number>]][IF<pattern>]. list section [if pattern]
|
|||||||||||||||||||||||
DATA |
|||||||||||||||||||||||
DATA statements tend to be intertwined with line numbers, which we don't really want to have to think about anymore. RESTORE [+][number] allows the data pointer to be set to the first DATA in the program - 'RESTORE' - the next set of data ahead of the statement - 'RESTORE +' - or the data number lines ahead - 'RESTORE +number'. You can save the current data pointer by using 'LOCAL DATA' and restore it to its previous value by using 'RESTORE DATA'. RESTORE DATA is performed automatically if you use LOCAL DATA in a function or procedure. |
|||||||||||||||||||||||
SUMLEN |
|||||||||||||||||||||||
The SUMLEN function returns the sum of the lengths of all the strings in a string array. For example:
|
|||||||||||||||||||||||
MOD |
|||||||||||||||||||||||
In addition to its usual function of returning the remainder of a division, the MOD function will return the modulus (square root of the sum of the squares of each element) of a numeric array. For example:
|
|||||||||||||||||||||||
QUIT |
|||||||||||||||||||||||
As a function, QUIT will return TRUE if BASIC was invoked with the -quit command line parameter. |
|||||||||||||||||||||||
CRUNCH |
|||||||||||||||||||||||
CRUNCH is a simple BASIC compressor built into the interpreter itself. The reason that it is simple, is that even the 'clever' BASIC compressors can't always compress a program and retain its original meaning (i.e. errors creep into the expressions within the code). The statement is passed a single integer (or expression) which is a bitfield, constructed as follows:
BASIC will automatically CRUNCH programs if BASIC$Crunch is defined. I personally don't advise this as some programs object to being CRUNCHed and voice their objection by locking up the machine. :-( |
|||||||||||||||||||||||
Using the RETURN statement |
|||||||||||||||||||||||
The RETURN statement can be used in the declaration of procedures and functions to indicate that the variable passed is to have its value written back when the function exits. For example:
Any variable passed to this procedure would normally be protected from being changed. Once the RETURN is added, any changes to the variable is automatically reflected when the procedure or function exits. A side effect of using this is that if you call a procedure or function which uses RETURN with an actual parameter - e.g. PROCtimestwo(55) - then BASIC will produce the error 'Invalid RETURN actual parameter'. |
|||||||||||||||||||||||
END |
|||||||||||||||||||||||
This expression alters the amount of memory allocated to BASIC, so you can actually change the wimpslot of your program without resorting to using
For example, the expression ' |
|||||||||||||||||||||||
Saving Space, Increasing Speed
|
|||||||||||||||||||||||
I think of this as obvious, but I'll point it out anyway: Use Integer variables in preference to Real variables wherever possible - they're faster. Although in most cases, they use the same amount of memory - both use 12 bytes for a single-character variable name. (BASIC VI uses 16 bytes for a Real variable as its real variables are stored as actual floating-point numbers). |
|||||||||||||||||||||||
Bitfields |
|||||||||||||||||||||||
Using an integer as a set of flags is much more space efficient than using several variables, or variable arrays, to do the same job. |
|||||||||||||||||||||||
Omitting brackets |
|||||||||||||||||||||||
Where operator priority allows, you can often omit brackets. For example, the following code:
can be rewritten as the following:
which will still yeild the same result, as the numerical operators all have a higher priority than the shift (<<) operator.
Brackets can be omitted on most functions e.g. |
|||||||||||||||||||||||
Bitfields |
|||||||||||||||||||||||
Using an integer as a set of flags is much more efficient than using several variables, or variable arrays, to do the same job. |
|||||||||||||||||||||||
Bit-clearing |
|||||||||||||||||||||||
Use ' |
|||||||||||||||||||||||
Omitting <>0 |
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Accessing memory |
|||||||||||||||||||||||
You access memory using the |
|||||||||||||||||||||||
Shift multiplication |
|||||||||||||||||||||||
Using the << shift command to replace multiplications by power-of-two numbers will yeild a speedup. |
|||||||||||||||||||||||
Being clever 8-) |
|||||||||||||||||||||||
Although you can write code that looks like this:
Use your brain! With a bit of thought you can see that an exact equivalent is:
There's usually plenty of code which can bereduced like this. |
|||||||||||||||||||||||
Memory use |
|||||||||||||||||||||||
Unlike certain other languages, BASIC cannot reallocate
Each time this is called, 256 bytes will be allocated. Once the procedure is exited the memory will not be returned to the control of the interpreter (even though mem% is marked as LOCAL) so if this procedure is repeatedly called, you will eventually run out of memory. Saying that, if you DIM string and integer arrays and they're marked as local, then you will get the memory back when the routine finishes. |
|||||||||||||||||||||||
Operator priorities |
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Constant variable reduction |
|||||||||||||||||||||||
This is a feature of the BasCompress and StrongBS BASIC compressors that I find very useful indeed. The compressor notes all variables that don't change over the duration of the program - constant variables - and replaces all references to them in the output file with their equivalent constant value. Because constant values tend to be faster than constant variables, this gives a speedup as well as (often) reducing the size of the program. |
|||||||||||||||||||||||
Using LIBRARY |
|||||||||||||||||||||||
It's usual for programmers to build up collections of their own generic routines which they amass into libraries. The LIBRARY statement allows these libraries to be loaded into BASIC's memory at run-time for use by your program. |
|||||||||||||||||||||||
Block array initialisation |
|||||||||||||||||||||||
Whole arrays (of any type) can be assigned in one go by supplying a set of parameters, rather than a single value. For example:
The parameter list does not need to have as many entries as the array that is being assigned. You can also do block operations leading to very powerful statements such as:
Note that the last subscript changes quickest, so, using a 3x3x3 array as an example, the following would happen:
(0, 0, 0) = first
|
|||||||||||||||||||||||
@% |
|||||||||||||||||||||||
@% sets the format of the printed numerical output and, optionally, string output. It is an unusual variable as you can assign it both a number and a string, the string being an ANSI printf format string. <&80 for "," separator, &00 for "."><0,1,2 for G,E,F><digits after '.'><digits before '.'> The default format is "G10.9". |
|||||||||||||||||||||||
Avoiding Faulty Code
|
|||||||||||||||||||||||
Multiple-line THEN statements can often be compressed down to a single line to provide a useful saving of space. However, doing this can lead to some problems when the compressed structures don't behave as you would expect them to. For example, say you have a construct like this:
and you compress it down to:
then BASIC will execute a-and-not-b when a or b is FALSE - the first matching ELSE found is executed - which obviously isn't equivalent to the multiple-line construct. |
|||||||||||||||||||||||
TIME |
|||||||||||||||||||||||
The TIME statement returns the number of centiseconds elasped since the machine was switched on. By default, it is equal to the value returned from OS_MonotonicTime. This value is used in some OS calls, such as Wimp_PollIdle to represent time delays.
TIME uses the OS_Word calls 1 and 2 to read and write the system clock - it is not private to each running BASIC program. This means that if you're using TIME in a multitasking program and another program issues Some programs use code such as:
to return to their program after a specific time interval (in this example, in two seconds time). This code would not function as expected if another program issued the You can avoid this mess by replacing TIME references with calls to a FNtime routine which returns the real monotonic time:
|
|||||||||||||||||||||||
Omitting THEN statements |
|||||||||||||||||||||||
BASIC allows you to omit the THEN statement on a single-line IF structure. If you want to make sure your program will be handled correctly when passed through a BASIC compressor program, you must always use the |
|||||||||||||||||||||||
Correct use of shift operators (>>, << and >>>) |
|||||||||||||||||||||||
Using the >> operator to shift down the topmost bit (or something which includes the topmost bit) of a word will not work, as it is a signed operator. Instead use the >>> operator which will shift down unsigned. (There is no equivalent <<< operator.) |
|||||||||||||||||||||||
Trailing spaces |
|||||||||||||||||||||||
Beware of trailing spaces after THEN statements. For example:
This will set the value of variable b to 7 irrespective of the value of a. |
|||||||||||||||||||||||
LOCAL variables |
|||||||||||||||||||||||
If you use a |
|||||||||||||||||||||||
Cunning Stunts :-)
|
|||||||||||||||||||||||
It's actually possible to assign a variable from a procedure by doing something like PROCassign("greeting$", "hello"). The trick is in judicious use of the EVAL and RETURN statements.
Obviously, this version will only work for strings, but it can be adapted for other data types. |
|||||||||||||||||||||||
Dynamic STRING$() buffers |
|||||||||||||||||||||||
The STRING$(<number>,<string>) statement can be used to create temporary buffers on the fly, for example when calling a SWI which needs a string buffer for output. However, their maximum length is 255 which is one less than the 256 character buffer you usually need. For example:
Note: This can be a bit tricky at times, you really have to read whatever has been put in the buffer immediately after the call, otherwise it may get overwritten. |
|||||||||||||||||||||||
Further on from BASIC
|
|||||||||||||||||||||||
The natural way to code a block memory movement procedure in BASIC is to write a FOR loop with a load/store pair inside. However, this is a slow way to do it. Using a small ARM code block copier, it will go zillions of times faster*. Although assembly can be assembled at run-time. However, if the code is getting quite lengthy it's best to store it in a file and load it in when your program starts up. * Approximate figure. ;-) Note: assembler area limit checking, exported routines from CALL. |
|||||||||||||||||||||||
Detecting the OS version |
|||||||||||||||||||||||
The function INKEY(-256) returns the operating system version identifier - a unique value of which is returned for each release of the host operating system. This value is not related to the '3.11', '3.71', and so on, version numbers as it has to retain compatibility with the identifiers from other Acorn OS's. This isn't specifically a BASIC feature, as all the interpreter really does is return the value available from calling SWI OS_Byte 129,0,&FF. The identifiers are:
|
|||||||||||||||||||||||
Using newer OS calls |
|||||||||||||||||||||||
Using the more recent versions of system calls is likely to provide performance increases and enhanced functionality. For example:
Another example is the RISC OS 3.6 sprite plot calls which can now plot using a simple ordered dither, if you specify a bit in R0. |
|||||||||||||||||||||||
Thanks |
|||||||||||||||||||||||
Thanks to those people who have made suggestions and provided information towards this page. If you have any feedback on this page, it would be gratefully accepted. |
Index - Hello - New! - Software - BASIC
Notes - Good & Bad - Sticks - Links - Copyright
DaveSpace