******************************************************************************** * 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
No exported symbols found.