Use the Spectrum 128 style menu below to navigate to the various sections detailing the Extended BASIC mechanism.
|Method of Operation|
This extended BASIC mechanism exploits the method used by the Editor ROM (ROM 0) to produce a standard Spectrum error report. When an error report is generated by ROM 0, an error handler routine is called. In the UK Spectrum 128, this is located at $05AC (in other models it is located elsewhere). This routine filters off the new 128 specific error codes and these are then handled locally within ROM 0 to produce the error message. To produce the range of standard Spectrum error reports, ROM 0 delegates this task to ROM 1 (the BASIC ROM). As well as switching to ROM 1, it is also necessary to switch back from RAM bank 7 (which holds all of the new Editor workspace variables) to RAM bank 0 (which is the normal RAM bank used by programs). Switching of RAM banks is performed directly within the error handler routine. Switching of ROMs is achieved by stacking the address of a RST 08H instruction held within the new system variable RAMRST (located in the old ZX Printer buffer) and then calling the SWAP routine (also located within the ZX Printer buffer). Before making the switch, the error code is stored in the byte following the RST 08H instruction, in new system variable RAMERR. Once the SWAP routine has completed, the return will be to the RST 08H instruction. By overwriting the RST 08H instruction with a jump instruction of some kind, it is possible to direct control to a user program and hence intercept all such errors.
Note that the method presented here to extend BASIC is intended to investigate the feasibility of the approach described above. However, it does (as will be described) impose limitations which other approaches do not suffer from. However, many of command parsing techniques will be applicable whichever method is used to extend BASIC, even if via the ZX Interface 1, and so these webpages should be read with that in mind.
There are no suitable single byte instructions that can be used to replace the RST 08H instruction. Using a JR instruction is not possible since the jump destination will depend upon the error code in RAMERR and this will always be within the new system variables, which must not be modified. The only solution is to use a JP instruction. The RAMERR location then holds the low byte of the destination address. The high byte is held within system variable BAUD. It is therefore necessary to overwrite BAUD such that the JP instruction directs control to a suitable location that can hold the extended BASIC parser. This means that any of the RS232 routines within ROM 0 cannot be used whilst the extended BASIC parser is active. This is not a major drawback since the extended BASIC parser can be temporarily disabled whilst RS232 commands are used (which requires restoring BAUD) and then re-enabled afterwards (thereby setting BAUD again for use by the parser).
The RAMRST routine is called by ROM 0 for a variety of errors and so this means that the low byte of the JP destination address could be any one of 256 different locations. An error trap block is therefore required which in its most simplest form contains 255 NOP instructions. This allows all destination addresses to drop down to a common intercept routine. The nature of the error can then be determined from the contents of RAMERR. The efficiency of this mechanism can be improved since not all error codes are used by ROM 0 with the RAMRST routine. Therefore only a specific subset needs to be trapped. This still spans the same address range since the error codes range from $FF to $1C, but at least the unused space can be used to hold other extended BASIC routines. The range of error codes used with RAMRST are:
|$01||2||Variable not found|
|$03||4||Out of memory|
|$06||7||RETURN without GO SUB|
|$07||8||End of file|
|$0B||C||Nonsense in BASIC|
|$0D||E||Out of Data|
|$0E||F||Invalid file name|
|$14||L||Break into program|
|$15||M||Ramtop no good|
Most of these only occur in special circumstances for particular commands, and hence are not that useful to intercept. The really useful error code is "C Nonsense in BASIC" since this is produced when invalid syntax is found. Not all syntax errors can be trapped but the ones that can provide plenty of scope for adding new commands, and in a neat way - there is no need to use an asterisk, etc, as is often required when extending BASIC using the facilities of the ZX Interface 1. Note that due to a bug in ROM 0, error code $1C "a MERGE error" is incorrectly sent to ROM 1 for handling.
To keep as much free space available for BASIC programs as possible, the extended BASIC mechanism must use as little conventional RAM as possible. The bulk of the extended BASIC parser can therefore be stored in one of the additional RAM banks. RAM bank 7 cannot be used since it already holds the RAM disk catalogue and workspace variables. The other RAM banks form the RAM disk and so can hold files. Fortunately, the amount of free space left in the RAM disk is stored in new system variable SFSPACE, and so overwriting this with a lower amount reserves space at the end of the RAM disk for exclusive use by the extended BASIC parser. In total, the RAM disk can occupy up to 72K, with the last 8K of this sharing the space that the catalogue uses (providing there are no clashes). To reserve 8K of space exclusively for the extended BASIC parser means limiting the RAM disk to 56K. The catalogue then has exclusive use of the 8K space in RAM bank 7, and the extended BASIC parser the 8K below this in RAM bank 6.
Once control has passed to the parser within RAM bank 6, the parser needs a way of accessing any other RAM bank, and any ROM. To achieve this, a set of paging routines is required and these must be located within RAM bank 5 or RAM bank 2, since these are available no matter which RAM bank or ROM is paged in. The most convenient position for these paging routines is between the standard system variables and the channel information area. This is the same area where the additional system variables introduced when using the ZX Interface 1 are placed. The ZX Interface 1 will insert its system variables if it detects that the channel information area is at its default address. By installing the extended BASIC mechanism, this will not be the case and as a result the ZX Interface 1 interprets this to mean that its new system variables have previously been set up here and so will never set them up itself. To overcome this, the extended BASIC parser can install the default ZX Interface 1 system variables followed by its new paging routines.
The extended BASIC parser resides within four areas of RAM:
- A modification of the 128 system variable RAMRST, replacing the RST 08H instruction with a JP instruction.
- An error trap block at the end of conventional RAM that intercepts any of the possible JP destinations from RAMRST.
- For error "C Nonsense in BASIC", control is passed to the extended BASIC parser which resides in RAM bank 6.
- Paging routines inserted where the channel information normally resides are used to coordinate the switching between the two ROMs and the RAM banks when executing commands.
|Extended BASIC Parser|
The extended BASIC mechanism can only be used to intercept certain types of syntax error. These are when:
- The word at the beginning of a BASIC line that does not form a valid command keyword.
- The characters following the commands CAT, ERASE and FORMAT do not form valid 128 BASIC commands.
The first method is the more powerful of the two since it means any new command keyword can be constructed. For example, it is possible to add a SCROLL command and for this to appear within the BASIC program just like any other command keyword. However, when entering new keywords for immediate execution, the 128 BASIC interpreter will attempt to evaluate the text as a variable and hence will produce error message "2 Variable not found". To overcome this, a symbol, such as '&', can be designated to precede all new commands, i.e. &SCROLL would be equivalent to SCROLL but would also work as an immediate execution command. Note that it is possible to have embedded spaces within new command keywords, e.g. TO UPPER, and also to have keywords that are a subset of other keywords, e.g. TRIM and TRIM LEFT. It is down to the extended BASIC parser to support these.
In addition, it is also possible to extend a few existing BASIC commands. For example, it is possible to reproduce the ZX Interface 1 command CLS # which restores the permanent colours and clears the screen. The extended BASIC parser would intercept the syntax error caused by the #. It can then process the new characters and accept the command as valid. At run time, the CLS command would be identified and the standard CLS command handler within ROM 1 automatically called to process the command. This command handler clears the screen and then returns to the statement processing loop without checking whether further characters follow in the BASIC line. It does not need to perform such a check since this would have been done at syntax checking time, and the line rejected if it is invalid. The processing loop detects the rogue # and raises a syntax error. The extended BASIC parser would then be invoked and directs control to the new CLS command handler. This command handler routine would then perform the restoration of the permanent colours and clear the screen with these colours. Therefore, both ROM 1 and the extended BASIC parser process the CLS command. The ability to extend standard BASIC commands can only really be done with commands where a syntax error is raised immediately after the keyword. If the syntax error occurs later in the BASIC line then it becomes very difficult to determine the preceding characters of the command. For example, the BEEP command could be extended to accept a third parameter which defines a repetition count, e.g. BEEP n, m, repeat. The extended BASIC parser would get invoked with the error marker pointing at the ', repeat' part of the command. It would be necessary for the extended BASIC parser to scan backwards through the command characters to determine that it was indeed a BEEP command. Since parameters can be formed from variable length expressions, this becomes a very difficult task. As a result, there are very few standard BASIC commands that can realistically, or sensibly, be extended.
It is the responsibility of the extended BASIC parser to handle the case of new commands, converting the typed commands to upper case if required. It might also handle spaces, skipping over surplus and inserting leading or trailing spaces as (and if) required. If the parser ensures that there is a trailing space, then it should prevent doing this if the last character of the command is a '#' since this is used by the Spectrum system to denote a stream and hence is to be immediately followed by a numeric value.
It is not possible to create new functions using the extended BASIC mechanism. Functions can either be created using the standard DEF FN command, or alternatively new commands can be constructed that set a variable and then this variable can be tested using a standard IF command.
Due to a bug within ROM 0, inserting a BASIC line (consisting of new or existing commands) which ends with a trailing space will cause the cursor to move to the last row containing a BASIC statement or, if there are no lines below, then the cursor will jump to the bottom of the screen. This bug is caused by the ROM 0 mechanism that aims to remove duplicate spaces between two keywords. When it encounters the first space, it makes a note of it in the workspace variable at $FD84 within RAM bank 7 and will only insert the space if the following character is not a token with a leading space. However, if the end of the line is found after the first space then the ROM neglects to write out the trailing space. Note that this bug does not apply to the Spanish Spectrum 128 since it uses a different 128 BASIC editor to the other 128K models.