TRAP IT by D. Pope from ZX Computing Feb/Mar.1984 The experts said it couldn't be done - to disable the Break key in a BASIC program, but Mr D Pope shows us how it CAN be done. ON-ERROR processing is one of the few useful features omitted on the Sinclair machines but I realised quite recently that it would be possible (with the aid of a small machine code routine) to provide such a function. In the section of the Spectrum manual which contains details of the system variables (Chapter 25) there is a WORD entry called ERR_SP at address 23613 which is described as the "Address of item on machine stack to be used as error return", which I examined by using the PEEK command in a function as shown below. It was obvious that the word at 23613 pointed at an entry in the machine stack and this was printed giving the value 4867 which is within the Sinclair ROM. I wrote a small machine code routine (not included here) which simply jumped to 4867 after placing a required value into the system variable ERR_NR and found that I got the appropriate error report as shown in the Sinclair manual in appendix B. It seemed reasonable to assume that, if the stack entry pointed to by ERR SP was altered to address a machine code routine other than 4867, then any subsequent error would cause the alternative routine to execute. I developed the machine code routine shown quite quickly and it worked for all errors except 12 (C in the Sinclair manual) "Nonsense in BASIC" which, despite considerable further effort, I could not trap without putting the machine into a recursive loop. The machine code routine consists of two parts, the first labelled START is run from BASIC with an appropriate USR nnnn call and this places the address of the second part, TRAP, into the machine stack entry pointed to by ERR_SP. The TRAP routine is then entered on any "error" completion (from 0 upwards). Names and addresses Although comprehensive comments have been included with the machine code, a little additional explanation is probably worthwhile, particularly since the routines were written to be run wherever they are placed in memory. First, the address of TRAP placed onto the machine stack must be the correct 'absolute address' regardless of where the routine is stored. This is calculated dynamically by the first two instructions, relying on the fact that the BC register pair contains the address of START on entry from the USR nnnn function. This absolute address is placed into the machine stack entry pointed to by ERR_SP by the next five instructions. Secondly, the TRAP routine must be able to replace its own entry address onto the machine stack if it is to handle successive errors and although this is done by the two instructions starting at PUSH, the value used for TRAP in the LD DE,TRAP instruction must be modified in order to allow it to function correctly when loaded at differing memory addresses. The last five instructions in the START routine before RET place the corrected value into the instruction labelled PUSH to overcome this problem. It must be stressed that instruction modification of this type is very bad programming practice and should only be used where there is no alternative. The modified instructions should be adequately explained as should those which carry out the modification. In this instance I could see no way of avoiding dynamic instruction modification but if anyone can offer an alternative approach I should be most interested to hear from them. Basically speaking When the trap routine is entered (on any BASIC completion) it first checks the given error code and passes control to the normal error handling code at 4867 if the error is either 0 (OK), 9 (STOP), or 12 (Nonsense in BASIC); this gives the BASIC programmer the option to allow their program to end normally when required by executing STOP or issuing a GO TO for a line past the end of the program. Without this way out the only way to terminate a program with TRAP incorporated would be to pull out the power lead, a point which will not be missed by anyone who wishes to "protect" the code of a BASIC program. As indicated at the beginning of the article error 12 seems impossible to recover from and is therefore also given back to the Spectrum routine to handle. For any error other than 0, 9 and 12 the TRAP stack entry is restored and then the number of the BASIC line chosen to begin ON-ERROR processing is placed into the system variable NWPPC. I chose line 9900, statement 1 but this could easily be changed as required by following the comments against the EQUates for GTOLL, GTOLH and GTOST. In order that the user's error handling routine (beginning at line 9900) can make routing decisions based on the line where the error occurred, the next few instructions save the line, statement and error numbers. These details could be placed into any spare memory locations (four bytes are required) but, since I do not have a printer, I often use the printer buffer as workspace and the routine stores the crash details there. The error line number is placed into the word at 23296, the statement number within that line is put into the byte at 23298 and the actual error number (increased by 1) is stored in the byte at 23299. It should be noted that the error is stored as a binary number, not in the equivalent character form given in the normal error reports expected from the BASIC; thus 21 would be shown following an attempted "BREAK" if the contents of 23299 was PEEKED. Finally in the TRAP routine, the ERR_NR is reset to 255 (its normal value) and a jump is made to the normal processing loop used when executing a BASIC program. The address (7030) of MAINL was found by a small routine (not included here) which printed the contents of the first few machine stack entries, which always shows 7030 immediately below the word with 4867. Although the routine could be used from a direct command it would serve little purpose since the trap address would be reset by the Spectrum routine when the command execution completed. The TRAP only needs to be set up once in a BASIC program and will continue to pass errors to line 9900 until an error 0, 9 or 12 is encountered. Any BASIC programmer who has ever wished for an ON-ERROR function will have few problems finding uses for this routine but, as a starter, it is fun to produce a small program which keeps going regardless of attempts to "BREAK" it and only finishes when it has done its job: 10 RANDOMIZE USR nnnn 20 PRINT "STOP ME IF YOU CAN." 30 FOR a=1 TO 10000 40 PRINT AT 1,0;a 50 NEXT a 60 STOP 9900 PRINT AT 4,0;"NO, I WON'T." 9910 PAUSE 100 9920 CLS 9930 GO TO 4O Of course the value used for "nnnn" will depend on where you decide to place the TRAP routine. I have not included a loader with the routine details because so many good examples have been printed in the past that most people must be able to enter a machine code program without problems. The function for displaying WORD length data from any memory location which was mentioned at the beginning of the article is: 10 DEF FN a(x)=PEEK x+256*PEEK (x+1) whereupon PRINT FN a(23613) gives a value just below RAMTOP, and entering PRINT FN a(FN a(23613)) should return 4867. Incidentally, I believe a similar function could be produced to give ON-ERROR handling to ZX81 users since that machine has a similar configuration of system variables. Unfortunately I sold my ZX81 to fund the Spectrum and cannot therefore give any assistance with that project! -- Another Fine Product transcribed by: Jim Grimwood (jimg@globalnet.co.uk), Weardale, England --