|
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:
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. |
Using the REL Directive The Relocating Loader RBOOT Internals RLOAD Internals Running with Applesoft Basic A Bug in RLOAD Conclusions Acknowledgments References Example Listing RBOOT Listing RLOAD Listing Table A-1: Editor Commands Table A-2: Edit Mode Keys Table B-1: Assembler Directives Table E-1: REL File Format Table E-2: RLD Format Symbol Table Format |
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 =*
| Table A-1: Editor commands; caps significant; bracketed items optional. | |
|---|---|
| Add [line#] | Add line(s) to the text buffer, [starting with line#]. |
| APPEND [n] pathname | Replace line number n and following with the specified file. |
| ASM pathname[,objpathname] | Assemble pathname to pathname.[0-n] (default) or objpathname; @ means no object file. |
| BLOAD pathname,A[$]address | Load binary pathname into the specified address. |
| BSAVE pathname,A[$]address,L[$]length | Save binary pathname from the specified address & length. |
| CAT [path] | Display a short catalog, using the current prefix or the specified path. |
| CATALOG [path] | Display a long catalog, using the current prefix or the specified path. |
| COLumn 40 | Set the editor to 40-column mode. |
| COLumn 80 | Set the editor to 80-column mode. |
| Copy a [-b] TO c | Insert line number a [through line b] before line c. |
| CREATE path | Create a new subdirectory. |
| Change [a [-b]] .old.new. | Change old string to new string [in line numbers a through b]. |
| DELETE pathname | Delete the file specified by pathname. |
| Del a [-b] | Delete lines a though b from text buffer. |
| Edit [a [-b]] [.string.] | Edit each line [in the range a through b] containing string. |
| END | Exit to Basic. |
| EXEC pathname | Execute the EDASM commands in pathname. |
| EXIT [pathname] | Exit to Basic [or the specified pathname]. |
| FILE | Display information about the current file in the text buffer. |
| Find [a [-b]] [.string.] | Display the lines containing string [in the range 1 through b[. |
| Insert a | Insert lines before line number a. |
| KILL2 | Return to single-buffer mode, killing contents of buffer two. |
| List [a [-b]] | List line a through b. |
| LOCK pathname | Lock the file with the specified pathname. |
| LOaD pathname | Load the text file in pathname into the text buffer. |
| MON | Enter the monitor' ctrl-Y returns to the editor. |
| NEW | Delete the entire contents of the text buffer. |
| Online | List volumes online. |
| PreFIX [/][path] | Display the current prefix or set it to path. |
| Print [a [-b]] | Display lines [a through b] without line numbers. |
| PR# n [,DevInitString] | Send output to printer in slot n [with an optional initialization string]. |
| PR# n,[Pp][Ll]pathname | Send output to pathname on disk in slot n [with physical & logical page length p and l]. |
| PTROFF | Disable printer output. |
| PTRON | Enable printer output. |
| RENAME oldpathname,newpathname | Rename oldpathname to newpathname; file must be unlocked. |
| Replace a [-b] | Replace lines a [through b] and insert new line(s). |
| SaVE [a [-b]] pathname | Save [lines a through b] to pathname. |
| SET Delim [.x.] | Set the command separator to : (default) or x = [, ], \, ^ or _. |
| SET Lcase | Set lowercase input on Apple ][. |
| SET Ucase | Set uppercase input on Apple ][. |
| SWAP | Swap the currently active text buffer for the alternate. |
| Tabs [a[,b[,c[...]]]] [.tabchar.] | Set tabs stops in columns a, b, c ... j, using tabchar. |
| TRuncON | Truncate comment display. |
| TRuncOFF | Don't truncate comment display. |
| UNLOCK pathname | Unlock the file having pathname. |
| Where n | Show the memory address of line n. |
| XLOAD [pathname,A[$]address] | Load a non-binary file [into address A[$]]. |
| XSAVE [pathname,A[$]address [,L[$]length]] | Save a non-binary file using the type, address and length from the previous XLOAD [optionally using address A[$] and length L[$]]. |
| Table A-2: Edit mode control keys. | |
|---|---|
| Left arrow | Move cursor left one character. |
| Right arrow | Move cursor right one character. |
| Return | Accept the line as it appears. |
| Text | Typed text replaces existing text. |
| Control-D | Delete current character. |
| Control-E | Enable lowercase input (Apple ][/+). |
| Control-F | Find the next character typed. |
| Control-I | Insert character(s) before the cursor, esc returns to replace mode. |
| Control-R | Restore the original line. |
| Control-T | Truncate the line at the cursor. |
| Control-V | Enter the next character verbatim. |
| Control-W | Disable lowercase input (Apple ][/+). |
| Table B-1: Assembler directives. | |
|---|---|
| ASC dstring | Assemble ASCII character data from delimited string. |
| CHN pathname | Chain to the new source file in pathname. |
| CHR c | Character to use for REP directive. |
| DATE | The date in mm-ddd-yy format. |
| DCI dstring | ASCII character data, last byte negative. |
| DDB expr[,expr] | Define double byte, hi/lo order. |
| DEF id | Define external entry point. |
| DEND | End data section. |
| DFB expr[,expr] | Define byte(s). |
| DO expr | Do assembly if expr > 0. |
| DS expr[,expr] | Define storage. |
| DSECT | Begin data section. |
| DW expr[,expr] | Define words, lo/hi order. |
| ELSE | Conditional assembly alternative. |
| ENTRY id | Define external entry point. |
| id EQU expr | Set identifier to expr. |
| EXTERN id | Mark id as external. |
| FAIL p,dstring | Fail assembly on pass p with message string. |
| FIN | Finish conditional assembly. |
| IBUFSIZ expr | Set include buffer size in pages, default 16. |
| IDNUM | The time in hh:mm format. |
| IFxx expr | Conditionally assemble based on xx & expr: EQ=0, NE!=0, LT<0, LE<=0, GE>=0, GT>0. |
| INCLUDE | Include source in pathname. |
| LST [NO]option | List on, off, cyc (cycle count), gen (show all generated code), warn (warnings), unasm (show unassembled conditional code), asym (alpha symbol table), vsym (value symbol table), sixup (wide symbol table), exp (show macro expansion). |
| MACLIB [path] | Path to directory containing macro files. |
| MSB [ON, OFF] | Set or clear MSB in strings. |
| OBJ expr | Address of object code. |
| ORG | Address of code origin. |
| PAGE | Send a form feed to current output. |
| REF | External reference. |
| REL | Generate relocation data in output. |
| REP expr | Generate character CHR, expr times. |
| SBTL dstring | Set subtitle. |
| SBUFSIZ expr | Set source buffer size in pages, default 4. |
| SKP expr | Skip expr blank lines. |
| STR dstring | Length byte followed by string. |
| SYS | Set filetype of next object file to SYS. |
| TIME | The time in hh:mm format (six bytes). |
| X6502 | Allow 65c02 opcodes. |
| ZDEF id | Zero page identifier. |
| ZREF | Zero page external reference. |
| ZXTRN | Zero page external reference. |
| Table E-1: Relocatable (REL) File Format | |
|---|---|
| Byte | Contents of byte |
| 0 | Length of code image, lo byte |
| 1 | Length of code image, hi byte |
| 2 to len+1 | Binary code image with length in bytes 0 and 1 above |
| len+2 to EOF | Relocation dictionary, consisting of N 4-byte entries with the structure shown in Table E-2 |
| Table E-2: Relocation Dictionary (RLD) Format | |
|---|---|
| Byte | Contents of byte |
| 1 | RLD-flag byte containing 4 flag bits: $80 bit: Size of relocatable field; set -> 2 bytes, clr -> 1 byte $40 bit: Upper/Lower 8 of a 16 bit value; set -> hi 8, clr -> lo 8 $20 bit: Normal/Reversed 2 byte field; set -> hi/lo, clr -> lo/hi (DDB is set) $01 bit: "Not end of RLD" flag; set -> more entries, clr -> end of RLD |
| 2 | Field offset in image, lo byte. |
| 3 | Field offset in image, hi byte. |
| 4 | Lo 8 bits of 16 bit value for an 8 bit field containing upper 8 bits (e.g. #>ADR); $40 bit clear -> 0; $10 bit set -> EXTRN symbol number. |
| N*4+1 | Binary zero marks end of RLD. |
| N*4+2 | Beginning of optional external symbol directory (ESD); generated for each EXTRN or ENTRY directive. |
| 1 to s1 | The EXTRN/ENTRY symbolic name of length s1 bytes; all bytes have the hi bit set, except the last. |
| s1+1 | Symbol type flags: $10 bit: Set -> EXTRN symbol type. $08 bit: Set -> ENTRY symbol type. |
| s1+2 | EXTRN type: symbol number referred to by an RLD entry; ENTRY type: lo byte of offset for ENTRY type symbol. |
| s1+3 | Hi byte of offset for ENTRY type symbol. |
| end | Binary zero marks end of ESD entries, of which there may be none. |
| Symbol Table Format | |
|---|---|
| $1E00: (~DCI name)(Flags)(Lo value)(Hi value) | |
| Flag bit | Meaning |
| %10000000 | Undefined Symbol. |
| %01000000 | Unreferenced Symbol. |
| %00100000 | Relative Symbol |
| %00010000 | External Symbol |
| %00001000 | Entry Symbol. |
| %00000100 | Macro. |
| %00000010 | No-such-label Error. |
| %00000001 | Forward-referenced. |