Mastering Sideways ROM & RAM - Module 18 - ROM Filing System ------------------------------------------------------------ The ROM filing system (RFS) is available on all BBC microcomputers with 1.00 operating system, or later. The RFS is selected by typing *ROM and uses data stored in paged ROMs or in serially accessed ROMs associated with the Acorn speech system. The RFS is similar to a read-only tape filing system but with the data stored in SWR instead of on cassettes. programs stored in the RFS can only be run by loading them from the filing system into the user memory of the computer. There are two service calls, &0D and &0E, associated with the RFS. Service call &0D can be demonstrated with the service call trace program introduced in Module 2. The RFS initialisation call (&0D) is issued by the MOS when the RFS is active and a filing system command has been used. It gives an active ROM the opportunity to inform the MOS that it contains RFS files, and the start address of those files. On entry, the Y register contains 15 minus the ROM number of the next ROM to be scanned. If this so called adjusted ROM number is less that the number of the ROM receiving the call, the call should be ignored because it has been processed earlier. Otherwise, the adjusted ROM number for the current ROM should be stored in &F5, the start address of the RFS data should be stored in the RFS address vector at locations &F6 and &F7, and the accumulator should be reset to zero before returning control to the MOS. This rather complicated response to service call &0D is summarised in figure 18.1 in which "file" marks the address at which the RFS formatted files start. PHA \ save accumulator CMP #13 \ is it service call &0D? BNE fourteen \ branch if not &0D TYA \ Y stores adjusted ROM number EOR #&0F \ calculate (15-adjusted ROM number) CMP &F4 \ compare with this ROM number BCC out \ return if less LDA #file MOD 256 \ low byte of first RFS file STA &F6 \ store in RFS vector LDA #file DIV 256 \ high byte of first RFS file STA &F7 \ store in RFS vector LDA &F4 \ load this ROM number EOR #&0F \ calculate adjusted ROM number STA &F5 \ and store in &F5 .exit PLA \ balance stack LDA #0 \ flag service provided RTS \ and return to MOS .out PLA \ restore accumulator RTS \ and return to MOS .fourteen Figure 18.1 The service call &0D interpreter. ----------- --------------------------------- The RFS byte get call (&0E) is issued after initialising the ROM with service call &0D. It is used to retrieve one byte pointed to by the RFS address vector. A ROM must only respond to this call if the number stored in &F5 is 15 minus the number stored in &F4. The byte should be returned in the Y register with the accumulator reset to zero and the RFS address vector incremented to point to the next byte. The necessary coding is shown in figure 18.2 PHA \ save accumulator CMP #13 \ is it service call &0D? BNE fourteen \ branch if not &0D . . \ service call &0D goes in here . .exit PLA \ restore accumulator LDA #0 \ flag service provided RTS \ and return to MOS .fourteen CMP #14 \ is it service call &0E? BNE out \ branch if not &0E LDA &F5 \ get adjusted ROM number EOR #&0F \ restore to current value CMP &F4 \ compare with this ROM's number BNE out \ branch if its another ROM LDY #0 \ reset Y LDA (&F6),Y \ load vectored byte TAY \ transfer byte to Y register INC &F6 \ increment low byte of vector BNE exit \ branch if not page boundary INC &F7 \ increment high byte of vector JMP exit \ and exit with A = 0 .out PLA \ restore accumulator RTS \ and retrun to MOS Figure 18.2 The service call &0E interpreter. ----------- --------------------------------- The coding in figures 18.1 and 18.2 will be used in the interpreters of all the programs used in the RFS modules of this course. The programs which illustrate this part of the course use a modular design in which the ROM header and interpreter are created in a first program which then chains a second program containing the RFS formatting software. The second program reads files from the current filing system, formats them for the RFS and stores them in memory starting at the end of the header created in the first program. When all the required files are formatted the RFS ROM image is stored on disc or tape. The program RFSHEAD demonstrates a simple service ROM header and RFS interpreter. It uses the routines in figures 18.1 and 18.2 to create the RFS interpreter and then chains the program RFSGEN which does the RFS formatting. RFSHEAD stores the address of the first byte available for RFS data in the resident integer variable O%. RFSGEN uses the address in O% as the start address for RFS data. The program RFSGEN will be used in every RFS module of this course. It will be chained by programs, such as RFSHEAD, which use different headers and interpreters to achieve different objectives, but all the RFS ROM images need to store their data in the format created by RFSGEN. The ROM image created by RFSGEN stores data in a block structure similar to the format used by the tape filing system, with the following exceptions. 1. The ASCII character "+" (&2B) is used as an end of ROM marker. This is not an end of data marker and does not mean that files larger than 16k cannot be stored. If, for example, a 30k file is stored in two ROMs the first 15k would be in one ROM and terminated with and end of ROM marker. The second 15k of the file must be in the next ROM scanned by the RFS and again followed by an end of ROM marker. The RFS formatting program RFSGEN only formats up to 16k RFS ROM images but it can be modified to run in a second processor and format 32k RFS ROM images. 2. The block header (not the ROM header) is replaced by the "#" (hash) continuation character (&23) for all except the first and last blocks. If a continuation character is used in this way it means that the header is unchanged from the previous block, except for the block number. 3. The four spare bytes in the cassette header block are used to store the address of the byte after the end of the file, enabling the RFS default fast file searching. The RFS data produced by RFSGEN must be preceded by a ROM header, such as the one generated by the program RFSHEAD. The two programs RFSHEAD and RFSGEN can be used with either the disc filing system or the tape filing system to create the RFS ROM image. If the DFS is used both these two programs and the files to be formatted must be on the same disc. If the tape filing system is used you will need to know in advance the load and execute addresses and the length of all the files to be formatted. The formatting programs can be used to create RFS ROM images by programmers who have no idea why the programs work. Such beginners will find figures 18.3 and 18.4 helpful. Figure 18.3 shows the formatting programs being used with the DFS and figure 18.4 shows the same programs being used with the tape filing system. In both cases they are used to format the supplied program DEMO into an RFS ROM image and to store the ROM image in a file called TEST. To use the ROM image load the file TEST into SWR and press the Break key. Then type *ROM and press Return. If you have used the DEMO program supplied with the course you can then either *RUN "DEMO" or CHAIN "DEMO" from the RFS into user memory in either the I/O processor or a 6502 second processor. The BASIC program DEMO has been modified so that it can be *RUN. If you want to know how this was done then disassemble it from &2194 to see the machine code embedded in the REM statement in line 360. >CHAIN "RFSHEAD" SRW SOFTWARE (96) Drive 0 Option 0 (off) Dir. :0.$ Lib. :0.$ DEMO RFSGEN RFSHEAD Press to end Enter file name = DEMO Enter file name = Save filename = TEST $.TEST FF8000 FF8000 000980 16E > Figure 18.3 Using the RFS formatter with the DFS. ----------- ------------------------------------- >CHAIN "RFSHEAD" Searching Loading RFSHEAD 04 0442 Searching Loading RFSGEN 08 08BA Press to end Enter file name = DEMO Load address = &1900 Exec address = &2194 File length = &8D2 Searching Loading DEMO 08 08D2 FFFF1900 FFFF802 Enter file name: Save filename = TEST RECORD then RETURN TEST 09 0980 FFFF8000 FFFF8000 > Figure 18.4 Using the RFS formatter with the TFS. ----------- ------------------------------------- 10 REM: RFSHEAD 20 MODE7 30 HIMEM=&3C00 40 diff=&8000-HIMEM 50 H%=HIMEM 60 ROMnumber=&F4 70 phROM=&F5 80 ROMpoint=&F6 90 FOR pass = 0 TO 2 STEP 2 100 P%=HIMEM 110 [ OPT pass 120 BRK 130 BRK 140 BRK 150 JMP service+diff 160 OPT FNequb(&82) 170 OPT FNequb((copyright+diff) MOD 256) 180 BRK 190 OPT FNequs("RFS") 200 .copyright 210 BRK 220 OPT FNequs("(C)1986 Gordon Horsington") 230 BRK 240 .service 250 PHA 260 CMP #13 270 BNE fourteen 280 TYA 290 EOR #&F 300 CMP ROMnumber 310 BCC out 320 LDA #(lastbyte+diff) MOD 256 330 STA ROMpoint 340 LDA #(lastbyte+diff) DIV 256 350 STA ROMpoint+1 360 LDA ROMnumber 370 EOR #&F 380 STA phROM 390 .exit 400 PLA 410 LDA #0 420 RTS 430 .fourteen 440 CMP #14 450 BNE out 460 LDA phROM 470 EOR #&F 480 CMP ROMnumber 490 BNE out 500 LDY #0 510 LDA (ROMpoint),Y 520 TAY 530 INC ROMpoint 540 BNE exit 550 INC ROMpoint+1 560 JMP exit+diff 570 .out 580 PLA 590 RTS 600 .lastbyte 610 ] 620 NEXT 630 O%=lastbyte 640 CHAIN"RFSGEN" 650 DEFFNequb(byte) 660 ?P%=byte 670 P%=P%+1 680 =pass 690 DEFFNequw(word) 700 ?P%=word 710 P%?1=word DIV 256 720 P%=P%+2 730 =pass 740 DEFFNequd(double) 750 !P%=double 760 P%=P%+4 770 =pass 780 DEFFNequs(string$) 790 $P%=string$ 800 P%=P%+LEN(string$) 810 =pass 10 REM: RFSGEN 20 IFH%<>HIMEM END 30 DIM pblock 20,name 20,mcode 50 40 osargs=&FFDA 50 osfile=&FFDD 60 oscli=&FFF7 70 address=&A8 80 high=&AA 90 low=&AB 100 last=&AC 110 finish=FALSE 120 A%=0 130 X%=0 140 Y%=0 150 nottape=USR(osargs)AND &000000FF 160 IFnottape<4 nottape=FALSE ELSE nottape=TRUE 170 FORpass=0 TO 2 STEP 2 180 P%=mcode 190 [ OPT pass 200 LDA #0 210 STA high 220 STA low 230 TAY 240 .nextbyte 250 LDA high 260 EOR (address),Y 270 STA high 280 LDX #8 290 .loop 300 LDA high 310 ROL A 320 BCC rotate 330 LDA high 340 EOR #8 350 STA high 360 LDA low 370 EOR #&10 380 STA low 390 .rotate 400 ROL low 410 ROL high 420 DEX 430 BNE loop 440 INY 450 CPY last 460 BNE nextbyte 470 RTS 480 ] 490 NEXT 500 IF nottape : *CAT 510 PRINT'"Press to end"' 520 REPEAT 530 INPUTLINE"Enter file name = "$name 540 IF $name="" finish=TRUE 550 IF NOT nottape PROCtape 560 IF NOT finish PROCfile 570 UNTIL finish 580 ?O%=ASC("+") 590 INPUT'"Save filename = "filename$ 600 IF filename$="" END 610 $mcode="SAVE "+filename$+" "+STR$~(HIMEM)+" "+STR$~(O% +1)+" FFFF8000 FFFF8000" 620 X%=mcode 630 Y%=X% DIV 256 640 *OPT1,2 650 CALL oscli 660 *OPT1,0 670 END 680 DEFPROCfile 690 channel=OPENUP($name) 700 IFchannel=0 PRINT"File not found" : ENDPROC 710 !pblock=name 720 IF nottape PROCdisc 730 lename=LEN($name)+23 740 nextfile=&8000+O%-HIMEM+lename-lename*(fsize>256)-3* (fsize>512)*(((fsize-1)DIV256)-1) + fsize 750 IF nextfile > &BFFE PRINT"File too big" : CLOSE#channel : ENDPROC 760 block=0 770 IFfsize>256 PROCfirst 780 PROCheader(128,fsize) 790 CLOSE#channel 800 ENDPROC 810 DEFPROCheader(flag,length) 820 lename=LEN($name) 830 ?O%=ASC("*") 840 O%=O%+1 850 $O%=$name 860 O%=lename+O% 870 ?O%=0 880 O%!1=load 890 O%!5=exec 900 O%!9=block 910 O%!11=length 920 O%?13=flag 930 O%!14=nextfile 940 ?last=lename+18 950 !address=O%-lename 960 CALL mcode 970 O%!18=!high 980 O%=O%+20 990 PROCread(length) 1000 block=block+1 1010 ENDPROC 1020 DEFPROCread(length) 1030 J%=length-1 1040 FORI%=0TOJ% 1050 O%?I%=BGET#channel 1060 NEXT 1070 !address=O% 1080 ?last=length 1090 CALL mcode 1100 O%!length=!high 1110 fsize=fsize-length 1120 O%=O%+length+2 1130 ENDPROC 1140 DEFPROCfirst 1150 PROCheader(0,256) 1160 IFfsize<=256 ENDPROC 1170 REPEAT 1180 ?O%=ASC("#") 1190 O%=O%+1 1200 PROCread(256) 1210 block=block+1 1220 UNTIL fsize<=256 1230 ENDPROC 1240 DEFPROCtape 1250 IF finish ENDPROC 1260 INPUT'"Load address = &"l$ 1270 load=EVAL("&"+l$) 1280 INPUT"Exec address = &"l$ 1290 exec=EVAL("&"+l$) 1300 INPUT"File length = &"l$ 1310 fsize=EVAL("&"+l$) 1320 *OPT1,2 1330 ENDPROC 1340 DEFPROCdisc 1350 A%=5 1360 X%=pblock 1370 Y%=X% DIV 256 1380 CALL osfile 1390 load=X%!2 1400 exec=X%!6 1410 fsize=X%!10 1420 ENDPROC