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.