Extended Basic Using Interface 1, it is possible to extend the Spectrum's Basic interpreter and add your own commands - see, for example, the article by Kathleen Peel in February's [1984] Your Computer. I have discovered another method of doing this which does not use Interface 1, and will work whether or not it is fitted. It can be used on any Spectrum, either 16K or 48K. The ability to add your own commands can be very useful. It allows you to produce customised versions of Basic for particular applications. For example, you could add extra graphics commands to assist in writing arcade games, or add toolkit routines to help while debugging programs. Any new command which you decide to use must fail the normal syntax checking in ROM. Two ways of doing this are particularly suitable for this method. The easiest is to use one of the keyword tokens RND to STEP as your command. Listing 1, for example, demonstrates SCREEN$ used in this way. Note that this does not affect the normal use of the token - for example SCREEN$ as a function. The other method is to invent new words such as *Renum and *Trade which would need to be typed letter by letter. The "*", or another shifted character, is required to get out of K cursor mode. Just like any other command, your new one could then be followed by a number of parameters - for example *Renum 100,10 might renumber from line 100 in steps of 10. The program in listing 1 sets up and demonstrates the use of a new command SCREEN$ which can be used to change the paper and ink colours of the whole screen without erasing its contents. The new command has the syntax: SCREEN$ paper colour,ink colour (e.g. SCREEN$ 5,1) Listing 1 can be used on both 16K and 48K computers. First type in lines 1 to 21. These lines set up the machine code routine which recognises, checks syntax, and inter- prets the new command. The machine-code loader includes a checksum, but even so it is best to SAVE these lines before you try a RUN, because if you have made a mistake in the DATA statements the computer might crash. If everything seems OK when you RUN these lines, you can test the new command by typing: SCREEN$ 5,1 as a direct command which should change the screen colours to cyan paper and blue ink. If this works you can now delete lines 1 to 19 before entering the rest of the program. You must leave lines 20 and 21 however, otherwise the new command will not be recognised when you RUN the program. The method for adding new commands is to intercept the error routine which is used by the interpreter when an incorrect command is found. The address of the error routine is the bottom item on the machine stack, and it is pointed to by the system variable ERR SP - address 23613/4. The occurrence of an error (during either syntax-checking or runtime) causes the following actions from the ROM: # The address reached by the interpreter - system variable CH ADD - is copied to the error pointer - X PTR. # The error code is put into ERR NR. The error code is one less than the report code which is printed after a runtime error. For example, the error code is 255 for report 0 (OK), and is 11 for report C - Nonsense in BASIC. An unrecognised command would have an error code of 11 during both syntax checking and runtime. # The stack pointer is loaded from ERR SP, and so it points to the bottom item on the machine stack. # The floating-point calculator stack and memory area is cleared. # The machine code instruction RET removes the bottom item from the stack and jumps to this address. While syntax- checking, this address is normally 12B7 Hex, and causes the offending line to be displayed with a marker at the appro- priate position. At runtime, the address is normally 1303 Hex, which halts the program and displays a report message. By POKEing the start address of your machine code routine onto the bottom of the stack, you can divert the error routine. This gives you a chance to find out whether the error was caused by the interpreter reaching one of your new commands. If so, your routine can take over the inter- pretation of the statement. This can be understood more clearly by examining listing 2, the disassembled routine for interpreting the SCREEN$ command used in the example. There are several important parts in this routine which will be useful to you in creating your own new commands. The first thing to do is to see whether the error code is 11 for Nonsense in BASIC. If not, then return to the ROM routine for either a syntax or run-time error after pushing the address of your routine onto the bottom of the stack - it will be addressed by ERR SP ready for the next error. If the error code was 11, then the error may have been caused by your new command. At this stage, system variable CH ADD points to the character in the line after the one which caused the error. If your command uses one of the tokens RND to STEP - e.g., SCREEN$ - then it is easy to test for the appropriate code. If you choose new keywords which are typed letter by letter, such as *Renum, then each character should be individually tested, and CH ADD advanced along the line as you do so by using RST 18H and RST 20H - see table 1. If your new command did not cause the error, then a return to the ROM can be made. After identifying the command, your routine must make sure that it is followed by the correct number of expres- sions, and during runtime they must be evaluated. The easiest way to do this is to use the line-scanning routines in ROM - see table 1. Before calling these, CH ADD must point to the first character of the expression, and afterwards it will point to the character following the expression. At run-time, the value of the expression is put onto the calculator stack. The final syntax check which must be made is to ensure that the last character of the statement is Enter or a colon. CH ADD should point to this character, otherwise the Basic interpreter will be upset when you return. If syntax- checking, the return can now be made after resetting the stack. At runtime the command can now be executed. The start address of your routine is replaced on the bottom of the stack, and a jump back to the ROM made. Any number of new commands can be added to the inter- preter using this method. Each one will need its own syntax-checking and runtime routine. If you want to try this for yourself, the example in listing 2 will give you an idea of what is involved. After writing your machine code routine, it can then be loaded into memory. The best place to put it is above RAMTOP, using the CLEAR command to reserve some space for it. Before your new command(s) will be recognised, you must POKE the start address of the machine-code routine onto the bottom of the stack as in lines 20 and 21 of listing 1. Similar POKEs must be included at the beginning of any program which uses your extended Basic. Although your routine should replace its start address onto the stack each time it is called, the RUN command has the effect of clearing the stack and returning the normal error address. The POKEs are needed in the program to overcome this. ___________________________________________________________ Listing 2. ORG RAMTOP+1 ;The routine is self-relocating but ;must be put just above RAMTOP START LD A,(23610) ;Was the error code = 11 for CP 11 ;"Nonsense in BASIC"? JR Z,NONSENSE ERROR BIT 7,(IY+1) ;Bit 7 of FLAGS is set at runtime JR NZ,RUNERROR SYNTXERR LD HL,(23730) ;Syntax error. START = RAMTOP + 1. INC HL ;START is put onto bottom of stack PUSH HL ;ready for next error. JP 12B7H ;Back to ROM at this address. RUNERROR CALL 1303H ;Runtime error - produce report. LD (IY+0),255 ;Clear error number LD HL,(23641) ;Remove floating point forms from CALL 11A7H ;line in editing area before LD HL,(23730) ;doing a syntax check INC HL ;Put START on bottom of stack PUSH HL JP 12B4H ;Return to ROM NONSENSE LD HL,(23645) ;CH ADD is the address reached by DEC HL ;interpreter. Obtain character LD A, (HL) ;which caused the error. CP 170 ;Was it SCREEN$? JR NZ,ERROR ;Error if not. LD (IY+0),255 ;Reset ERR NR and X PTR, and then LD (IY+38),0 ;check for two CALL 1C7AH ;numbers separated by comma. If not BIT 7,(IY+0) ;found, ERR NR will indicate an JR Z,ERROR ;error. CH ADD has been advanced CP 13 ;and A contains the next character JR Z,OK ;which must be ENTER or a colon LD (IY+0),11 ;else give "Nonsense in BASIC" CP 58 ;error. JR NZ,ERROR OK LD (IY+0),255 ;Syntax is OK, so reset ERR NR BIT 7,(IY+1) ;If runtime the command can now be JR NZ,DO-IT ;obeyed LD HL,(23730) ;else the addresses START and INC HL ;12B7H are put onto stack and a PUSH HL ;return to the ROM is made. LD HL,12B7H PUSH HL JP 1B76H DO-IT LD HL,(23693) ;Execution. First permanent colours LD (23695),HL ;are copied to temporary colours. CALL 1E94H ;The INK colour is unstacked CP 8 ;If it was 8, the ink is left JR Z,PAPER ;unchanged, else the ROM routine LD D,A ;is used to change ATTR T ink SCF CALL 2235H PAPER CALL 1E94H ;Paper colour is unstacked, and CP 8 ;if it was not 8, the ROM routine JR Z,OUT ;is used again LD D,A AND A CALL 2235H OUT CALL 1CADH ;Temp colours are made permanent. LD A,(23693) ;LDIR instruction is used to make LD HL,5800H ;the attributes colours the same LD DE,5801H ;as ATTR P. LD BC,2FFH LD (HL),A LDIR LD HL,(23730) ;START is put onto the bottom of INC HL ;the stack ready for the next PUSH HL ;error, and a return to the ROM JP 1B76H ;is made. ___________________________________________________________ Table 1. ADDRESS (Hex) 18 RST 18H loads the accumulator with the character from the program addressed by CH ADD. Non-print- able characters (e.g. colour codes) are ignored and CH ADD advanced until a valid character is found. 20 RST 20H. CH ADD is incremented and the next character from the program put into the accumu- lator. 1C82 A numeric expression is evaluated and its value put on the calculator stack if runtime. CH ADD should point to the first character of the expression before calling this routine, and afterwards it points to the next character after the expression. If the expression was numeric, bit 6 of system variable FLAGS is set (reset for a string). 1C7A Evaluate two numeric expressions, separated by a comma and put values onto calculator stack if runtime. CH ADD and FLAGS as for 1C82. 1E94 Take number off calculator stack and put into the accumulator. The number must be positive and less than 256. 1E99 Take number off calculator stack and put into BC. The number must be positive and less than 65536.