(back to project page)

RBOOT Disassembly

                   ********************************************************************************
                   * Disassembly of RBOOT, the DOS Toolkit relocatable loader boot shim, as       *
                   * described in "Applesoft Tool Kit" on ch.4 pg.29. This is used to load        *
                   * Apple's relocatable Hi-Res Character Generator (HRCG).                       *
                   *                                                                              *
                   * This BLOADS at $208 and, after a CALL 520, itself loads RLOAD into free      *
                   * space around 1-2 pages above BASIC variables, then points the USR vector     *
                   * there. There is enough padding to let you allocate some simple variables     *
                   * without clobbering RLOAD before you can use it. You can then load            *
                   * relocatable modules (file type R) by calling RLOAD via USR(0).               *
                   *                                                                              *
                   * RBOOT's relocation method is simple but clever. It disassembles RLOAD with   *
                   * monitor routines to find 16-bit operands, which must be addresses on a 6502. *
                   * If the address is within the module (per the file header address and length) *
                   * it is relocated. If not--e.g. an Applesoft call--it is left alone. This      *
                   * can't relocate 8-bit low or high address immediates, and only works on code  *
                   * (not data) but RLOAD is written with this in mind. RLOAD even uses the BIT   *
                   * operand once to disguise a data address as code.                             *
                   *                                                                              *
                   * Not documented: you can load a different binary file (instead of RLOAD) by   *
                   * quoting it after the call: CALL 520"RLOAD2". That could be left over from    *
                   * the development process.                                                     *
                   *                                                                              *
                   * Disasm by Jim Ursetto https://github.com/ursetto with 6502bench SourceGen.   *
                   ********************************************************************************
                   FM_OPEN         .eq     $01    {const}
                   FM_CLOSE        .eq     $02    {const}
                   FM_READ         .eq     $03    {const}
                   BAS_HANDLERR    .eq     $f2e9  {const}    ;routine to handle errors if onerr goto active
                   BAS_USRVEC      .eq     $0a    {addr/3}   ;USR() command vector
                   MON_LENGTH      .eq     $2f               ;output of MON_INSDS2
                   MON_A3L         .eq     $40               ;general purpose
                   MON_A3H         .eq     $41               ;general purpose
                   BAS_STREND      .eq     $6d    {addr/2}   ;pointer to end of numeric storage (2b)
                   BAS_MEMSIZE     .eq     $73    {addr/2}   ;HIMEM (2b)
                   BAS_CHRGET      .eq     $b1               ;get next character or Applesoft token
                   BAS_CHRGOT      .eq     $b7               ;get next, but don't advance TXTPTR
                   BAS_ERRNUM      .eq     $de               ;error number for onerr handler
                   BAS_REMSTK      .eq     $f8               ;stack pointer saved before each statement
                   DOS_FM          .eq     $03d6             ;DOS file manager entry point
                   DOS_LOCFPL      .eq     $03dc             ;loads Y/A with address of FM param list
                   DOS_LOCRPL      .eq     $03e3             ;loads Y/A with address of RWTS IOB
                   MON_INSDS2      .eq     $f88e

                                   .org    $0208
0208: 4c 32 02     rboot_entry     jmp     start

020b: e0           rload_bound     .dd1    $e0               ;end of RLOAD, rounded up to next page
020c: 00 00        fileaddr        .dd2    $0000             ;ORG of RLOAD (as found in file address)
020e: 00 00        filelen         .dd2    $0000             ;length of RLOAD
0210: 00 00        usrvec          .dd2    $0000             ;intermediate copy of USR vector
0212: 00 00        rload_end       .dd2    $0000             ;end of relocated RLOAD in memory
0214: d2 cc cf c1+ filename        .str    ↑“RLOAD                         ”

                   ; param_list ($06/$07) is also used to pass rload_end to RLOAD.
                   param_list      .var    $06    {addr/2}   ;DOS FM / RWTS-IOB param list

0232: 20 e3 03     start           jsr     DOS_LOCRPL
                   ; First, obtain the current slot and drive number from DOS RWTS.
0235: 84 06                        sty     param_list        ;address of RWTS IOB
0237: 85 07                        sta     param_list+1
0239: a0 01                        ldy     #$01
023b: b1 06                        lda     (param_list),y    ;slot*16
023d: 4a                           lsr     A
023e: 4a                           lsr     A
023f: 4a                           lsr     A
0240: 4a                           lsr     A
0241: 48                           pha                       ;push slot number
0242: c8                           iny
0243: b1 06                        lda     (param_list),y
0245: 48                           pha                       ;push drive number
                   ; Build a file manager call to open RLOAD.
0246: 20 dc 03                     jsr     DOS_LOCFPL
0249: 84 06                        sty     param_list
024b: 85 07                        sta     param_list+1
024d: a0 00                        ldy     #$00
024f: a9 01                        lda     #FM_OPEN          ;Call type: OPEN
0251: 91 06                        sta     (param_list),y
0253: c8                           iny
0254: a9 00                        lda     #$00
0256: 91 06                        sta     (param_list),y    ;Call subtype: N/A
0258: c8                           iny
0259: a9 01                        lda     #$01              ;Record length: 1
025b: 91 06                        sta     (param_list),y
025d: c8                           iny
025e: a9 00                        lda     #$00              ;(high byte of record length)
0260: 91 06                        sta     (param_list),y
0262: c8                           iny
0263: 91 06                        sta     (param_list),y    ;Volume: any
0265: c8                           iny
0266: 68                           pla                       ;Drive number (from RWTS)
0267: 91 06                        sta     (param_list),y
0269: c8                           iny
026a: 68                           pla                       ;Slot number (from RWTS)
026b: 91 06                        sta     (param_list),y
026d: c8                           iny
026e: a9 04                        lda     #$04              ;File type: Binary
0270: 91 06                        sta     (param_list),y
0272: c8                           iny
0273: a9 14                        lda     #<filename        ;Filename to open
0275: 91 06                        sta     (param_list),y
0277: a9 02                        lda     #>filename
0279: c8                           iny
027a: 91 06                        sta     (param_list),y
027c: 20 b7 00                     jsr     BAS_CHRGOT
027f: f0 2f                        beq     no_more_args
                   ; If a quoted argument is next on the BASIC CALL line, set the filename to load
                   ; to that (instead of RLOAD). For example: CALL 520"RLOAD2" (end quote can be
                   ; omitted if end of line). This was probably for debugging the relocator.
0281: c9 22                        cmp     #‘"’              ;begin quote
0283: d0 26                        bne     discard_eol
0285: 20 b1 00                     jsr     BAS_CHRGET
0288: f0 26                        beq     no_more_args
028a: aa                           tax
028b: a0 1d                        ldy     #$1d              ;length of filename buffer (-1)
028d: a9 a0                        lda     #$a0
028f: 99 14 02     @loop           sta     filename,y        ;clear filename buffer to spaces
0292: 88                           dey
0293: 10 fa                        bpl     @loop
0295: 8a                           txa
0296: c8           copy_filename   iny
0297: c0 1e                        cpy     #$1e              ;avoid filename buffer overrun
0299: b0 10                        bcs     discard_eol
029b: 09 80                        ora     #$80
029d: 99 14 02                     sta     filename,y
02a0: 20 b1 00                     jsr     BAS_CHRGET
02a3: f0 0b                        beq     no_more_args      ;end of line or
02a5: c9 22                        cmp     #‘"’              ;end quote terminates filename
02a7: f0 02                        beq     discard_eol
02a9: d0 eb                        bne     copy_filename

02ab: 20 b1 00     discard_eol     jsr     BAS_CHRGET        ;discard rest of basic line
02ae: d0 fb                        bne     discard_eol
02b0: a2 01        no_more_args    ldx     #$01              ;do not create file if not found
02b2: 20 67 03                     jsr     call_dos_fm
02b5: 90 05                        bcc     check_binary      ;carry set if DOS error
02b7: a2 06                        ldx     #$06
02b9: 4c 53 03                     jmp     onerr

02bc: a0 07        check_binary    ldy     #$07              ;file type of opened file
02be: b1 06                        lda     (param_list),y
02c0: 29 7f                        and     #$7f
02c2: c9 04                        cmp     #$04              ;is file type B?
02c4: f0 05                        beq     read_file         ;yes, read it
02c6: a2 0a                        ldx     #$0a
02c8: 4c 53 03                     jmp     onerr

                   ********************************************************************************
                   * Read RLOAD into just after end of BASIC variables, ignoring its load address *
                   * of $800. It will be page-aligned and with $101-$200 bytes of padding,        *
                   * presumably so a few simple variable allocations afterward will not clobber   *
                   * it. Later, BASIC's USR vector is pointed to this code at offset 0.           *
                   ********************************************************************************
02cb: a0 00        read_file       ldy     #$00
02cd: a9 03                        lda     #FM_READ
02cf: 91 06                        sta     (param_list),y    ;READ file
02d1: c8                           iny
02d2: a9 02                        lda     #$02              ;read/write range of bytes
02d4: 91 06                        sta     (param_list),y
02d6: a0 06                        ldy     #$06
02d8: a9 04                        lda     #$04              ;read 0004 bytes (file addr and len)
02da: 91 06                        sta     (param_list),y
02dc: c8                           iny
02dd: a9 00                        lda     #$00
02df: 91 06                        sta     (param_list),y
02e1: c8                           iny
02e2: a9 0c                        lda     #<fileaddr        ;read into our fileaddr/filelen
02e4: 91 06                        sta     (param_list),y
02e6: c8                           iny
02e7: a9 02                        lda     #>fileaddr
02e9: 91 06                        sta     (param_list),y
02eb: c8                           iny
02ec: a9 04                        lda     #$04              ;unknown. cannot find docs on FM offset 0A/0B
02ee: 91 06                        sta     (param_list),y
02f0: c8                           iny
02f1: a9 00                        lda     #$00
02f3: 91 06                        sta     (param_list),y
02f5: 20 67 03                     jsr     call_dos_fm       ;execute READ
02f8: ad 0e 02                     lda     filelen
02fb: 8d 12 02                     sta     rload_end         ;anticipating our load address low byte is $00
02fe: a0 06                        ldy     #$06
0300: 91 06                        sta     (param_list),y    ;place file length into FM READ block
0302: c8                           iny
0303: ad 0f 02                     lda     filelen+1
0306: 48                           pha                       ;save high byte of filelen
0307: 91 06                        sta     (param_list),y
0309: a9 00                        lda     #$00              ;#<usrvec (page aligned)
030b: c8                           iny
030c: 91 06                        sta     (param_list),y    ;load address low byte
                   ; Round STREND down to the nearest page and add $200, setting usrvec (our load
                   ; address and USR vector) to $101 - $200 bytes past the start of Applesoft free
                   ; space. (The layout is PROGRAM, VARS, ARRAYS, FREE, STRINGS, with strings
                   ; growing down from HIMEM, so STREND is the end of array variable data and the
                   ; start of free space.) usrvec temporarily holds the USR vector until we confirm
                   ; the program will fit.
030e: 8d 10 02                     sta     usrvec
0311: a6 6e                        ldx     BAS_STREND+1      ;start of free space
0313: e8                           inx                       ;add $200 to BAS_STREND
0314: e8                           inx
0315: 8e 11 02                     stx     usrvec+1          ;STREND+$101 <= usrvec <= STREND+$200
0318: 18                           clc
0319: 68                           pla                       ;restore high byte of filelen
031a: 6d 11 02                     adc     usrvec+1
031d: 8d 13 02                     sta     rload_end+1       ;rload_end = usrvec + filelen
0320: 8a                           txa                       ;#>usrvec
0321: c8                           iny
0322: 91 06                        sta     (param_list),y    ;load address high byte
0324: ad 12 02                     lda     rload_end
0327: c5 73                        cmp     BAS_MEMSIZE       ;Ensure rload_end lies under HIMEM
0329: ad 13 02                     lda     rload_end+1
032c: e5 74                        sbc     BAS_MEMSIZE+1
032e: 90 05                        bcc     setup_usr
0330: a2 0e                        ldx     #$0e
0332: 4c 53 03                     jmp     onerr             ;program does not fit, bail

                   ; The program fits in memory, so load it, relocate it, and update BASIC's USR
                   ; vector with our temporary usrvec. File operations here are not checked for
                   ; error.
0335: 20 67 03     setup_usr       jsr     call_dos_fm
0338: 20 5e 03                     jsr     close_file
033b: 20 6d 03                     jsr     relocate
033e: ad 10 02                     lda     usrvec
0341: 85 0b                        sta     BAS_USRVEC+1
0343: ad 11 02                     lda     usrvec+1
0346: 85 0c                        sta     BAS_USRVEC+2
                   ; Stash rload_end in a safe place in zero page. This will be used shortly by
                   ; RLOAD to verify it won't clobber itself when loading the relocatable module.
0348: ad 12 02                     lda     rload_end
034b: 85 06                        sta     param_list
034d: ad 13 02                     lda     rload_end+1
0350: 85 07                        sta     param_list+1
0352: 60                           rts                       ;Return to BASIC

                   ********************************************************************************
                   * Close file and jump to ONERR handler, which is expected to be active. X is   *
                   * error number (here $06, $0A or $0E). Note: Sets the BASIC errno and restores *
                   * stack, normally done at the beginning of HANDLERR.                           *
                   ********************************************************************************
0353: 86 de        onerr           stx     BAS_ERRNUM        ;set errno
0355: 20 5e 03                     jsr     close_file
0358: a6 f8                        ldx     BAS_REMSTK        ;Restore BASIC stack pointer
035a: 9a                           txs
035b: 4c ef f2                     jmp     BAS_HANDLERR+6    ;handle ONERR (bypasses stack/errno set)

035e: a0 00        close_file      ldy     #$00
0360: a9 02                        lda     #FM_CLOSE
0362: 91 06                        sta     (param_list),y
0364: 4c 67 03                     jmp     call_dos_fm       ;falls through

0367: 20 dc 03     call_dos_fm     jsr     DOS_LOCFPL
036a: 4c d6 03                     jmp     DOS_FM

                   ; Relocate RLOAD. Scan the RLOAD area with monitor disassembly routines to find
                   ; 16-bit operands (addresses), and if they are internal to RLOAD, relocate them.
                   ; We can identify internal addresses because they are between the ORG of RLOAD
                   ; (fileaddr) and its end (+filelen). Scan ends when a BRK is encountered. Note:
                   ; the org and usrvec are always page-aligned, and the end is rounded up to the
                   ; next page.
036d: ad 10 02     relocate        lda     usrvec            ;usrvec low byte is implicitly 0
0370: 85 40                        sta     MON_A3L           ;store usrvec in 16-bit temporary
0372: ad 11 02                     lda     usrvec+1          ;... for scan start address
0375: 85 41                        sta     MON_A3H
0377: ad 0f 02                     lda     filelen+1         ;high byte of length
037a: 38                           sec                       ;add 1 page for BCS below
037b: 6d 0d 02                     adc     fileaddr+1        ;page of usrvec+fileaddr+filelen+1
037e: 8d 0b 02                     sta     rload_bound       ;next page after end of RLOAD
0381: a0 00        @loop           ldy     #$00
0383: b1 40                        lda     (MON_A3L),y       ;get opcode
0385: f0 30                        beq     @done             ;exit on first zero opcode (BRK)
0387: 20 8e f8                     jsr     MON_INSDS2        ;compute operand length
038a: a4 2f                        ldy     MON_LENGTH        ;output of INSDS2
038c: c0 02                        cpy     #$02              ;look for a two-byte operand (address)
038e: d0 18                        bne     next_opcode
0390: b1 40                        lda     (MON_A3L),y       ;get operand high byte
0392: cd 0b 02                     cmp     rload_bound       ;are we > the last RLOAD page (>= RLOAD+1)
0395: b0 11                        bcs     next_opcode       ;yes, skip relocation
0397: cd 0d 02                     cmp     fileaddr+1        ;are we < the lowest RLOAD page?
039a: 90 0c                        bcc     next_opcode       ;yes, skip relocation
                   ; To each 16-bit address operand, add the usrvec page - fileaddr page to get the
                   ; relocated page. The lower byte is ignored, as both the file and usrvec are
                   ; page-aligned.
039c: ad 11 02                     lda     usrvec+1
039f: 18                           clc
03a0: 71 40                        adc     (MON_A3L),y       ;*operand += usrvec page
03a2: 38                           sec
03a3: ed 0d 02                     sbc     fileaddr+1        ;*operand -= fileaddr page
03a6: 91 40                        sta     (MON_A3L),y
03a8: 98           next_opcode     tya
03a9: 38                           sec                       ;add opcode len (1)
03aa: 65 40                        adc     MON_A3L           ;and operand len (A) to code ptr
03ac: 85 40                        sta     MON_A3L
03ae: a5 41                        lda     MON_A3H
03b0: 69 00                        adc     #$00
03b2: 85 41                        sta     MON_A3H
03b4: b8                           clv
03b5: 50 ca                        bvc     @loop             ;always

03b7: 60           @done           rts

Symbol Table

No exported symbols found.