Mastering Sideways ROM & RAM - Module 02 - ROM headers ------------------------------------------------------ Paged ROMs and sideways RAMs can contain languages, utilities or programs and data stored in the RFS. All ROMs, whatever they contain, must be recognised by the operating system. To be recognised the first dozen or so bytes must use the ROM header format shown in figure 2.1. The first 10 bytes of the header from &8000 to &8009 have fixed positions in memory and must conform with the pattern shown but, because the copyright and title strings vary in length and because some of the rest of the header is optional, the header does not have a fixed size. Memory map Memory map of ROM of header &BFFF +-----------+ /+-----------------------+ ! ! / !Tube relocation address! ! ! ! !&00 ! ! ! / !Copyright string ! ! ! ! !&00 ! ! ! / !Optional version string! ! ROM code ! ! !&00 ! ! ! / !ROM title string ! &8009- ... ! ! ! !Binary version number ! &8008 +-----------+ / !Copyright offset ! &8007 !Interpreter! ! !ROM type byte ! &8006 +-----------+/ !JMP service ! &8003-&8005 ! Header ! !JMP language ! &8000-&8002 &8000 +-----------+ - - - +-----------------------+ Figure 2.1 The ROM Header ------------------------- The first three bytes of the ROM header (&8000-&8002) contain the language entry point or, if the ROM is not a language, the three bytes will all be zero. The first byte of the language entry point is a JMP opcode followed by the two byte address of the start of the language coding. A language in the BBC micro can be any machine code program which is completely independant of any other language and can take over the processing of input and the production of output until another language is selected to replace it. As far as the BBC micro is concerned Word processors, spreadsheets and machine code monitors all fall into the same category as the more readily recognised languages Logo, Forth and Pascal. Language ROMs will not be considered until module 17 and so, for the first part of the course, the ROM headers will all have zero in the first three bytes. The next three bytes of the ROM header (&8003-&8005) contain the service entry point. All ROMs, except BASIC, must have a service entry point. The first byte of the service entry point is a JMP opcode followed by the two byte address of an interpreter which must process the service requests made by the MOS. Service requests are made to each ROM in turn when, for example, the MOS finds an unrecognised * command or when you type *HELP. The address of the interpreter is usually the first byte after the end of the header, as shown in the ROM memory map, but you can assemble the interpreter anywhere you like within the 16K of memory available to the SWR. The design of service request interpreters will form a very important part of this course. The byte at &8006 is the ROM type byte you met briefly in module 1. The ROM type byte is a flag byte which tells the MOS what the ROM is expected to do. Bit 1 of the byte is always set on all ROMs except BASIC and bits 0, 2 and 3 are always clear. Bit 4 controls the Electron soft key expansions and is always clear on the BBC micro. Bit 5 is set if the ROM is a language ROM with the language coding assembled to run in a second processor. Bit 5 will be clear in all the examples covered in this course. Bit 6 is set if the ROM has a language entry and bit 7 is set if the ROM has a service entry. All ROMs, except BASIC, have bit 7 set. In the examples covered in this course the ROM type byte will have one of only two values. Service ROMs will have &82 stored at &8006 and language ROMs will have &C2 stored at &8006. &82 = 1000 0010 binary, ie. service entry point and bit 1 set, &C2 = 1100 0010 binary, ie. language and service entry points and bit 1 set. If you use the program READROM (from module 1) to look at some of the ROMs you have in your computer there are two other values you might find at &8006. BASIC has &60 stored at &8006 because it does not have a service entry point and bit 1 is clear. "Hi" versions of languages (except Hi-BASIC) have &D2 stored at &8006 because they have a tube relocation address as well as language and service entry points. The copyright offset pointer at &8007 is the offset from &8000 to the zero byte preceding the mandatory copyright string. The copyright string must start in the first &FF bytes of the ROM image and the copyright offset pointer stores the least significant byte of the address of the zero byte preceding the string. (See figure 2.2) The binary version number at &8008 is ignored by the MOS. It can have any value from &00 to &FF and is used to indicate the version number of the ROM. The title string starts at &8009 and must end with a zero byte. The title string is printed out by the MOS on selection of the ROM as a language ROM. It will also be used as a command name in the simple one-command interpreters used in some of the modules in this course. The version string is optional and, if it is used, it must follow the zero byte at the end of the title string. The version string will not be used in any of the examples in this course. If you use a version string it should be in the format "(12 Mar 1987)". The copyright string is essential because the MOS uses this string to recognise the ROM and, if it does not exist, the ROM will be ignored by the MOS. The format must always be a zero byte followed by "(C)" and whatever else you want to include and then another zero byte. The minimum copyright string is a zero byte followed by "(C)" and another zero byte. The tube relocation address, if present, is a 4 byte address at which the language part of the ROM has been assembled. There may be other Tube-specific addresses following the relocation address. Tube relocation will not be covered in this course. Writing the source code for the header is much easier than writing about it. The optional version string has been left out of all the example programs. The source code for the header of the program TRACE assembles as shown in figure 2.2. You can see that it is &29 bytes long but it could be shorter if it used the minimum copyright string of "(C)". Figure 2.2 is a BASIC 2 version of TRACE assembled with bit zero of the OPT directive set. 8000 00 EQUB &00 \ No language 8001 00 EQUB &00 \ entry point 8002 00 EQUB &00 8003 4C 2A 80 JMP service \ Service entry point 8006 82 EQUB &82 \ ROM type byte 8007 0E EQUB copyright MOD 256 \ copyright offset 8008 00 EQUB &00 \ Binary version 8009 54 52 41 43 45 EQUS "TRACE" \ Title string 800E .copyright 800E 00 EQUB &00 800F 28 43 29 31 39 38 37 20 47 6F 72 64 6F 6E 20 48 6F 72 73 69 6E 67 74 6F 6E EQUS "(C)1987 Gordon Horsington" 8028 00 EQUB &00 8029 .service \ Start of interpreter Figure 2.2 The ROM header of TRACE ---------------------------------- When the MOS requests the paged ROMs to provide a service the highest priority ROM, ROM &0F, is offered the first opportunity to respond to the request. It is entered at the service entry point with the registers set up as shown in figure 2.3. Register Service call information ---------------------------------------------------------- Accumulator Service type requested X register Number of current ROM Y register Any parameter required for the service Figure 2.3 Service call registers. ---------------------------------- If ROM &0F is unable to provide the service requested it must exit using RTS with all three registers preserved and the next ROM, ROM &0E, will be given the same opportunity to provide the service, and so on down to ROM &00. If a ROM is able to provide the service requested, and does not want any other ROM to attempt to provide the same service, the accumulator should be zero on exit. The program TRACE is used to provide a display of the service requests made to the sideways RAM bank containing the object code it generates. Chain the program and, when pROMpted, give a filename for the object code which will be saved on disc or tape before you load it into SWR. It is not necessary to include a quotation mark before and after the filename. After saving the object code, load it into SWR and press the Break key. You will immediately see a display of the service requests sent to the ROMs when break is pressed. Type *HELP and you will see the help service request. Type *DISC or *ROM or whatever else you think will make service requests to the paged ROMs and you will soon get to know the service request codes. Do not use TRACE when *SPOOLing or *EXECing files. If you do it will cause the computer to hang up. The service request codes are summerised in figure 2.4. 00 No operation. A higher priority ROM has already provided the service requested. 01 Absolute workspace claim. This will be dealt with in module 12. 02 Private workspace claim. Modules 11 and 13. 03 Auto-boot. Modules 19, 23, 24. 04 Unrecognised command. Modules 3, 5, 8, 9, 10, 11, 13, 15, 16, 17, 20, 21, 22, 23, 24. 05 Unrecognised interupt. Module 14. 06 Break (error). Module 13. 07 Unrecognised Osbyte. Module 6. 08 Unrecognised Osword. Module 7. 09 *HELP. Modules 16, 17, 23, 24. 0A Claim static workspace. Module 12. 0B NMI release. Not covered in this course. 0C NMI claim. Not covered in this course. 0D ROM Filing System initialise. Modules 18, 19, 20, 21, 22, 23, 24. 0E ROM Filing System byte get. Modules 18, 19, 20, 21, 22, 23, 24. 0F Vectors claimed. Not covered in this course. 10 SPOOL/EXEC file closure warning. Not covered in this course. 11 Font implosion/explosion warning. Not covered in this course. 12 Initialise filing system. Not covered in this course. FE Tube system post initialisation. Modules 12, 22 (illegal use). FF Tube system main initialisation. Not covered in this course. Figure 2.4 BBC B Service requests. ---------------------------------- The program TRACE works by intercepting every service request sent to the SWR. Before doing anything else it preserves the A, X and Y registers and the two zero page memory locations used by the print routine by pushing them on the stack (lines 310,370). It then retrieves the service request from the stack and pushes it back onto the top of the stack (lines 380-400) followed by the Y, X and A registers again (lines 420-470). It then selects a yellow foreground colour if Mode 7 is used (lines 480-560) and prints the content of the accumulator, the X and Y registers in hexadecimal (lines 570-680). An explanation of the service request type is then given for the BBC B service requests (lines 690-870). The Master computer uses service requests not available on the BBC B and the content of the registers for these requests is shown but not described. The program exits with all the registers and memory locations restored (lines 880-990). Having the trace active all the time is both distracting and undesirable. You can disable the utility by read protecting the sideways RAM bank or by loading some other ROM image to replace it. Always press Ctrl+Break after read protecting SWR or reloading a ROM image. Although the Master computer uses service requests that are not available on the B series it uses all the B series service requests as well. For this reason all properly written software for the BBC B will also work on the Master but it will not make use of the the extra facilities, such as paged workspace, without responding to the Master specific service requests. 10 REM: TRACE 20 MODE7 30 HIMEM=&3C00 40 DIM save 50 50 diff=&8000-HIMEM 60 address=&A8 70 stack=&105 80 mode=&355 90 osasci=&FFE3 100 osnewl=&FFE7 110 oscli=&FFF7 120 FOR pass = 0 TO 2 STEP 2 130 P%=HIMEM 140 [ OPT pass 150 BRK 160 BRK 170 BRK 180 JMP service+diff 190 OPT FNequb(&82) 200 OPT FNequb((copyright+diff) MOD 256) 210 BRK 220 OPT FNequs("TRACE") 230 .copyright 240 BRK 250 OPT FNequs("(C)1987 Gordon Horsington") 260 BRK 270 .service 280 PHA 290 TXA 300 PHA 310 TYA 320 PHA 330 LDA address 340 PHA 350 STX address 360 LDA address+1 370 PHA 380 TSX 390 LDA stack,X 400 PHA 410 STA address+1 420 TYA 430 PHA 440 LDA address 450 PHA 460 LDA address+1 470 PHA 480 LDA mode 490 CMP #&07 500 BNE notmode7 510 LDA #&83 520 BNE space 530 .notmode7 540 LDA #ASC(" ") 550 .space 560 JSR osasci 570 LDA #ASC("A") 580 JSR equals+diff 590 PLA 600 JSR printbyte+diff 610 LDA #ASC("X") 620 JSR equals+diff 630 PLA 640 JSR printbyte+diff 650 LDA #ASC("Y") 660 JSR equals+diff 670 PLA 680 JSR printbyte+diff 690 PLA 700 CLC 710 ADC #2 720 CMP #&15 730 BCS endprint 740 ASL A 750 TAY 760 LDA index+diff,Y 770 STA address 780 INY 790 LDA index+diff,Y 800 STA address+1 810 LDY #&FF 820 .printloop 830 INY 840 LDA (address),Y 850 BEQ endprint 860 JSR osasci 870 JMP printloop+diff 880 .endprint 890 JSR osnewl 900 PLA 910 STA address+1 920 PLA 930 STA address 940 PLA 950 TAY 960 PLA 970 TAX 980 PLA 990 RTS 1000 .printbyte 1010 PHA 1020 LSR A 1030 LSR A 1040 LSR A 1050 LSR A 1060 JSR nibble+diff 1070 PLA 1080 JSR nibble+diff 1090 LDA #ASC(" ") 1100 JMP osasci 1110 .nibble 1120 AND #&0F 1130 SED 1140 CLC 1150 ADC #&90 1160 ADC #&40 1170 CLD 1180 JMP osasci 1190 .equals 1200 JSR osasci 1210 LDA #ASC("=") 1220 JMP osasci 1230 .index 1240 OPT FNequb((fe+diff) MOD 256) 1250 OPT FNequb((fe+diff) DIV 256) 1260 OPT FNequb((ff+diff) MOD 256) 1270 OPT FNequb((ff+diff) DIV 256) 1280 OPT FNequb((zero+diff) MOD 256) 1290 OPT FNequb((zero+diff) DIV 256) 1300 OPT FNequb((one+diff) MOD 256) 1310 OPT FNequb((one+diff) DIV 256) 1320 OPT FNequb((two+diff) MOD 256) 1330 OPT FNequb((two+diff) DIV 256) 1340 OPT FNequb((three+diff) MOD 256) 1350 OPT FNequb((three+diff) DIV 256) 1360 OPT FNequb((four+diff) MOD 256) 1370 OPT FNequb((four+diff) DIV 256) 1380 OPT FNequb((five+diff) MOD 256) 1390 OPT FNequb((five+diff) DIV 256) 1400 OPT FNequb((six+diff) MOD 256) 1410 OPT FNequb((six+diff) DIV 256) 1420 OPT FNequb((seven+diff) MOD 256) 1430 OPT FNequb((seven+diff) DIV 256) 1440 OPT FNequb((eight+diff) MOD 256) 1450 OPT FNequb((eight+diff) DIV 256) 1460 OPT FNequb((nine+diff) MOD 256) 1470 OPT FNequb((nine+diff) DIV 256) 1480 OPT FNequb((ten+diff) MOD 256) 1490 OPT FNequb((ten+diff) DIV 256) 1500 OPT FNequb((eleven+diff) MOD 256) 1510 OPT FNequb((eleven+diff) DIV 256) 1520 OPT FNequb((twelve+diff) MOD 256) 1530 OPT FNequb((twelve+diff) DIV 256) 1540 OPT FNequb((thirteen+diff) MOD 256) 1550 OPT FNequb((thirteen+diff) DIV 256) 1560 OPT FNequb((fourteen+diff) MOD 256) 1570 OPT FNequb((fourteen+diff) DIV 256) 1580 OPT FNequb((fifteen+diff) MOD 256) 1590 OPT FNequb((fifteen+diff) DIV 256) 1600 OPT FNequb((sixteen+diff) MOD 256) 1610 OPT FNequb((sixteen+diff) DIV 256) 1620 OPT FNequb((seventeen+diff) MOD 256) 1630 OPT FNequb((seventeen+diff) DIV 256) 1640 OPT FNequb((eighteen+diff) MOD 256) 1650 OPT FNequb((eighteen+diff) DIV 256) 1660 .fe 1670 OPT FNequs("Tube system post init.") 1680 BRK 1690 .ff 1700 OPT FNequs("Tube system main init.") 1710 BRK 1720 .zero 1730 OPT FNequs("No operation") 1740 BRK 1750 .one 1760 OPT FNequs("Abs. workspace claim") 1770 BRK 1780 .two 1790 OPT FNequs("Private workspace claim") 1800 BRK 1810 .three 1820 OPT FNequs("Auto-boot") 1830 BRK 1840 .four 1850 OPT FNequs("Unrecognised command") 1860 BRK 1870 .five 1880 OPT FNequs("Unrecognised interupt") 1890 BRK 1900 .six 1910 OPT FNequs("Break") 1920 BRK 1930 .seven 1940 OPT FNequs("Unrecognised Osbyte") 1950 BRK 1960 .eight 1970 OPT FNequs("Unrecognised Osword") 1980 BRK 1990 .nine 2000 OPT FNequs("Help") 2010 BRK 2020 .ten 2030 OPT FNequs("Claim static workspace") 2040 BRK 2050 .eleven 2060 OPT FNequs("NMI release") 2070 BRK 2080 .twelve 2090 OPT FNequs("NMI claim") 2100 BRK 2110 .thirteen 2120 OPT FNequs("RFS initialise") 2130 BRK 2140 .fourteen 2150 OPT FNequs("RFS byte get") 2160 BRK 2170 .fifteen 2180 OPT FNequs("Vectors claimed") 2190 BRK 2200 .sixteen 2210 OPT FNequs("SPOOL/EXEC file closure") 2220 BRK 2230 .seventeen 2240 OPT FNequs("Font implode/explode") 2250 BRK 2260 .eighteen 2270 OPT FNequs("Init. filing system") 2280 BRK 2290 .lastbyte 2300 ] 2310 NEXT 2320 INPUT'"Save filename? : "filename$ 2330 IF filename$="" END 2340 $save="SAVE "+filename$+" "+STR$~(HIMEM)+" "+STR$~(las tbyte)+" FFFF8000 FFFF8000" 2350 X%=save 2360 Y%=X% DIV 256 2370 *OPT1,2 2380 CALL oscli 2390 *OPT1,0 2400 END 2410 DEFFNequb(byte) 2420 ?P%=byte 2430 P%=P%+1 2440 =pass 2450 DEFFNequw(word) 2460 ?P%=word 2470 P%?1=word DIV 256 2480 P%=P%+2 2490 =pass 2500 DEFFNequd(double) 2510 !P%=double 2520 P%=P%+4 2530 =pass 2540 DEFFNequs(string$) 2550 $P%=string$ 2560 P%=P%+LEN(string$) 2570 =pass