Mastering Sideways ROM & RAM - Module 08 - Service ROM errors ------------------------------------------------------------- In Module 4 you were shown how to use the DFS command *ENABLE within your own SWR programs. If you look carefully at the program ENABLE used to illustrate Module 4 you will see that the reset routine is only executed if *ENABLE is typed before *RESET. If any other command is typed before *RESET the interpreter simply returns control to the MOS with the accumulator reset to zero. This is the simplest form of error handling - if the user types something unexpected return with the accumulator reset to zero. In this module I will show you how to deal with user generated errors within SWR service routines in such a way that an error will abort a BASIC program and print an appropriate error message. The way of dealing with errors in language ROMs is different from the method described here and will be dealt with in Module 17. Figure 8.1 illustrates how the DFS ROM responds to user generated errors such as neglecting to include a mandatory argument. If you enter the DFS command *TYPE without an argument, the DFS responds by making the current language print the syntax it expects. If you make the same mistake within a running BASIC program the DFS makes the syntax message available to BASIC and aborts the BASIC program. BASIC then prints the message and the line number in which the error was generated. >*TYPE Syntax: TYPE >NEW >10 *TYPE >20 STOP >RUN Syntax: TYPE at line 10 > Figure 8.1 Error handling by the DFS ------------------------------------ The DFS handles the syntax error by copying the appropriate error message from the DFS ROM into the error message buffer at the bottom of the hardware stack, from &100 upwards, and then jumping to &100. Figure 8.2 is a hexadecimal and ASCII dump of the error message buffer following the syntax error generated in figure 8.1. 0100 00 DC 53 79 6E 74 61 78 ..Syntax 0108 3A 20 54 59 50 45 20 3C : TYPE < 0110 66 73 70 3E 00 00 00 00 fsp>.... 0118 00 00 00 00 00 00 00 00 ........ Figure 8.2 A memory dump of the error message buffer ----------------------------------------------------- The first byte of the error message buffer is the BRK opcode &00. The next byte is the error number, in this example &DC. The error number is followed by the message "Syntax: TYPE " and then another BRK opcode. The DFS ROM passed control to the error buffer by jumping to &100. The MOS, after processing the BRK instruction, passed control to the BASIC error handler which printed the message starting at &102 until it encountered the next BRK opcode at &114. When the error was generated by a command within a BASIC program, BASIC added " at line 10" onto the end of the DFS error message. When the MOS processes a BRK instruction it issues service call 6 to all the sideways ROMs. When service call 6 is issued, the address of the error message number is stored in &FD (low byte) and &FE (high byte). Location &F0 contains a copy of the stack pointer after the BRK was executed and Osbyte &BA (or the less respectable method of peeking location &24A) can be used to find the number of the ROM in use when the error was generated. This information will be used in Module 13 when trapping errors generated in other ROMs is covered. User generated errors within your service ROMs should be dealt with in the same way that the DFS deals with them. You should copy an error message, starting and ending with the BRK opcode, into the error buffer and then jump to &100 so that the MOS and currently active language can take responsibility for the error handling. An error number normally follows the first BRK opcode. You should note that error number 0 is treated as 'fatal' by BASIC and other languages, causing any ON ERROR handler to be ignored. . . . .mistake LDX #message MOD 256 \ low byte of error message LDY #message DIV 256 \ high byte of error message JMP error \ jump to error handler .message BRK \ BRK opcode EQUB &7F \ error number 127 EQUS "User error" BRK \ last byte EQUB &FF \ message termination character . . . .error STX &A8 \ store X and Y STY &A9 \ for indirect addressing LDY #&FF \ initialise offset .errorloop INY \ increment offset LDA (&A8),Y \ read next byte of message STA &100,Y \ copy into error buffer BPL errorloop \ branch if not termination character JMP &100 \ jump to first BRK . . . Figure 8.3 User generated error handling. ------------------------------------------ In the outline error handling routine in figure 8.3, control will be passed to the label ".mistake" after detecting a user generated error. The X and Y registers are loaded with the address of the error message and control is passed to the label ".error". The error message is copied from the ROM into the error buffer. The copying stops when &FF is found at the end of the error message and control is then passed to &100. The MOS and the currently active language can then take over the error processing. There is no need to balance the stack before jumping to the BRK instruction at &100. If you have been following the course, you should by now have enough experience to modify the program ENABLE (from Module 4) to include this more sophisticated error handling technique. The error handler in figure 8.3 has been used in the program ERROR. This program uses the now familiar one command interpreter to implement the new command *CHECK (0-6). This syntax means that the command must have an argument in the range from 0 to 6 inclusive. If the argument is outside that range or missing there is a syntax error. The command can be used from within a BASIC program to check which filing system is active. If it is not the one you want, as specified by the argument, then the routine responds with an incorrect filing system error message and aborts the BASIC program. If the active filing system is the one you want then the program will continue running without interuption. The different filing systems are represented by the argument as follows: 0 No filing system active, 1 1200 baud cassette, 2 300 baud cassette, 3 ROM filing system, 4 Disc filing system, 5 Econet filing system, 6 Telesoftware filing system. If you use the command *CHECK without an argument in a BASIC program, the program will abort with an error message "Syntax: *CHECK (0-6) in line xxx". If the DFS is active and you include the command *CHECK 1 in a BASIC program, the program will abort with an error message "Incorrect filing system in line xxx". Note that you should not normally impose assumptions on calls like this, you should call the operating system or filing system and let it tell you if a call is unsupported. The program arbitarily checks for 0-6 to demonstrate the concepts being discussed. When the interpreter (lines 290-570) recognises the new command it passes control to the label ".found" (line 580). The argument is initialised (lines 590-600) and the two zero page memory locations used by the routine are pushed on the stack (lines 610-640). The argument is read (line 650). If no argument is found (line 660) control is passed to the label ".mistake" (line 710). After reading the argument it is converted into binary and checked to see if it is in range (lines 670-700). If it is not in range control is passed to the label ".mistake" (line 710). If it is in range the binary representation of the argument is pushed on the stack (line 760), the accumulator and index registers are reset to zero (lines 770-790) and Osargs is called (line 800). The active filing system is returned in the accumulator and stored in &A8 (line 810). The argument is pulled off the stack and compared with the active filing system number (lines 820-830). If they are identical the routine restores the zero page locations (lines 890-920), balances the stack (lines 930-950) and returns control to the MOS after reseting the accumulator to zero (lines 960-970). If the active filing system number is not the same as the argument the routine generates the incorrect filing system error message (lines 850-870). The error handler (lines 980-1110) is a little different from the one in figure 8.3 because it restores the two zero page bytes at &A8 and &A9 before jumping to &100. The registers pushed on the stack by the interpreter are not pulled off before jumping to &100 because the stack is reset by the language error handler after executing the BRK instruction at &100. 10 REM: ERROR 20 MODE7 30 HIMEM=&3C00 40 DIM save 50 50 diff=&8000-HIMEM 60 address=&A8 70 comvec=&F2 80 errstack=&100 90 gsinit=&FFC2 100 gsread=&FFC5 110 osargs=&FFDA 120 oscli=&FFF7 130 FOR pass = 0 TO 2 STEP 2 140 P%=HIMEM 150 [ OPT pass 160 BRK 170 BRK 180 BRK 190 JMP service+diff 200 OPT FNequb(&82) 210 OPT FNequb((copyright+diff) MOD 256) 220 BRK 230 .title 240 OPT FNequs("CHECK") 250 .copyright 260 BRK 270 OPT FNequs("(C)1987 Gordon Horsington") 280 BRK 290 .service 300 CMP #4 310 BEQ unrecognised 320 .exit 330 RTS 340 .unrecognised 350 PHA 360 TXA 370 PHA 380 TYA 390 PHA 400 LDX #&FF 410 .comloop 420 INX 430 LDA title+diff,X 440 BEQ found 450 LDA (comvec),Y 460 INY 470 CMP #ASC(".") 480 BEQ found 490 AND #&DF 500 CMP title+diff,X 510 BEQ comloop 520 PLA 530 TAY 540 PLA 550 TAX 560 PLA 570 RTS 580 .found 590 SEC 600 JSR gsinit 610 LDA address 620 PHA 630 LDA address+1 640 PHA 650 JSR gsread 660 BCS mistake 670 SEC 680 SBC #ASC("0") 690 CMP #7 700 BCC file 710 .mistake 720 LDX #(syntax+diff) MOD 256 730 LDY #(syntax+diff) DIV 256 740 JMP error+diff 750 .file 760 PHA 770 LDA #0 780 TAX 790 TAY 800 JSR osargs 810 STA address 820 PLA 830 CMP address 840 BEQ correct 850 LDX #(wrong+diff) MOD 256 860 LDY #(wrong+diff) DIV 256 870 JMP error+diff 880 .correct 890 PLA 900 STA address+1 910 PLA 920 STA address 930 PLA 940 PLA 950 PLA 960 LDA #0 970 RTS 980 .error 990 STX address 1000 STY address+1 1010 LDY #&FF 1020 .errorloop 1030 INY 1040 LDA (address),Y 1050 STA errstack,Y 1060 BPL errorloop 1070 PLA 1080 STA address+1 1090 PLA 1100 STA address 1110 JMP errstack 1120 .syntax 1130 BRK 1140 OPT FNequb(&DC) 1150 OPT FNequs("Syntax") 1160 OPT FNequb(&3A) 1170 OPT FNequs(" *CHECK (0-6)") 1180 BRK 1190 OPT FNequb(&FF) 1200 .wrong 1210 BRK 1220 OPT FNequb(&FE) 1230 OPT FNequs("Incorrect filing system") 1240 BRK 1250 OPT FNequb(&FF) 1260 .lastbyte 1270 ] 1280 NEXT 1290 INPUT'"Save filename = "filename$ 1300 IF filename$="" END 1310 $save="SAVE "+filename$+" "+STR$~(HIMEM)+" "+STR$~(las tbyte)+" FFFF8000 FFFF8000" 1320 X%=save 1330 Y%=X% DIV 256 1340 *OPT1,2 1350 CALL oscli 1360 *OPT1,0 1370 END 1380 DEFFNequb(byte) 1390 ?P%=byte 1400 P%=P%+1 1410 =pass 1420 DEFFNequw(word) 1430 ?P%=word 1440 P%?1=word DIV 256 1450 P%=P%+2 1460 =pass 1470 DEFFNequd(double) 1480 !P%=double 1490 P%=P%+4 1500 =pass 1510 DEFFNequs(string$) 1520 $P%=string$ 1530 P%=P%+LEN(string$) 1540 =pass