Mastering Sideways ROM & RAM - Module 24 - Forward referencing -------------------------------------------------------------- All the source code programs used in this course to generate ROM images have had HIMEM set to &3C00. This allows up to 16k of object code to be held in memory in mode 7. The DFS sets PAGE to &1900 on the BBC B leaving only 8.75K for the source code. This is plenty of room when generating simple RFS ROM images but when designing a ROM which uses a large amount of machine code it is sometimes necessary to use a modular program design with forward referencing to overcome the memory limitations of the BBC microcomputer. Modular programming has already been used in the RFS course modules and involves little more than spliting up a large program into a number of smaller programs. Forward referencing is a technique which can be used with modular Assembly language programs. It uses the resident integer variables (A% to X%) as labels to store the addresses of memory locations. The contents of these memory locations are unknown in the program which defines them but become known or can be calculated when a later program is executed. The contents of the locations stored in the resident integer variables are given dummy values in the initial program. These dummy values are altered in the later program when the actual values to be stored in the memory locations become available. This idea is illustrated in the two short example programs FIRST and LAST. The program FIRST needs to load the X and Y registers with the low and high byte of the address of a command to be used with the command line interpreter (CLI). The actual address of the command is determined in LAST but it is unknown to FIRST and so the index registers are loaded with the dummy address &0000 in FIRST. X% and Y% are used to label the instruction before each address byte. It is not possible to label the address bytes directly. 10 REM: FIRST 20 HIMEM=&5000 30 oscli=&FFF7 40 FOR pass=0TO3 STEP3 50 P%=HIMEM 60 [ OPT pass 70 .X% 80 LDX #0 90 .Y% 100 LDY #0 110 JSR oscli 120 ] 130 NEXT 140 O%=P% 150 CHAIN"LAST" When the program FIRST chains the program LAST the address of the LDX instruction in FIRST becomes available to LAST in X% and the address of the LDY instruction in FIRST becomes available to LAST in Y%. After LAST has assembled the command for the CLI, the address of the first byte of the command can be poked into the bytes following the LDX and LDY instructions assembled in FIRST. This is done in lines 120 and 130 of LAST. 10 REM: LAST 20 HIMEM=&5000 30 FOR pass=0TO3 STEP3 40 P%=O% 50 [ OPT pass 60 RTS 70 .help 80 EQUS "HELP" 90 EQUB &0D 100 ] 110 NEXT 120 X%?1=help MOD 256 130 Y%?1=help DIV 256 When the instructions in the programs FIRST and LAST are assembled they produce the listing shown in figure 24.1. You can see in figure 24.1 that the dummy address &0000 has been used to specify the address of the CLI command. The label X% points to the LDX instruction and the label Y% points to the LDY instruction. >CHAIN "FIRST" 5000 OPT pass 5000 .X% 5000 A2 00 LDX #0 5002 .Y% 5002 A0 00 LDY #0 5004 20 F7 FF JSR oscli 5007 OPT pass 5007 60 RTS 5008 .help 5008 48 45 4C 50 EQUS "HELP" 500C 0D EQUB &0D > Figure 24.1 Listing created by FIRST and LAST ----------- ----------------------------------- The area of memory used to store the object code is disassembled in figure 24.2. You can see in figure 24.2 that the dummy address following the LDX and LDY instructions has been replaced with the actual address of the CLI command. >*DIS 5000 5000 A2 08 .. LDX #&08 5002 A0 50 .P LDY #&50 5004 20 F7 FF .. JSR OSCLI 5007 60 ` RTS 5008 48 H PHA 5009 45 4C EL EOR &4C 500B 50 0D P. BVC &501A Figure 24.2 A disassembly of the memory used by FIRST and LAST ----------- ---------------------------------------------------- This technique has been used in the demonstration programs MODONE and MODTWO. These programs are used with RFSGEN and DEMO to produce a ROM image which includes a multiple * command interpreter, extended help, RFS files and booting the RFS with R+Break. The source code for this ROM image would require more than the 8.75k available if it was all in one program and so a modular approach was used to write the source code programs. Forward referencing was necessary because the address of the start of the RFS files was required in lines 390 and 420 of MODONE but this address was determined by the size of the object code created by MODTWO. The addresses (minus 1) of the memory locations used for the low and high bytes of the RFS dummy address are stored in A% and B% and the actual address of the start of the RFS files is poked into these memory locations in lines 2040 and 2050 of MODTWO. The print subroutine in MODONE is labeled with C% (line 1410 of MODONE) and this address is also passed over to MODTWO (line 110 of MODTWO) so that the same routine can be used in the second program (lines 300 and 470 of MODTWO) without having to write the code for the subroutine in MODTWO. This technique can be used to produce 16K of object code with only 8.75K of memory available for the source code. The lack of memory in the BBC B is not always a disadvantage. The small files used in modular programs are much easier to debug than the huge source code program which would be required to produce a 16K object code file from just one source code file. 10 REM: MODONE 20 MODE7 30 HIMEM=&3C00 40 diff=&8000-HIMEM 50 H%=HIMEM 60 address=&A8 70 ROMnumber=&F4 80 phROM=&F5 90 ROMpoint=&F6 100 stack=&103 110 osasci=&FFE3 120 osnewl=&FFE7 130 osbyte=&FFF4 140 oscli=&FFF7 150 FOR pass = 0 TO 2 STEP 2 160 P%=HIMEM 170 [ OPT pass 180 BRK 190 BRK 200 BRK 210 JMP service+diff 220 OPT FNequb(&82) 230 OPT FNequb((copyright+diff) MOD 256) 240 BRK 250 OPT FNequs("MODULAR ROM DESIGN") 260 .copyright 270 BRK 280 OPT FNequs("(C)1987 Gordon Horsington") 290 BRK 300 .service 310 PHA 320 CMP #13 330 BNE fourteen 340 TYA 350 EOR #&F 360 CMP ROMnumber 370 BCC out 380 .A% 390 LDA #0 \ replace with LSB of RFS data 400 STA ROMpoint 410 .B% 420 LDA #0 \ replace with MSB of RFS data 430 STA ROMpoint+1 440 LDA ROMnumber 450 EOR #&F 460 STA phROM 470 .exit 480 PLA 490 LDA #0 500 RTS 510 .fourteen 520 CMP #14 530 BNE trythree 540 LDA phROM 550 EOR #&F 560 CMP ROMnumber 570 BNE out 580 LDY #0 590 LDA (ROMpoint),Y 600 TAY 610 INC ROMpoint 620 BNE exit 630 INC ROMpoint+1 640 JMP exit+diff 650 .out 660 PLA 670 RTS 680 .trythree 690 TXA 700 PHA 710 TYA 720 PHA 730 TSX 740 LDA stack,X 750 CMP #3 760 BEQ three 770 JMP lastbyte+diff \ continue in MODULE2 780 .three 790 LDA #&7A 800 JSR osbyte 810 CPX #&33 \ Is it R Break? 820 BEQ rbreak 830 PLA 840 TAY 850 PLA 860 TAX 870 PLA 880 RTS 890 .rbreak 900 LDA #&C9 910 LDX #1 920 LDY #0 930 JSR osbyte \ Disable keyboard 940 LDA #&0F 950 LDX #0 960 JSR osbyte \ Flush all buffers 970 LDA address 980 PHA 990 LDA address+1 1000 PHA 1010 LDX #(rfs+diff) MOD 256 1020 LDY #(rfs+diff) DIV 256 1030 JSR print+diff 1040 LDX #(cat+diff) MOD 256 1050 LDY #(cat+diff) DIV 256 1060 JSR print+diff 1070 LDA #&8D 1080 JSR osbyte \ Select RFS 1090 LDA #&8B 1100 LDX #1 1110 LDY #2 1120 JSR osbyte \ *OPT1,2 1130 LDX #(dot+diff) MOD 256 1140 LDY #(dot+diff) DIV 256 1150 JSR oscli \ Catalogue RFS 1160 LDA #&8B 1170 LDX #0 1180 LDY #0 1190 JSR osbyte \ *OPT 1,0 1200 JSR osnewl 1210 LDX #(rfs+diff) MOD 256 1220 LDY #(rfs+diff) DIV 256 1230 JSR print+diff 1240 LDX #(act+diff) MOD 256 1250 LDY #(act+diff) DIV 256 1260 JSR print+diff 1270 PLA 1280 STA address+1 1290 PLA 1300 STA address 1310 LDA #&C9 1320 LDX #0 1330 LDY #0 1340 JSR osbyte \ Enable keyboard 1350 PLA 1360 PLA 1370 PLA 1380 LDA #0 1390 RTS 1400 .print 1410 .C% 1420 STX address 1430 STY address+1 1440 LDY #&FF 1450 .printloop 1460 INY 1470 LDA (address),Y 1480 BEQ endprint 1490 JSR osasci 1500 JMP printloop+diff 1510 .endprint 1520 RTS 1530 .dot 1540 OPT FNequs("CAT") 1550 OPT FNequb(&0D) 1560 .rfs 1570 OPT FNequs("ROM Filing System ") 1580 BRK 1590 .cat 1600 OPT FNequs("Catalogue") 1610 OPT FNequb(&0D) 1620 BRK 1630 .act 1640 OPT FNequs("active") 1650 OPT FNequw(&0D0D) 1660 BRK 1670 .lastbyte 1680 ] 1690 NEXT 1700 O%=lastbyte 1710 CHAIN"MODTWO" 1720 DEFFNequb(byte) 1730 ?P%=byte 1740 P%=P%+1 1750 =pass 1760 DEFFNequw(word) 1770 ?P%=word 1780 P%?1=word DIV 256 1790 P%=P%+2 1800 =pass 1810 DEFFNequd(double) 1820 !P%=double 1830 P%=P%+4 1840 =pass 1850 DEFFNequs(string$) 1860 $P%=string$ 1870 P%=P%+LEN(string$) 1880 =pass 10 REM: MODTWO 20 HIMEM=&3C00 30 diff=&8000-HIMEM 40 address=&A8 50 comvec=&F2 60 stack=&105 70 gsinit=&FFC2 80 gsread=&FFC5 90 osasci=&FFE3 100 osword=&FFF1 110 printer=C% 120 FOR pass = 0 TO 2 STEP 2 130 P%=O% 140 [ OPT pass 150 LDA address 160 PHA 170 LDA address+1 180 PHA 190 TSX 200 LDA stack,X 210 CMP #9 220 BNE tryfour 230 SEC 240 JSR gsinit 250 LDX #0 260 JSR gsread 270 BCC tryextended 280 LDX #(helpmsg+diff) MOD 256 290 LDY #(helpmsg+diff) DIV 256 300 JSR printer+diff 310 BEQ quit 320 .helploop 330 INX 340 JSR gsread 350 .tryextended 360 CMP #ASC(".") 370 BEQ okextended 380 AND #&DF 390 CMP helptitle+diff,X 400 BEQ helploop 410 LDA #&FF 420 CMP helptitle+diff,X 430 BNE quit 440 .okextended 450 LDX #(helpinfo+diff) MOD 256 460 LDY #(helpinfo+diff) DIV 256 470 JSR printer+diff 480 BEQ quit 490 .tryfour 500 CMP #4 510 BNE quit 520 LDX #&FE 530 TYA 540 PHA 550 .firstchar 560 INX 570 PLA 580 TAY 590 PHA 600 LDA (comvec),Y 610 AND #&DF 620 CMP #ASC("X") 630 BNE interpret 640 INY 650 .interpret 660 INX 670 LDA commtable+diff,X 680 BMI found 690 LDA (comvec),Y 700 INY 710 CMP #ASC(".") 720 BEQ founddot 730 AND #&DF 740 CMP commtable+diff,X 750 BEQ interpret 760 .another 770 INX 780 LDA commtable+diff,X 790 BPL another 800 CMP #&FF 810 BNE firstchar 820 .exit 830 PLA 840 .quit 850 PLA 860 STA address+1 870 PLA 880 STA address 890 PLA 900 TAY 910 PLA 920 TAX 930 PLA 940 RTS 950 .founddot 960 INX 970 LDA commtable+diff,X 980 BPL founddot 990 .found 1000 CMP #&FF 1010 BEQ exit 1020 STA address+1 1030 INX 1040 LDA commtable+diff,X 1050 STA address 1060 PLA 1070 SEC 1080 JSR gsinit 1090 JMP (address) 1100 .commtable 1110 OPT FNequs("REVERB") 1120 OPT FNequb((reverb+diff) DIV 256) 1130 OPT FNequb((reverb+diff) MOD 256) 1140 OPT FNequs("ALARM") 1150 OPT FNequb((alarm+diff) DIV 256) 1160 OPT FNequb((alarm+diff) MOD 256) 1170 OPT FNequs("ZOING") 1180 OPT FNequb((zoing+diff) DIV 256) 1190 OPT FNequb((zoing+diff) MOD 256) 1200 OPT FNequb(&FF) 1210 .helpmsg 1220 OPT FNequb(&0D) 1230 OPT FNequs("MODULAR ROM") 1240 OPT FNequb(&0D) 1250 OPT FNequw(&2020) 1260 .helptitle 1270 OPT FNequs("MOD") 1280 OPT FNequw(&0DFF) 1290 BRK 1300 .helpinfo 1310 OPT FNequw(&200D) 1320 OPT FNequs(" ALARM") 1330 OPT FNequw(&200D) 1340 OPT FNequs(" REVERB") 1350 OPT FNequw(&200D) 1360 OPT FNequs(" ZOING") 1370 OPT FNequw(&000D) 1380 .alarm 1390 LDA #8 1400 LDX #(ealarm+diff) MOD 256 1410 LDY #(ealarm+diff) DIV 256 1420 JSR osword \ Envelope 1430 LDA #7 1440 LDX #(salarm+diff) MOD 256 1450 LDY #(salarm+diff) DIV 256 1460 JSR osword \ Sound 1470 .pullout 1480 PLA 1490 STA address+1 1500 PLA 1510 STA address 1520 PLA 1530 PLA 1540 PLA 1550 LDA #0 1560 RTS 1570 .reverb 1580 LDA #8 1590 LDX #(ereverb+diff) MOD 256 1600 LDY #(ereverb+diff) DIV 256 1610 JSR osword \ Envelope 1620 LDA #7 1630 LDX #(sreverb+diff) MOD 256 1640 LDY #(sreverb+diff) DIV 256 1650 JSR osword \ Sound 1660 JMP pullout+diff 1670 .zoing 1680 LDA #8 1690 LDX #(ezoing+diff) MOD 256 1700 LDY #(ezoing+diff) DIV 256 1710 JSR osword \ Envelope 1720 LDA #7 1730 LDX #(szoing+diff) MOD 256 1740 LDY #(szoing+diff) DIV 256 1750 JSR osword \ Sound 1760 JMP pullout+diff 1770 .ealarm 1780 OPT FNequd(&00010101) 1790 OPT FNequd(&00004800) 1800 OPT FNequd(&01FF00FD) 1810 OPT FNequw(&7E7E) 1820 .salarm 1830 OPT FNequd(&00010011) 1840 OPT FNequd(&00FF0032) 1850 .ereverb 1860 OPT FNequd(&FF010803) 1870 OPT FNequd(&01010101) 1880 OPT FNequd(&79F6FBFE) 1890 OPT FNequw(&7E7E) 1900 .sreverb 1910 OPT FNequd(&00030010) 1920 OPT FNequd(&00400000) 1930 .ezoing 1940 OPT FNequd(&EC140102) 1950 OPT FNequd(&64646414) 1960 OPT FNequd(&0000FF7F) 1970 OPT FNequd(&007E) 1980 .szoing 1990 OPT FNequd(&00020012) 2000 OPT FNequd(&003200CC) 2010 .lastbyte 2020 ] 2030 NEXT 2040 A%?1=((lastbyte+diff) MOD 256) 2050 B%?1=((lastbyte+diff) DIV 256) 2060 REM: Poke address of the start of 2070 REM: RFS into MODONE 2080 O%=lastbyte 2090 CHAIN"RFSGEN" 2100 END 2110 DEFFNequb(byte) 2120 ?P%=byte 2130 P%=P%+1 2140 =pass 2150 DEFFNequw(word) 2160 ?P%=word 2170 P%?1=word DIV 256 2180 P%=P%+2 2190 =pass 2200 DEFFNequd(double) 2210 !P%=double 2220 P%=P%+4 2230 =pass 2240 DEFFNequs(string$) 2250 $P%=string$ 2260 P%=P%+LEN(string$) 2270 =pass