Your Spectrum
Issue 3, May 1984 - Extending BASIC
Home Contents KwikPik
First the bad news - you'll need to do some actual thinking if you want to get the full benefit from reading this article. But the good news is that it's worth it because, once you've 'taught' the Spectrum a new statement, then you'll be able to use it in any Basic program you care to write. So, let's move on and look at some fundamental concepts ...
The first concept is the fact that the Interface 1 contains a brand new ROM (called the Shadow ROM) which is totally separate from the normal Spectrum ROM. Both ROMs use the same addresses, so you can only ever have one of them in place at once. The Shadow ROM occupies addresses 0000 to 1FFF Hex - so as soon as you start using it, every CALL instruction in this range will actually CALL a routine in the Shadow ROM, not the Spectrum ROM. This includes the re-starts - all of the RST instructions do different things when the Shadow ROM is in place. And here's where the crunch comes in: an Extended Basic handling routine operates with the Shadow ROM in place.
A machine code routine to cope with a new Basic command has to scan through such a line of Basic to make sure all is well, and then carry out the required task. The second fundamental concept then is that of the 'current character' in a Basic line. The gist of it is that we can only check one character at a time (although in practice, there are some ROM subroutines we can use to speed things up) and so the 'current character' is a pointer to the character we are currently checking. (This pointer is stored as the system variable (CH_ADD).)
To avoid all confusion, we'll precede all Shadow ROM addresses with the capital letter 'X'. So '1234' means address 1234 in the Spectrum ROM, and 'X1234' means address l234 in the Shadow ROM. Because all the re-starts are different, it'll be useful to spend some time explaining just one of them - RST X1O. The byte 'D7' (meaning RST X1O) must be followed by two bytes of data. These bytes represent an address in the Spectrum ROM (that's the Spectrum ROM, not the Shadow ROM) with, as ever, the bytes in reverse order. The purpose of this instruction is to call a subroutine in the Spectrum ROM at the address given. So 'D72000' - which is Hex for 'RST X10/0020' - actually means CALL 0020 in the Spectrum ROM. This is, of course, very handy.
Let's consider a routine which extends Basic, making 'USR' a command instead of just a function. Thus, the line:

100 USR 40000

is now a valid Basic line. The purpose of this Basic line is to call a machine code routine (in the above case at address 40000), but it avoids all that messing around with RANDOMIZE USR and PRINT USR and so on. In other words, the final value of the BC register is
E X T E N D I N G
 BASIC 
Stand by to have your mind ever so slightly blown because, for those proud possessors of a ZX Interface 1, Toni Baker is about to explain the procedure by which new commands can be added to the Spectrum vocabulary.
'forgotten' at the end of the routine - there are no 'side effects' like changing the random number seed. The routine at address 40000 (or wherever) must assume that the Spectrum ROM - so in other words my USR command is exactly the same as the normal USR function.
The machine code necessary to create this new command is very short, and may be placed at absolutely any address in RAM. To activate it, however, you must not CALL it in the usual way. Instead you must activate it by loading the system variable VECTOR (address 5CB7, or decimal 23735) with the address of the label START in my program - that's all you need to do.
Having typed in the machine code to any suitable address (remembering to fill in the address that's labelled SUB_ADDR in the listing, and having POKEd the system variable VECTOR with the address of the label START) you'll find that BASIC has now been extended. Try this Basic program to see:

10 INPUT x
20 PRINT x
30 IF x=0 THEN USR 3435
40 GO TO 10


USR 3435 is the subroutine for CLS in the ROM, so you should expect the screen to clear every time you input zero. Does it work?
You'll notice that my machine code listing contains rather a lot of calls to
subroutines in the ROMs. Some of these are important for you to know. Address X01F0 is the address you have to jump to if the Basic line is not recognised. Address X05B7 is a sub- routine which tests for the end of a Basic line - that is for a colon or a carriage return; if this is not found then a syntax error results. But more importantly, if we are only checking the syntax, and not actually carrying out the statement, then this routine will insert the line into a Basic program if it has a line number - or prepare to execute it if not. Control will not return from subroutine X05B7 unless we are actually carrying out the statement. Finally, address X05C1 is the address you must jump to when all the work has been carried out.
When extending Basic on your own, it will be useful to know lots of other sub- routine addresses in the Spectrum ROM. There are too many of them to list here, so either to grab hold of a copy of The Complete Spectrum ROM Disassembly by Dr Ian Logan and Dr Frank O'Hara or, alternatively, get in touch with the ZX Machine Code Users' Club, nn xxxxxxxxx xxxx, xxxxxxxxx, xxxxxx xxxxxx xxnn nxx.
Incidentally, there's one nice little advantage to using my USR command instead of Uncle Clive's USR function. And that is that with my routine the final value of HL' is unimportant, whereas Sinclair Research's routine necessitates that HL' must contain 2758 on return.
CODEASSEMBLERCOMMENTS
D71800
 
FEC0
C2F001
D72000
 
D7821C
 
 
CDB705
 
 
D7991E
 
 
 
ED43????
D7
START     RST X10/
          0018,GET_CHAR
          CP "USR"
          JP NZ,X01F0,ERROR
          RST X10/
          0020,NEXT_CHAR
          RST X10/
          1C82,EXPT_1NUM
 
          CALL X05B7,ST_END
 
 
          RST X10/
          1E99,FIND_INT2
 
 
          LD (SUB_ADDR),BC
          RST X10
A: = current character - ie. the
first character in the Basic line.
Is this character 'USR'?
Generate a syntax error if not.
Look at the next character in the
line.
Check for a numeric expression at
this point. Generate a syntax error if
not.
Check for the end of the statement.
Generate a syntax error if not. If in
Edit mode, then exit at this point.
Place the result of the numeric
expression in the BC register pair, or
generate report code B if out of
range.
Store this address.
 
0000
 
C3C105
SUB_ADDR  DEFS 02
 
          JP X05C1,LINE_RUN
Call the required subroutine with the
Spectrum ROM in place.
End of the routine.
Home Contents KwikPik