Mastering Sideways ROM & RAM - Module 04 - Using *ENABLE -------------------------------------------------------- In this module I will show you how to use the *ENABLE checker provided in the Acorn 0.90 DFS. This technique can be adapted for use in your own sideways RAM programs with either DFS 0.90 or DFS 1.20. The DFS provides a disc-specific operating system control functions support (Osfsc) which you can use from your own machine code programs. Osfsc is vectored by the filing system control vector (FSCV) at &21E. When DFS is selected, it vectors to &FF2A, which switches to the DFS ROM and uses the extended FSCV vector at &DCC to access the DFS. Osfsc does not have a direct entry address only an indirect entry at &21E. The accumulator on entry to Osfsc contains a code defining the action to be performed. Osfsc called with A=8 (X and Y undefined) is used by the DFS to implement a protection mechanism on dangerous commands by insisting that the previous command was *ENABLE. Acorn DFS 0.90 has a DFS Enable flag at &10C8. It can have one of three values shown in figure 4.1 &10C8 Meaning ---------------------------------------------------------- &FF No record of *ENABLE, flag disabled. &00 Flag on its way from &01 to &FF, flag transitory. &01 *ENABLE was the last command, flag enabled. Figure 4.1 The Enable flag at &10C8 (DFS 0.90) ---------------------------------------------- Osfsc sets the negative flag if *ENABLE was not the last command. It also resets the enable flag as shown in figure 4.2 Before call After call N flag -------------------------------- &FF &FF 1 &00 &FF 1 &01 &00 0 Figure 4.2 The Enable and Negative flags after *ENABLE ------------------------------------------------------ It can be seen from figure 4.2 that calling Osfsc with A=8 switches the enable flag from an enabled state (&01) to a transitory state (&00) and then to a disabled state (&FF). It can also test for *ENABLE as the previous command (negative flag clear). You will see later in the module that the transitory state can be used to test for *ENABLE from sideways RAM service utilities but calling Osfsc and testing the negative flag is the "legal" way to test for *ENABLE from user memory. Not all DFS ROMs fully implement Osfsc with A=8. Acorn DFS 1.20 does not and DFS ROMs from manufacturers other than Acorn may not implement it in the way demonstrated. You can use the program ENABTST to test your DFS ROM. ENABTST must run in the I/O processor. Load ENABTST into memory and run the program. It should respond with "Not enabled at line 230". Type *ENABLE, press Return and run the program again. If your DFS implements an enable checker the program will respond with "Enabled at line 230", if the DFS does not implement an enable checker the program will again respond with "Not enabled at line 230". If the program responded with the enabled message run it a third time. If the enable checker works properly the program will go back to the not enabled message. This test program will show that the enable checker, if it has been properly implemented in the DFS, will only indicate an enabled state if *ENABLE was the previous command. 10 REM: ENABTST 20 DIM mcode &100 30 fscv=&21E 40 FOR pass=0 TO 2 STEP 2 50 P%=mcode 60 [ OPT pass 70 .test 80 LDA #8 90 JSR osfsc 100 BPL enabled 110 BRK 120 BRK 130 OPT FNequs("Not enabled") 140 .enabled 150 BRK 160 BRK 170 OPT FNequs("Enabled") 180 BRK 190 .osfsc 200 JMP (fscv) 210 ] 220 NEXT 230 CALL test 240 END 250 DEFFNequs(string$) 260 $P%=string$ 270 P%=P%+LEN(string$) 280 =pass The program ENABTST demonstrates the legal use of Osfsc. It should be called by jumping to a subroutine where the only instruction is an indirect jump using FSCV, ie. JMP(&21E). Osfsc cannot be used in this way in a sideways RAM program because using a * command to call your utility will reset the enable flag into its transitory state before Osfsc has the chance to test it. As you have seen in figure 4.2 Osfsc cannot distinguish between the transitory state and the disabled state using the negative flag. In order to use *ENABLE in your own SWR software you have to cheat by peeking the enable flag directly and then use the transitory state, when it is on its way from &01 to &FF, to indicate an enabled state. The flag at &10C8 (&10C7 in DFS 1.20) will equal zero in the transitory state and this is used to indicate to SWR software that *ENABLE was the previous command. This technique is demonstrated in the program ENABLE which can be used with either Acorn DFS 0.90 or 1.20 by choosing the appropriate address for the enable flag in line 90. Remember that this makes the program DFS specific. If you intend to use this technique you must specify which DFS is to be used with your programs. The program ENABLE uses a "dangerous" routine to reset the computer and is protected using the *ENABLE command. To show yourself how the reset works first find the reset address for your computer's operating system. To find it from BASIC type PRINT ~ !-4 and press Return. With MOS 1.20 you will get the following result: >PRINT ~ !-4 DC1CD9CD > This prints the hexadecimal values of the 4 bytes at the top of the memory map. The reset address is in bytes &FFFC (low byte) and &FFFD (high byte), in this example the low byte is &CD and the high byte is &D9. The reset address is &D9CD. To reset the BBC B type: >*FX 151,78,127 >CALL !-4 This will reset the computer and clear the user memory. The value of protecting such a dangerous routine from careless use should be obvious. ENABLE uses a similar header and interpreter to the SWR software used to illustrate Module 3. The title string, and therefore the command string, is "RESET". To use the program load the object code it generates into sideways RAM and press Break. Then type *ENABLE and Return followed by *RESET and Return. The enable checker is in lines 600 and 610. If the number stored in the enable flag is &00 control is passes to the reset routine starting at line 670. If the enable flag stores any number other than zero the registers are pulled off the stack and control is passed back to the MOS with A=0 (lines 620-650). The registers are not pulled off the stack by the reset routine (lines 680-720) because reseting the computer in this way also resets the stack pointer. 10 REM: ENABLE 20 MODE7 30 HIMEM=&3C00 40 DIM save 50 50 diff=&8000-HIMEM 60 comvec=&F2 70 REM: DFS 1.20 enabflag=&10C7 80 REM: DFS 0.90 enabflag=&10C8 90 enabflag=&10C8 100 gsread=&FFC5 110 osbyte=&FFF4 120 oscli=&FFF7 130 reset=&FFFC 140 FOR pass = 0 TO 2 STEP 2 150 P%=HIMEM 160 [ OPT pass 170 BRK 180 BRK 190 BRK 200 JMP service+diff 210 OPT FNequb(&82) 220 OPT FNequb((copyright+diff) MOD 256) 230 BRK 240 .title 250 OPT FNequs("RESET") 260 .copyright 270 BRK 280 OPT FNequs("(C)1987 Gordon Horsington") 290 BRK 300 .service 310 CMP #4 320 BEQ unrecognised 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 .finish 530 PLA 540 TAY 550 PLA 560 TAX 570 PLA 580 RTS 590 .found 600 LDA enabflag 610 BEQ enabled \ Flag going from &01 to &FF 620 PLA 630 PLA 640 PLA 650 LDA #0 660 RTS 670 .enabled 680 LDA #&97 690 LDX #&4E 700 LDY #&7F 710 JSR osbyte \ *FX151,78,127 720 JMP (reset) 730 .lastbyte 740 ] 750 NEXT 760 INPUT'"Save filename = "filename$ 770 IF filename$="" END 780 $save="SAVE "+filename$+" "+STR$~(HIMEM)+" "+STR$~(las tbyte)+" FFFF8000 FFFF8000" 790 X%=save 800 Y%=X% DIV 256 810 *OPT1,2 820 CALL oscli 830 *OPT1,0 840 END 850 DEFFNequb(byte) 860 ?P%=byte 870 P%=P%+1 880 =pass 890 DEFFNequw(word) 900 ?P%=word 910 P%?1=word DIV 256 920 P%=P%+2 930 =pass 940 DEFFNequd(double) 950 !P%=double 960 P%=P%+4 970 =pass 980 DEFFNequs(string$) 990 $P%=string$ 1000 P%=P%+LEN(string$) 1010 =pass