Mastering Sideways ROM & RAM - Module 10 - Extended vectors ----------------------------------------------------------- The BBC microcomputer makes extensive use of vectors. The MOS vectors can be found in page two of memory from &200 to &235. There is also an area of memory in page &0D known as the extended vector space which has a further three bytes, called an extended vector, available to each vector. The first two bytes of an extended vector store the address of a vectored routine if it is in a sideways ROM and the third byte stores the number of that ROM. In this module I will show you how the vectored operating system routines can be re-vectored to point to an address in your SWR software. To do this it is necessary to store the address and ROM number of your new routine in the extended vector space and then change the address contained in a vector to point to an extended vector entry point in page &FF. There are two strategies for returning from vectored code, these are: 1. If the routine completely replaces the standard code it exits with an RTS instruction. 2. If routine does not completely replace the standard code it returns with JMP (oldvec) after restoring the registers. oldvec is the original vector contents. The example used in this module deals with the first strategy but the extended vector technique demonstrated can be used to deal with either strategy or a combination of both strategies. Each of the twenty-seven MOS vectors has a vector number, n, such that the vector can be found at location &200+(2*n). The extended vector entry point for each vector can be found at &FF00+(3*n). The start of the extended vector space should be found using Osbyte &A8 with X=&00 and Y=&FF. This returns the address of the first byte of the extended vector space, V, in X and Y. In a BBC B with MOS 1.20 V=&0D9F. The address and ROM number of your new routine must be stored in the extended vector space starting at V+(3*n). The number 3*n can be thought of as an offset on the first byte of the extended vector space for vector number n. This information has been summarised in figure 10.1 in which the number, name, location, extended vector entry point (EVEP) and offset on the extended vector space (EVS) has been listed for all the MOS vectors. No. Name Location EVEP EVS offset n &200+2*n &FF00+3*n 3*n ------------------------------------------ 0 USERV &200 &FF00 &00 1 BRKV &202 &FF03 &03 2 IRQ1V &204 &FF06 &06 3 IRQ2V &206 &FF09 &09 4 CLIV &208 &FF0C &0C 5 BYTEV &20A &FF0F &0F 6 WORDV &20C &FF12 &12 7 WRCHV &20E &FF15 &15 8 RDCHV &20E &FF18 &18 9 FILEV &212 &FF1B &1B 10 ARGSV &214 &FF1E &1E 11 BGETV &216 &FF21 &21 12 BPUTV &218 &FF24 &24 13 GBPBV &21A &FF27 &27 14 FINDV &21C &FF2A &2A 15 FSCV &21E &FF2D &2D 16 EVENTV &220 &FF30 &30 17 UPTV &222 &FF33 &33 18 NETV &224 &FF36 &36 19 VDUV &226 &FF39 &39 20 KEYV &228 &FF3C &3C 21 INSV &22A &FF3F &3F 22 REMV &22C &FF42 &42 23 CNPV &22E &FF45 &45 24 IND1V &230 &FF48 &48 25 IND2V &232 &FF4B &4B 26 IND3V &234 &FF4E &4E Figure 10.1 The extended vector entry points and offsets -------------------------------------------------------- You can use figure 10.1 to find the addresses that need to be altered to point any vector into SWR software. If, for example, you want to alter vector number 16, the event vector, to point to your own SWR routine you first need to use Osbyte &A8 to find the start of the extended vector space and store the start address in two consecutive zero page bytes. Then, using post-indexed indirect addressing with the EVS address stored in these two bytes and the EVS offset in the Y register, store the address of your new routine and the ROM number of your SWR program in the extended vector space. Lastly point the event vector into your SWR program by storing the extended vector entry point for vector 16 (&FF30) in the event vector (&220 and &221). The coding in figure 10.2 could be used within your SWR program to re-vector EVENTV to point to a new routine within the same ROM image. LDA #&A8 \ Osbyte &A8 LDX #&00 LDY #&FF JSR &FFF4 \ find start of EVS STX &A8 \ store least sig. byte of EVS STY &A9 \ store most sig. byte of EVS LDY #&30 \ offset on EVS for EVENTV LDA #new MOD 256 \ least sig. byte of new routine STA (&A8),Y \ least sig. byte in EVS INY \ increment offset for most sig. byte LDA #new DIV 256 \ most sig. byte of new routine STA (&A8),Y \ most sig. byte in EVS INY \ increment offset for ROM number LDA &F4 \ find ROM number of new routine STA (&A8),Y \ store in EVS LDX #&30 \ least sig. byte of EVEP for EVENTV LDY #&FF \ most sig. byte of EVEP for EVENTV SEI \ set interupt disable flag STX &220 \ store EVEP in least sig. byte of EVENTV STY &221 \ store EVEP in most sig. byte of EVENTV CLI \ clear interupt disable flag Figure 10.2 Re-vectoring the event vector. ------------------------------------------- This rather elaborate system of extended vectors has been used to convert the program LOCK into sideways RAM format in the program VECTOR. The program LOCK was written for use with a cassette based BBC B computer. It is used to create "Locked" cassette files which can be *RUN but not *LOADed. This is one of the simplest cassette file protection systems. The program works by using the event vector to point to a routine which sets the least significant bit of the cassette block flag byte at &3CA fifty times a second. This overcomes the cassette filing system routine which clears the bit before saving each block. The program uses the start of vertical sync event which is generated fifty times a second coincident with the vertical sync on the video signal. LOCK has been written to run at &C00 but, if you want to use the program, you could assemble it to run in any available part of user memory. 10 REM: LOCK 20 DIM save 40 30 address=&A8 40 eventv=&220 50 osargs=&FFDA 60 osbyte=&FFF4 70 oscli=&FFF7 80 FOR pass = 0 TO 2 STEP 2 90 P%=&C00 100 [ OPT pass 110 LDA #0 120 TAX 130 TAY 140 JSR osargs 150 CMP #3 160 BCC tape 170 BRK 180 OPT FNequb(&FE) 190 OPT FNequs("Select *TAPE and try again") 200 BRK 210 .tape 220 LDA #13 230 LDX #4 240 LDY #0 250 JSR osbyte \ Disable vert sync event 260 LDX #lock MOD 256 270 LDY #lock DIV 256 280 SEI 290 STX eventv 300 STY eventv+1 310 CLI 320 LDA #14 330 LDX #4 340 JSR osbyte \ Enable vert sync event 350 RTS 360 .lock 370 PHP 380 CMP #4 390 BNE notfour 400 PHA 410 LDA &3CA 420 ORA #1 \ AND #&FE to unlock 430 STA &3CA 440 PLA 450 .notfour 460 PLP 470 RTS 480 ] 490 NEXT 500 INPUT'"Save filename? = "filename$ 510 IF filename$="" END 520 $save="SAVE "+filename$+" FFFF0C00+100" 530 X%=save 540 Y%=X% DIV 256 550 *OPT1,2 560 CALL oscli 570 *OPT1,0 580 END 590 DEFFNequs(string$) 600 $P%=string$ 610 P%=P%+LEN(string$) 620 =pass 630 DEFFNequb(byte) 640 ?P%=byte 650 P%=P%+1 660 =pass The object code generated by LOCK first checks to see if the casstte filing system is active (lines 110-160). If it is not the error routine (lines 170-200) halts the program and generates an error message. The conversion of this type of error trapping into SWR format has been explained in Module 8. The program disables the start of vertical sync event (lines 220-250). EVENTV is re-vectored to point to the cassette locking routine (lines 260-310) and event number 4, the start of vertical sync event, is enabled (lines 320-340) before returning (line 350). After pointing EVENTV to the routine starting in line 360 and enabling the start of vertical sync event, the locking routine (lines 360-470) will be executed fifty times a second ensuring that the least significant bit of the cassette block flag byte is always set. When you *SAVE a file with least significant bit of &3CA set that file will be locked. After *SAVEing the file be sure to press the Break key to reset all the vectors or type *FX13,4 to disable the start of vertical sync event. If you don't you will lock every file you save and you will be unable to load any further cassette files. To convert LOCK into sideways RAM format you need to add a suitable header, interpreter, and error routine all of which can be taken from the earlier modules of this course. You must also replace the re-vectoring of EVENTV with an extended vector routine similar to the one illustrated in figure 10.2. These modifications have been made in the program VECTOR. To use VECTOR load the object code it generates into SWR and press the Break key. The routine is selected by typing *LOCK ON and disabled by typing *LOCK OFF. VECTOR uses the familiar one-command interpreter from Module 3 (lines 320-600), argument reading routine from Module 5 (lines 620-760) and error handler from Module 8 (lines 1610-1740). If the command *LOCK OFF is recognised by the interpreter contol is passed to the label ".lockoff" (line 810) and the start of vertical sync event is disabled by the subroutine eventoff (line 820 and lines 1300-1340) before returning to the MOS (line 830). If the command *LOCK ON is recognised by the interpreter control passes to the label ".lockit" (line 840). The program first makes sure that the cassette filing system is active (lines 850-900). If the CFS is active it disables the start of vertical sync event (line 950 and lines 1300-1340) and finds the start of the extended vector space (lines 960-990), the address of which is stored in two consecutive zero page bytes (lines 1000-1010). The Y register is loaded with the event vector space offset (line 1020) and the address of the new routine and the ROM number are stored in the extended vector space (lines 1020-1100). EVENTV is re-vectored to point to the extended vector entry point (lines 1110-1160). Event number 4, the start of vertical sync event, is enabled (lines 1170-1190) before restoring the zero page memory locations (lines 1210-1240), balancing the stack (lines 1250-1270) and returning to the MOS with the accumulator reset to zero (lines 1280-1290). One of the most important things to notice from this conversion is that the vectored routine which sets the least significant bit of the cassette block flag byte in the program LOCK (lines 360-470) has not been altered at all in the program VECTOR (lines 1350-1460). All the modifications needed to re-vector the routine to work from SWR are done to the extended vector space, the extended vector entry point and the vector. 10 REM: VECTOR 20 MODE7 30 HIMEM=&3C00 40 DIM save 50 50 diff=&8000-HIMEM 60 address=&A8 70 comvec=&F2 80 ROMnumber=&F4 90 errstack=&100 100 eventv=&220 110 gsinit=&FFC2 120 gsread=&FFC5 130 osargs=&FFDA 140 osbyte=&FFF4 150 oscli=&FFF7 160 FOR pass = 0 TO 2 STEP 2 170 P%=HIMEM 180 [ OPT pass 190 BRK 200 BRK 210 BRK 220 JMP service+diff 230 OPT FNequb(&82) 240 OPT FNequb((copyright+diff) MOD 256) 250 BRK 260 .title 270 OPT FNequs("LOCK") 280 .copyright 290 BRK 300 OPT FNequs("(C)1987 Gordon Horsington") 310 BRK 320 .service 330 CMP #4 340 BEQ unrecognised 350 .exit 360 RTS 370 .unrecognised 380 PHA 390 TXA 400 PHA 410 TYA 420 PHA 430 LDX #&FF 440 .comloop 450 INX 460 LDA title+diff,X 470 BEQ found 480 LDA (comvec),Y 490 INY 500 CMP #ASC(".") 510 BEQ found 520 AND #&DF 530 CMP title+diff,X 540 BEQ comloop 550 PLA 560 TAY 570 PLA 580 TAX 590 PLA 600 RTS 610 .found 620 SEC 630 JSR gsinit 640 LDA address 650 PHA 660 LDA address+1 670 PHA 680 JSR gsread 690 BCS mistake 700 JSR gsread 710 BCS mistake 720 AND #&DF 730 CMP #ASC("F") 740 BEQ lockoff 750 CMP #ASC("N") 760 BEQ lockit 770 .mistake 780 LDX #(syntax+diff) MOD 256 790 LDY #(syntax+diff) DIV 256 800 JMP error+diff 810 .lockoff 820 JSR eventoff+diff 830 JMP quit+diff 840 .lockit 850 LDA #0 860 TAX 870 TAY 880 JSR osargs 890 CMP #3 900 BCC tape 910 LDX #(cfs+diff) MOD 256 920 LDY #(cfs+diff) DIV 256 930 JMP error+diff 940 .tape 950 JSR eventoff+diff 960 LDA #&A8 970 LDX #0 980 LDY #&FF 990 JSR osbyte 1000 STX address 1010 STY address+1 1020 LDY #&30 1030 LDA #(lock+diff) MOD 256 1040 STA (address),Y 1050 INY 1060 LDA #(lock+diff) DIV 256 1070 STA (address),Y 1080 INY 1090 LDA ROMnumber 1100 STA (address),Y 1110 LDX #&30 1120 LDY #&FF 1130 SEI 1140 STX eventv 1150 STY eventv+1 1160 CLI 1170 LDA #14 1180 LDX #4 1190 JSR osbyte 1200 .quit 1210 PLA 1220 STA address+1 1230 PLA 1240 STA address 1250 PLA 1260 PLA 1270 PLA 1280 LDA #0 1290 RTS 1300 .eventoff 1310 LDA #13 1320 LDX #4 1330 LDY #0 1340 JMP osbyte 1350 .lock 1360 PHP 1370 CMP #4 1380 BNE notfour 1390 PHA 1400 LDA &3CA 1410 ORA #1 \ AND #&FE to unlock 1420 STA &3CA 1430 PLA 1440 .notfour 1450 PLP 1460 RTS 1470 .syntax 1480 BRK 1490 OPT FNequb(&DC) 1500 OPT FNequs("Syntax") 1510 OPT FNequb(&3A) 1520 OPT FNequs(" *LOCK ON/OFF") 1530 BRK 1540 OPT FNequb(&FF) 1550 .cfs 1560 BRK 1570 OPT FNequb(&FE) 1580 OPT FNequs("Type *TAPE and try again") 1590 BRK 1600 OPT FNequb(&FF) 1610 .error 1620 STX address 1630 STY address+1 1640 LDY #&FF 1650 .errorloop 1660 INY 1670 LDA (address),Y 1680 STA errstack,Y 1690 BPL errorloop 1700 PLA 1710 STA address+1 1720 PLA 1730 STA address 1740 JMP errstack 1750 .lastbyte 1760 ] 1770 NEXT 1780 INPUT'"Save filename = "filename$ 1790 IF filename$="" END 1800 $save="SAVE "+filename$+" "+STR$~(HIMEM)+" "+STR$~(las tbyte)+" FFFF8000 FFFF8000" 1810 X%=save 1820 Y%=X% DIV 256 1830 *OPT1,2 1840 CALL oscli 1850 *OPT1,0 1860 END 1870 DEFFNequb(byte) 1880 ?P%=byte 1890 P%=P%+1 1900 =pass 1910 DEFFNequw(word) 1920 ?P%=word 1930 P%?1=word DIV 256 1940 P%=P%+2 1950 =pass 1960 DEFFNequd(double) 1970 !P%=double 1980 P%=P%+4 1990 =pass 2000 DEFFNequs(string$) 2010 $P%=string$ 2020 P%=P%+LEN(string$) 2030 =pass