Relocatable Code with RBOOT & RLOAD
EDASM: Relocatable Code with RBOOT & RLOAD.
The centerpiece of Apple Computer's ProDOS Assembler Tools is EDASM, a powerful, disk-based 65(c)02 macro assembler. One of its least used features is the ability to generate relocatable object modules using the REL pseudo-operation code (Listing 1). The feature is integrated with Applesoft Basic using the relocating loader tools, RBOOT and RLOAD.
Using the REL Directive.
The REL directive causes the assembler to append a relocation dictionary to the end of the object code. The relocating loader later uses this information to relocate the code module to a convenient place in memory between Applesoft and ProDOS. The REL directive should be placed at the beginning of the code, before any ORG directive or symbolic identifiers. The OBJ directive may not be used with REL. The object file produced by the REL directive has a file type of REL ($FE). Because the first two bytes of the file are the code's origin, such files cannot be BRUN.
The Relocating Loader.
The relocating loader consists of two programs, RBOOT and RLOAD. RBOOT loads RLOAD from disk and relocates it above the end of Applesoft variable storage. RLOAD then loads relocatable modules by name (using the Basic USR function) into memory and relocates them in place using the relocation dictionary produced by the assembler. Each module is loaded on a page boundary beneath ProDOS, and HIMEM is lowered appropriately using the routine GETBUFR.
Because the loader adjusts HIMEM, no strings may be allocated before invoking RLOAD, although numeric variables are preserved. Similarly, the program must not allocate any new variables until after the last use of RLOAD. After normal termination, the program should call FREBUFR ($BEF8, 48888) to restore memory used by the relocated modules.
The Basic program in Listing 1 invokes RBOOT, loads the example module MYMODULE and calls it. Note that the program allocates the numeric variable AD before running RBOOT. Also, note the use of CHR$(4) rather than a string variable, which would be overwritten by the RLOAD code itself.
The USR function takes one parameter: the full or partial pathname of the module to be loaded. If RLOAD can't find the module or encounters another error, the program exits with an error. Such errors can be trapped with Basic's ON ERR facility.
The USR function returns a signed real number that is the starting address of the relocated module. It is suitable for the Basic CALL statement. Although the relocating loader does not resolve external references, the effect of multiple entry points can be achieved with a table of JMP instruction at the beginning of the assembly code. The Basic program can then CALL AD, AD+3, AD+6, ... for each of the module's routines.
RBOOT internals.
RBOOT loads RLOAD and relocates it above Applesoft variable storage. As shown in Listing 2, it occupies memory from $218 to $3B1 to avoid conflict with Applesoft. RBOOT first checks to see if there is an established prefix. If not, it uses the volume name from the last accessed device. It then gets the RLOAD file's length and loads it into memory.
Once loaded, the code is relocated in place. Because RBOOT has limited memory, it uses a simple relocation scheme: the monitor's INSDS2 routine is called to calculate an instruction's length. Because the original code and the destination both start on a page boundary, only the high byte of a 3-byte instruction needs to be adjusted. Finally, the entry point of the relocated RLOAD code is stored in the USR vector on page zero.
RLOAD internals.
RLOAD loads the relocatable module named in the USR() function call, loads it below ProDOS, and relocates the code in place. As shown in Listing 3, RLOAD checks for a prefix, parses the module's name, and gets its length before loading the module's code image. NOte that a REL type file is required. The structure of the REL file is shown in Table E-1.
After loading the code, RLOAD scans the relocation dictionary, the structure of which is shown in Table E-2. Each record includes a set of flags and a field offset into the code image. The flags specify how each relocatable field is to be handled. The algorithm correctly handles reversed byte order (e.g. DDB) and hi/lo vector selection (e.g #>address). The dictionary includes an optional External Symbol Dictionary (ESD). The relocating loader does not resolve such symbols, but the extension to do so is straightforward.
Running with Applesoft Basic.
Both RBOOT and RLOAD are designed to run while Applesoft is executing. They rely extensively on the Basic global page documented in the ProDOS Technical Reference Manual, Appendix A.3. The global page ($BExx) symbols used in both listings follow the conventions used in that manual. In particular, note that GOSYSTEM returns via BADCALL, so any MLI errors have already been translated into Basic error codes. Also note that PRERR, the common error exit, pops the stack before returning to Basic via ERROUT. This allows ON ERR to handle failed attempts to load a module.
A bug in RLOAD.
To make room for the module, RLOAD calls GETBUFR to allocate space between ProDOS and HIMEM. Because this space includes the currently open file buffer, RLOAD calls Set_Buf to re-allocate the file's buffer. By default, RLOAD puts the new buffer four pages (1K) below the new code. This works well if the module is more than 1K in length. If not, the new buffer overlaps the old, and ProDOS signals fatal error #$C: "NO BUFFERS AVAILABLE." The minimal solution is to allocate the new buffer at least seven pages below the module in line 196. The extra space is reclaimed when the file is closed.
Conclusions.
It is interesting to compare the relocation methods used by RBOOT and RLOAD. RBOOT's simple relocation scheme requires that the code meet several constraints:
- All code must be contiguous and precede any data.
- Source and destination must start on a page boundary.
- An absolute-address instruction (e.g. BIT) must precede any vector (e.g. DATA and END) in order to ensure relocation.
- A zero byte is used as a sentinel to halt relocation.
In contrast, RLOAD's relocation algorithm can make full use of the information included in the REL file's relocation dictionary. With some care, the simple RBOOT approach may be used to relocate any assembler's code. Using the REL directive with EDASM and the relocating loader, arbitrary relocation can help conserve memory.
Acknowledgments.
Special thanks to the many participants on comp.sys.apple2.* who inspired this effort.
References.
ProDOS Assembler Tools, Apple Computer, Inc., 20525 Mariani Avenue, Cupertino, CA 95014, 1984.
ProDOS Technical Reference Manual, Apple Computer, Inc., 20525 Mariani Avenue, Cupertino, CA 95014, 1984.
Listing 1.
10 AD = 0 : REM Pre-allocate address variable
20 PRINT CHR$(4);"BRUN RBOOT"
30 AD = USR(0),"MYMODULE"
40 CALL AD
50 CALL 48888: REM Free module's space
SOURCE FILE #01 =>MYMODULE.S
0000: 1 REL
----- NEXT OBJECT FILE NAME IS MYMODULE
0800: 0800 2 ORG $800
0800: 0044 3 A5 EQU $44
0800: FDDA 4 PRBYTE EQU $FDDA
0800:A9 10 5 START LDA #$10
0802:85 44 6 STA A5
0804:A5 44 7 LOOP LDA A5
0806:20 0E 08 8 JSR OUTPUT
0809:C6 44 9 DEC A5
080B:D0 F7 0804 10 BNE LOOP
080D:60 11 RTS
080E:20 DA FD 12 OUTPUT JSR PRBYTE
0811:60 13 RTS
000000: 12 00 A9 10 85 44 A5 44 20 0E 08 C6 44 D0 F7 60 .....D.D ...D..`
000010: 20 DA FD 60 81 07 00 00 00 00 ..`......
Listing 2.
1 ;RBOOT: Load RLOAD above Applesoft array storage,
2 ;relocate it in place, and install its entry in USR(0).
3 ;Disassembled to preserve format information,
4 ;for study and to facilitate bug fixes in RLOAD.
5 ;
6 0006 PATHPTRL = $6
7 0007 PATHPTRH = $7
8 0008 TEMP = $8
9 000B USRL = $B
10 000C USRH = $C
11 002F INSLEN = $2F
12 0040 A3L = $40
13 0041 A3H = $41
14 006E STRENDH = $6E
15 0073 HIMEML = $73
16 0074 HIMEMH = $74
17 00DE ERRNUM = $DE
18 BE09 ERROUT = $BE09
19 BE6C VPATH1L = $BE6C
20 BE6D VPATH1H = $BE6D
21 BE70 GOSYSTEM = $BE70
22 BEB4 SSGINFO = $BEB4
23 BEB8 FIFILID = $BEB8
24 BEB9 FIAUXIDL = $BEB9
25 BEBA FIAUXIDH = $BEBA
26 BEC7 SREFNUM = $BEC7
27 BEC8 SBUFADRL = $BEC8
28 BEC9 SBUFADRH = $BEC9
29 BECE OSYSBUFL = $BECE
30 BECF OSYSBUFH = $BECF
31 BED0 OREFNUM = $BED0
32 BED6 RWREFNUM = $BED6
33 BED7 RWDATAL = $BED7
34 BED8 RWDATAH = $BED8
35 BED9 RWCOUNTL = $BED9
36 BEDA RWCOUNTH = $BEDA
37 BEDB RWTRANSL = $BEDB
38 BEDC RWTRANSH = $BEDC
39 BEDE CFREFNUM = $BEDE
40 BF30 DEVNUM = $BF30
41 BF9A PFIXPTR = $BF9A
42 F88E INSDS2 = $F88E
43 0218 *= $218
44 0218 START:
45 0218 4C 24 02 JMP START1
46 021B A0 ENDH .BYTE $A0 ;End page + 1
47 021C 00 STADRL .BYTE $0 ;Start address
48 021D 00 STADRH .BYTE $0
49 021E 00 LENL .BYTE $0 ;Code length
50 021F 00 LENH .BYTE $0
51 0220 00 DSADRL .BYTE $0 ;Destination address
52 0221 00 DSADRH .BYTE $0
53 0222 00 ENADRL .BYTE $0 ;End address
54 0223 00 ENADRH .BYTE $0
55 0224 START1: ;Initialize path buffer
56 0224 A9 FF LDA #$FF
57 0226 85 08 STA TEMP
58 0228 AD 6C BE LDA VPATH1L
59 022B 85 06 STA PATHPTRL
60 022D AD 6D BE LDA VPATH1H
61 0230 85 07 STA PATHPTRH
62 0232 A9 00 LDA #$0
63 0234 A8 TAY
64 0235 91 06 STA (PATHPTRL),Y
65 0237 AD 9A BF LDA PFIXPTR
66 023A D0 35 BNE PFIXSET
67 023C NOPFIX: ;Use last device
68 023C AD 30 BF LDA DEVNUM
69 023F 8D C7 BE STA SREFNUM
70 0242 A6 07 LDX PATHPTRH
71 0244 A4 06 LDY PATHPTRL
72 0246 C8 INY ;Start in byte 1
73 0247 D0 01 BNE GETVNAME
74 0249 E8 INX
75 024A GETVNAME: ;Get the volume name
76 024A 8C C8 BE STY SBUFADRL
77 024D 8E C9 BE STX SBUFADRH
78 0250 A9 C5 LDA #$C5 ;On_Line
79 0252 20 70 BE JSR GOSYSTEM
80 0255 90 04 BCC MAKEPATH
81 0257 4C 09 BE JMP ERROUT
82 025A 00 .BYTE $0
83 025B MAKEPATH: ;Volume name -> path
84 025B A0 01 LDY #$1
85 025D B1 06 LDA (PATHPTRL),Y
86 025F 29 0F AND #$F ;Length <= 15 chars
87 0261 AA TAX
88 0262 A9 2F LDA #'/' ;Replace length with /
89 0264 91 06 STA (PATHPTRL),Y
90 0266 E8 INX ;Adjust length
91 0267 E8 INX
92 0268 8A TXA
93 0269 88 DEY
94 026A 91 06 STA (PATHPTRL),Y
95 026C A8 TAY
96 026D A9 2F LDA #'/' ;Append /
97 026F 91 06 STA (PATHPTRL),Y
98 0271 PFIXSET: ;Get prefix length
99 0271 A0 00 LDY #$0
100 0273 B1 06 LDA (PATHPTRL),Y
101 0275 A2 00 LDX #$0
102 0277 A8 TAY
103 0278 CPFNAME: ;Add filename to prefix
104 0278 C8 INY
105 0279 BD 8A 02 LDA FNAME,X
106 027C 91 06 STA (PATHPTRL),Y
107 027E E8 INX
108 027F E0 05 CPX #$5
109 0281 90 F5 BCC CPFNAME
110 0283 FIXLEN: ;Adjust length
111 0283 98 TYA
112 0284 A0 00 LDY #$0
113 0286 91 06 STA (PATHPTRL),Y
114 0288 F0 05 BEQ GETINFO
115 028A FNAME:
116 028A 52 4C 4F .BYTE "RLOAD"
028D 41 44
117 028F GETINFO:
118 028F A9 0A LDA #$A ;Parameter count
119 0291 8D B4 BE STA SSGINFO
120 0294 A9 C4 LDA #$C4 ;Get_File_Info
121 0296 20 70 BE JSR GOSYSTEM
122 0299 90 04 BCC CHKFTYPE
123 029B 4C 09 BE JMP ERROUT
124 029E 00 .BYTE $0
125 029F CHKFTYPE:
126 029F AD B8 BE LDA FIFILID
127 02A2 C9 06 CMP #$6 ;Binary file?
128 02A4 F0 06 BEQ OPENFILE
129 02A6 A9 0D LDA #$D ;File type mismatch
130 02A8 PRERR1:
131 02A8 4C 09 BE JMP ERROUT
132 02AB 00 .BYTE $0
133 02AC OPENFILE: ;Open the file
134 02AC AD B9 BE LDA FIAUXIDL ;Save start address
135 02AF 8D 1C 02 STA STADRL
136 02B2 AD BA BE LDA FIAUXIDH
137 02B5 8D 1D 02 STA STADRH
138 02B8 A5 73 LDA HIMEML ;Use the default buffer
139 02BA 8D CE BE STA OSYSBUFL
140 02BD A5 74 LDA HIMEMH
141 02BF 8D CF BE STA OSYSBUFH
142 02C2 A9 C8 LDA #$C8 ;Open
143 02C4 20 70 BE JSR GOSYSTEM
144 02C7 B0 DF BCS PRERR1
145 02C9 GETEOF: ;Get end of file
146 02C9 AD D0 BE LDA OREFNUM
147 02CC 8D C7 BE STA SREFNUM
148 02CF A9 D1 LDA #$D1 ;Get_EOF
149 02D1 20 70 BE JSR GOSYSTEM
150 02D4 B0 74 BCS PRERR2
151 02D6 CHKMEM: ;Decide where
152 02D6 AD C8 BE LDA SBUFADRL ;Save the file length
153 02D9 8D 1E 02 STA LENL
154 02DC 8D 22 02 STA ENADRL
155 02DF 8D D9 BE STA RWCOUNTL
156 02E2 AD C9 BE LDA SBUFADRH
157 02E5 8D 1F 02 STA LENH
158 02E8 8D DA BE STA RWCOUNTH
159 02EB A9 00 LDA #$0
160 02ED 85 08 STA TEMP
161 02EF 8D D7 BE STA RWDATAL
162 02F2 8D 20 02 STA DSADRL
163 02F5 A6 6E LDX STRENDH
164 02F7 E8 INX ;Relocate on page
165 02F8 E8 INX ;boundary two pages up
166 02F9 8E 21 02 STX DSADRH
167 02FC 8E D8 BE STX RWDATAH
168 02FF 18 CLC
169 0300 AD C9 BE LDA SBUFADRH
170 0303 6D 21 02 ADC DSADRH
171 0306 8D 23 02 STA ENADRH ;ENADR := DSADR + length
172 0309 AD 22 02 LDA ENADRL ;Enough room?
173 030C C5 73 CMP HIMEML
174 030E AD 23 02 LDA ENADRH
175 0311 E5 74 SBC HIMEMH
176 0313 90 05 BCC READFILE
177 0315 A9 0E LDA #$E ;Program too large
178 0317 4C 09 BE JMP ERROUT
179 031A READFILE: ;Read in the code
180 031A AD D0 BE LDA OREFNUM
181 031D 8D D6 BE STA RWREFNUM
182 0320 A9 00 LDA #$0
183 0322 8D DB BE STA RWTRANSL
184 0325 8D DC BE STA RWTRANSH
185 0328 A9 CA LDA #$CA ;Read
186 032A 20 70 BE JSR GOSYSTEM
187 032D B0 1B BCS PRERR2
188 032F 20 56 03 JSR CLOSEFILE
189 0332 20 67 03 JSR RELOCATE ;Relocate it
190 0335 AD 20 02 LDA DSADRL ;Save the entry in USR()
191 0338 85 0B STA USRL
192 033A AD 21 02 LDA DSADRH
193 033D 85 0C STA USRH
194 033F AD 22 02 LDA ENADRL
195 0342 85 06 STA PATHPTRL
196 0344 AD 23 02 LDA ENADRH
197 0347 85 07 STA PATHPTRH
198 0349 60 RTS
199 034A PRERR2: ;Close then print error
200 034A 85 DE STA ERRNUM
201 034C 85 08 STA TEMP
202 034E 20 56 03 JSR CLOSEFILE
203 0351 A5 08 LDA TEMP
204 0353 4C 09 BE JMP ERROUT
205 0356 CLOSEFILE: ;Close the file
206 0356 AD D0 BE LDA OREFNUM
207 0359 8D DE BE STA CFREFNUM
208 035C A9 CC LDA #$CC
209 035E 20 70 BE JSR GOSYSTEM
210 0361 90 03 BCC CLOSERTS
211 0363 4C 09 BE JMP ERROUT
212 0366 CLOSERTS:
213 0366 60 RTS
214 0367 RELOCATE: ;Relocate the code in place
215 0367 AD 20 02 LDA DSADRL ;A3 := start of code
216 036A 85 40 STA A3L
217 036C AD 21 02 LDA DSADRH
218 036F 85 41 STA A3H
219 0371 AD 1F 02 LDA LENH
220 0374 38 SEC
221 0375 6D 1D 02 ADC STADRH
222 0378 8D 1B 02 STA ENDH ;Hi byte of code end + 1
223 037B RELOC1:
224 037B A0 00 LDY #$0
225 037D B1 40 LDA (A3L),Y
226 037F F0 30 BEQ RELOCRTS ;0 -> end of code
227 0381 20 8E F8 JSR INSDS2 ;Use monitor routine
228 0384 A4 2F LDY INSLEN ;for instruction length
229 0386 C0 02 CPY #$2 ;Only 3-byte instructions
230 0388 D0 18 BNE RELOC2
231 038A B1 40 LDA (A3L),Y
232 038C CD 1B 02 CMP ENDH ;Beyond end?
233 038F B0 11 BCS RELOC2
234 0391 CD 1D 02 CMP STADRH ;Before start?
235 0394 90 0C BCC RELOC2
236 0396 AD 21 02 LDA DSADRH ;Adjust hi byte
237 0399 18 CLC
238 039A 71 40 ADC (A3L),Y
239 039C 38 SEC
240 039D ED 1D 02 SBC STADRH
241 03A0 91 40 STA (A3L),Y
242 03A2 RELOC2: ;Next instruction
243 03A2 98 TYA ;Y = instruction length - 1
244 03A3 38 SEC
245 03A4 65 40 ADC A3L
246 03A6 85 40 STA A3L
247 03A8 A5 41 LDA A3H
248 03AA 69 00 ADC #$0
249 03AC 85 41 STA A3H
250 03AE B8 CLV
251 03AF 50 CA BVC RELOC1
252 03B1 RELOCRTS:
253 03B1 60 RTS
Listing 3.
1 ;RLOAD: Load & relocate modules above HIMEM
2 ;via Applesoft USR(0) interface.
3 ;Disassembled to preserve format information,
4 ;for study and to facilitate bug fixes in RLOAD.
5 ;
6 0006 PTRL = $6
7 0007 PTRH = $7
8 0008 ADRL = $8
9 0009 ADRH = $9
10 0073 HIMEML = $73
11 0074 HIMEMH = $74
12 00B1 CHRGET = $B1
13 00B7 CHRGOT = $B7
14 02A9 NINE = $2A9
15 02FD TEMP = $2FD
16 BE09 ERROUT = $BE09
17 BE6C VPATH1L = $BE6C
18 BE6D VPATH1H = $BE6D
19 BE70 GOSYSTEM = $BE70
20 BEB4 SSGINFO = $BEB4
21 BEB8 FIFILID = $BEB8
22 BEB9 FIAUXIDL = $BEB9
23 BEBA FIAUXIDH = $BEBA
24 BEC7 SREFNUM = $BEC7
25 BEC8 SBUFADRL = $BEC8
26 BEC9 SBUFADRH = $BEC9
27 BECE OSYSBUFL = $BECE
28 BECF OSYSBUFH = $BECF
29 BED0 OREFNUM = $BED0
30 BED6 RWREFNUM = $BED6
31 BED7 RWDATAL = $BED7
32 BED8 RWDATAH = $BED8
33 BED9 RWCOUNTL = $BED9
34 BEDA RWCOUNTH = $BEDA
35 BEDE CFREFNUM = $BEDE
36 BEF5 GETBUFR = $BEF5
37 BF30 DEVNUM = $BF30
38 BF9A PFIXPTR = $BF9A
39 C081 ROMIN = $C081
40 E2F2 GIVAYF = $E2F2
41 0800 *= $800
42 0800 START: ;Initialize path buffer
43 0800 AD 6C BE LDA VPATH1L
44 0803 85 06 STA PTRL
45 0805 AD 6D BE LDA VPATH1H
46 0808 85 07 STA PTRH
47 080A A9 00 LDA #$0
48 080C A8 TAY
49 080D 91 06 STA (PTRL),Y
50 080F AD 9A BF LDA PFIXPTR
51 0812 D0 34 BNE PFIXSET
52 0814 NOPFIX: ;Use last device
53 0814 AD 30 BF LDA DEVNUM
54 0817 8D C7 BE STA SREFNUM
55 081A A6 07 LDX PTRH
56 081C A4 06 LDY PTRL
57 081E C8 INY
58 081F D0 01 BNE GETVNAME
59 0821 E8 INX
60 0822 GETVNAME: ;Get the volume name
61 0822 8C C8 BE STY SBUFADRL
62 0825 8E C9 BE STX SBUFADRH
63 0828 A9 C5 LDA #$C5 ;On_Line
64 082A 20 70 BE JSR GOSYSTEM
65 082D 90 03 BCC MAKEPATH
66 082F 20 B7 0A JSR PRERR
67 0832 MAKEPATH: ;Volume name -> path
68 0832 A0 01 LDY #$1
69 0834 B1 06 LDA (PTRL),Y
70 0836 29 0F AND #$F ;Length <= 15 chars
71 0838 AA TAX
72 0839 A9 2F LDA #'/' ;Replace length with /
73 083B 91 06 STA (PTRL),Y
74 083D E8 INX ;Adding 2 chars
75 083E E8 INX
76 083F 8A TXA
77 0840 88 DEY
78 0841 91 06 STA (PTRL),Y ;Update string length
79 0843 A8 TAY
80 0844 A9 2F LDA #'/' ;Append /
81 0846 91 06 STA (PTRL),Y
82 0848 PFIXSET: ;Prepare to append name
83 0848 A0 00 LDY #$0
84 084A B1 06 LDA (PTRL),Y
85 084C A8 TAY
86 084D C8 INY
87 084E 20 B7 00 JSR CHRGOT
88 0851 PARSEUSR: ; Parse USR(0),"MODULE"
89 0851 D0 08 BNE PARSE1
90 0853 PARSERR:
91 0853 A9 0B LDA #$B ;Invalid parameter
92 0855 2C A9 02 BIT NINE ;Lo byte of ERROUT in RBOOT?
93 0858 20 B7 0A JSR PRERR
94 085B PARSE1:
95 085B 90 F6 BCC PARSERR ;C clear if digit
96 085D C9 2C CMP #',' ;Optional comma
97 085F D0 06 BNE PARSE2
98 0861 20 B1 00 JSR CHRGET
99 0864 4C 51 08 JMP PARSEUSR
100 0867 PARSE2:
101 0867 C9 22 CMP #'"' ;Open quote?
102 0869 D0 E8 BNE PARSERR
103 086B 4C 75 08 JMP PARSE3
104 086E CPFNAME: ;Copy file name
105 086E 91 06 STA (PTRL),Y
106 0870 C8 INY
107 0871 C0 7E CPY #$7E ;Path < 128 chars
108 0873 B0 10 BCS SETLEN
109 0875 PARSE3:
110 0875 20 B1 00 JSR CHRGET
111 0878 F0 0B BEQ SETLEN
112 087A 90 F2 BCC CPFNAME
113 087C C9 22 CMP #'"' ;Close quote?
114 087E D0 EE BNE CPFNAME
115 0880 20 B1 00 JSR CHRGET
116 0883 D0 CE BNE PARSERR
117 0885 SETLEN: ;Set path length
118 0885 88 DEY
119 0886 98 TYA
120 0887 A0 00 LDY #$0
121 0889 91 06 STA (PTRL),Y
122 088B A9 00 LDA #$0
123 088D 85 08 STA ADRL ;ADR := 0
124 088F 85 09 STA ADRH
125 0891 A9 0A LDA #$A ;Parameter count
126 0893 8D B4 BE STA SSGINFO
127 0896 A9 C4 LDA #$C4 ;Get_Info
128 0898 20 70 BE JSR GOSYSTEM
129 089B 90 03 BCC CHKFTYPE
130 089D 20 B7 0A JSR PRERR
131 08A0 CHKFTYPE: ;Check file type
132 08A0 AD B8 BE LDA FIFILID
133 08A3 C9 FE CMP #$FE ;REL file required
134 08A5 F0 05 BEQ OPENFILE
135 08A7 A9 0D LDA #$D ;File type mismatch
136 08A9 PRERR1:
137 08A9 20 B7 0A JSR PRERR
138 08AC OPENFILE:
139 08AC AD B9 BE LDA FIAUXIDL ;PTR := module's ORG address
140 08AF 85 06 STA PTRL
141 08B1 AD BA BE LDA FIAUXIDH
142 08B4 85 07 STA PTRH
143 08B6 A5 73 LDA HIMEML ;Use default buffer
144 08B8 8D CE BE STA OSYSBUFL
145 08BB A5 74 LDA HIMEMH
146 08BD 8D CF BE STA OSYSBUFH
147 08C0 A9 C8 LDA #$C8 ;Open
148 08C2 20 70 BE JSR GOSYSTEM
149 08C5 B0 E2 BCS PRERR1
150 08C7 AD D0 BE LDA OREFNUM
151 08CA 8D D8 0A STA FREFNUM ;Save the file's refnum
152 08CD 20 7C 0A JSR GETBYTE ;VAL := module's length
153 08D0 8D D1 0A STA VAL
154 08D3 20 7C 0A JSR GETBYTE
155 08D6 8D D2 0A STA VAL+1
156 08D9 AA TAX
157 08DA AD D1 0A LDA VAL
158 08DD F0 01 BEQ CHKMEM
159 08DF E8 INX
160 08E0 CHKMEM: ;Is there room?
161 08E0 8A TXA
162 08E1 85 09 STA ADRH ;ADR := dest address
163 08E3 A5 74 LDA HIMEMH
164 08E5 38 SEC
165 08E6 E5 09 SBC ADRH
166 08E8 85 09 STA ADRH
167 08EA A9 00 LDA #$0 ;Start on page boundary
168 08EC 85 08 STA ADRL
169 08EE AD CC 0A LDA END ;Check END
170 08F1 C5 08 CMP ADRL
171 08F3 AD CD 0A LDA END+1
172 08F6 E5 09 SBC ADRH
173 08F8 90 08 BCC PAGE
174 08FA A9 0E LDA #$E ;Program too large
175 08FC PRERR2: ;Close then exit via error
176 08FC 20 61 0A JSR CLOSE
177 08FF 20 B7 0A JSR PRERR
178 0902 PAGE: ;Calculate pages needed
179 0902 AE D2 0A LDX VAL+1
180 0905 AD D1 0A LDA VAL
181 0908 F0 01 BEQ MAKEROOM
182 090A E8 INX
183 090B MAKEROOM: ;Allocate space above HIMEM
184 090B 8A TXA
185 090C 20 F5 BE JSR GETBUFR
186 090F 90 05 BCC MVBUFFER
187 0911 A9 0E LDA #$E ;Program too large
188 0913 20 FC 08 JSR PRERR2
189 0916 MVBUFFER:
190 0916 85 09 STA ADRH ;ADR := dest address
191 0918 38 SEC
192 ;Reallocate the current file buffer using Set_Buf.
193 ;The original value was 4 pages for a 1K buffer.
194 ;This fails for modules < 1K, as the new
195 ;buffer overlaps the old. Should be >= #$7.
196 0919 E9 04 SBC #$4
197 091B 8D C9 BE STA SBUFADRH
198 091E A9 00 LDA #$0
199 0920 8D C8 BE STA SBUFADRL
200 0923 AD D0 BE LDA OREFNUM
201 0926 8D C7 BE STA SREFNUM
202 0929 A9 D2 LDA #$D2 ;Set_Buf
203 092B 20 70 BE JSR GOSYSTEM
204 092E 90 05 BCC READFILE
205 0930 A9 0C LDA #$C ;No buffers available
206 0932 20 FC 08 JSR PRERR2
207 0935 READFILE: ;Read the file
208 0935 A5 08 LDA ADRL ;OFFSET := ADR - PTR
209 0937 38 SEC
210 0938 E5 06 SBC PTRL
211 093A 8D CF 0A STA OFFSET
212 093D A5 09 LDA ADRH
213 093F E5 07 SBC PTRH
214 0941 8D D0 0A STA OFFSET+1
215 0944 A5 08 LDA ADRL ;DSTADR := ADR
216 0946 8D D3 0A STA DSTADR
217 0949 A5 09 LDA ADRH
218 094B 8D D4 0A STA DSTADR+1
219 094E AD D1 0A LDA VAL ;PTR := DSTADR + VAL
220 0951 18 CLC
221 0952 6D D3 0A ADC DSTADR
222 0955 85 06 STA PTRL
223 0957 AD D2 0A LDA VAL+1
224 095A 6D D4 0A ADC DSTADR+1
225 095D 85 07 STA PTRH
226 095F AD D1 0A LDA VAL ;Read VAL bytes
227 0962 8D D9 BE STA RWCOUNTL
228 0965 AD D2 0A LDA VAL+1
229 0968 8D DA BE STA RWCOUNTH
230 096B AD D3 0A LDA DSTADR ;Into DSTADR
231 096E 8D D7 BE STA RWDATAL
232 0971 AD D4 0A LDA DSTADR+1
233 0974 8D D8 BE STA RWDATAH
234 0977 AD D8 0A LDA FREFNUM ;From FREFNUM
235 097A 8D D6 BE STA RWREFNUM
236 097D A9 CA LDA #$CA ;Read
237 097F 20 70 BE JSR GOSYSTEM
238 0982 90 03 BCC RELOCAT
239 0984 4C FC 08 JMP PRERR2
240 0987 RELOCAT: ;Apply 4-byte relocation recs.
241 0987 A9 00 LDA #$0 ;VAL := 0
242 0989 8D D1 0A STA VAL
243 098C 8D D2 0A STA VAL+1
244 098F 20 7C 0A JSR GETBYTE ;Byte1, relocation flags
245 0992 8D D6 0A STA RFLAGS
246 0995 20 7C 0A JSR GETBYTE ;Byte 2, field offset lo
247 0998 18 CLC
248 0999 6D D3 0A ADC DSTADR ;ADR := DSTADR + field offset
249 099C 85 08 STA ADRL
250 099E 08 PHP
251 099F 20 7C 0A JSR GETBYTE ;Byte 3, field offset hi
252 09A2 28 PLP
253 09A3 6D D4 0A ADC DSTADR+1 ;Add offset hi
254 09A6 85 09 STA ADRH
255 09A8 A5 08 LDA ADRL ;Range check
256 09AA CD D3 0A CMP DSTADR ;Check start address
257 09AD A5 09 LDA ADRH
258 09AF ED D4 0A SBC DSTADR+1
259 09B2 90 0A BCC RELOC1
260 09B4 A5 08 LDA ADRL ;Check end address
261 09B6 C5 06 CMP PTRL
262 09B8 A5 09 LDA ADRH
263 09BA E5 07 SBC PTRH
264 09BC 90 05 BCC RELOC2
265 09BE RELOC1:
266 09BE A9 10 LDA #$10 ;Syntax error
267 09C0 20 FC 08 JSR PRERR2
268 09C3 RELOC2: ;Check relocation flags
269 09C3 A0 00 LDY #$0
270 09C5 A9 FF LDA #$FF
271 09C7 2C D6 0A BIT RFLAGS
272 09CA F0 72 BEQ EXIT ;0 -> end of relocation recs.
273 09CC 30 2B BMI RELOC4 ;Bit 7 set -> 2 byte field
274 09CE 70 13 BVS RELOC3 ;Bit 6 set -> hi byte
275 09D0 B1 08 LDA (ADRL),Y ;Adjust lo byte only
276 09D2 8D D1 0A STA VAL
277 09D5 20 4D 0A JSR ADDOFF
278 09D8 AD D1 0A LDA VAL
279 09DB 91 08 STA (ADRL),Y
280 09DD 20 7C 0A JSR GETBYTE ;Skip byte 4
281 09E0 4C 87 09 JMP RELOCAT ;Next record
282 09E3 RELOC3: ;Adjust hi byte
283 09E3 B1 08 LDA (ADRL),Y
284 09E5 8D D2 0A STA VAL+1
285 09E8 20 7C 0A JSR GETBYTE ;Byte 4 is lo byte
286 09EB 8D D1 0A STA VAL
287 09EE 20 4D 0A JSR ADDOFF
288 09F1 AD D2 0A LDA VAL+1
289 09F4 91 08 STA (ADRL),Y
290 09F6 4C 87 09 JMP RELOCAT ;Next record
291 09F9 RELOC4: ;Handle 2 byte field
292 09F9 A9 20 LDA #$20 ;Check order flag
293 09FB 2D D6 0A AND RFLAGS
294 09FE D0 1F BNE RELOC5 ;Reversed
295 0A00 B1 08 LDA (ADRL),Y ;Normal (lo/hi) order
296 0A02 8D D1 0A STA VAL
297 0A05 C8 INY
298 0A06 B1 08 LDA (ADRL),Y
299 0A08 8D D2 0A STA VAL+1
300 0A0B 20 4D 0A JSR ADDOFF
301 0A0E AD D2 0A LDA VAL+1
302 0A11 91 08 STA (ADRL),Y
303 0A13 88 DEY
304 0A14 AD D1 0A LDA VAL
305 0A17 91 08 STA (ADRL),Y
306 0A19 20 7C 0A JSR GETBYTE ;Skip byte 4
307 0A1C 4C 87 09 JMP RELOCAT ;Next record
308 0A1F RELOC5: ;Reverse (hi/lo) order
309 0A1F B1 08 LDA (ADRL),Y
310 0A21 8D D2 0A STA VAL+1
311 0A24 C8 INY
312 0A25 B1 08 LDA (ADRL),Y
313 0A27 8D D1 0A STA VAL
314 0A2A 20 4D 0A JSR ADDOFF
315 0A2D AD D1 0A LDA VAL
316 0A30 91 08 STA (ADRL),Y
317 0A32 88 DEY
318 0A33 AD D2 0A LDA VAL+1
319 0A36 91 08 STA (ADRL),Y
320 0A38 20 7C 0A JSR GETBYTE ;Skip byte 4
321 0A3B 4C 87 09 JMP RELOCAT ;Next record
322 0A3E EXIT:
323 0A3E 20 61 0A JSR CLOSE ;Close this module's file
324 0A41 AC D3 0A LDY DSTADR ;YA := module entry
325 0A44 AD D4 0A LDA DSTADR+1
326 0A47 2C 81 C0 BIT ROMIN
327 0A4A 4C F2 E2 JMP GIVAYF ;Leave in FAC as USR result
328 0A4D ADDOFF: ;VAL := VAL + OFFSET
329 0A4D 18 CLC
330 0A4E AD CF 0A LDA OFFSET
331 0A51 6D D1 0A ADC VAL
332 0A54 8D D1 0A STA VAL
333 0A57 AD D0 0A LDA OFFSET+1
334 0A5A 6D D2 0A ADC VAL+1
335 0A5D 8D D2 0A STA VAL+1
336 0A60 60 RTS
337 0A61 CLOSE:
338 0A61 48 PHA
339 0A62 98 TYA
340 0A63 48 PHA
341 0A64 8A TXA
342 0A65 48 PHA
343 0A66 AD D8 0A LDA FREFNUM
344 0A69 8D DE BE STA CFREFNUM
345 0A6C A9 CC LDA #$CC ;Close
346 0A6E 20 70 BE JSR GOSYSTEM
347 0A71 90 03 BCC CLOSE1
348 0A73 20 B7 0A JSR PRERR
349 0A76 CLOSE1:
350 0A76 68 PLA
351 0A77 AA TAX
352 0A78 68 PLA
353 0A79 A8 TAY
354 0A7A 68 PLA
355 0A7B 60 RTS
356 0A7C GETBYTE: ;Read a byte from FREFNUM
357 0A7C 98 TYA
358 0A7D 48 PHA
359 0A7E 8A TXA
360 0A7F 48 PHA
361 0A80 AD D8 0A LDA FREFNUM
362 0A83 8D D6 BE STA RWREFNUM
363 0A86 A9 01 LDA #$1
364 0A88 8D D9 BE STA RWCOUNTL
365 0A8B A9 00 LDA #$0
366 0A8D 8D DA BE STA RWCOUNTH
367 0A90 AD C9 0A LDA DATA
368 0A93 8D D7 BE STA RWDATAL
369 0A96 AD CA 0A LDA DATA+1
370 0A99 8D D8 BE STA RWDATAH
371 0A9C A9 CA LDA #$CA ;Read
372 0A9E 20 70 BE JSR GOSYSTEM
373 0AA1 90 09 BCC GETBYTE1
374 0AA3 C9 05 CMP #$5
375 0AA5 D0 0D BNE GETBYTE2
376 0AA7 A9 00 LDA #$0
377 0AA9 8D D7 0A STA BYTEBUF
378 0AAC GETBYTE1:
379 0AAC 68 PLA
380 0AAD AA TAX
381 0AAE 68 PLA
382 0AAF A8 TAY
383 0AB0 AD D7 0A LDA BYTEBUF
384 0AB3 60 RTS
385 0AB4 GETBYTE2:
386 0AB4 20 FC 08 JSR PRERR2
387 0AB7 PRERR: ;Exit via ERROUT
388 0AB7 8D FD 02 STA TEMP ;Save the code
389 0ABA 68 PLA ;Pop return stack
390 0ABB 8D FE 02 STA TEMP+1
391 0ABE 68 PLA
392 0ABF 8D FF 02 STA TEMP+2
393 0AC2 AD FD 02 LDA TEMP ;Restore code
394 0AC5 4C 09 BE JMP ERROUT
395 ;BIT instruction to force relocation
396 ;of vectors by RBOOT's simple method
397 0AC8 2C .BYTE $2C
398 0AC9 D7 0A DATA .WORD BYTEBUF
399 0ACB 2C .BYTE $2C ;Ditto
400 0ACC D9 0A END .WORD ENDCODE
401 0ACE 00 .BYTE $0
402 0ACF EE EE OFFSET .WORD $EEEE
403 0AD1 EE EE VAL .WORD $EEEE
404 0AD3 EE EE DSTADR .WORD $EEEE
405 0AD5 00 .BYTE $0
406 0AD6 01 RFLAGS .BYTE $1
407 0AD7 00 BYTEBUF .BYTE $0
408 0AD8 00 FREFNUM .BYTE $0
409 0AD9 ENDCODE =*
Copyright 2006 John B. Matthews
Distribution permitted under the terms of the GPL: http://www.gnu.org/copyleft/gpl.html.
Last updated 9-Mar-2009