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