;;; reloc.s - relocate code. this is just one puzzle piece. ;;; see README.txt for details. .export _main .include "atari.inc" start_addr = $71c0 ; mkrelocxex.c appends this stuff. code_start = end_addr code_end = end_addr+2 code_run = end_addr+4 code_init = end_addr+6 table = end_addr+8 ; we use 9 bytes of zero page, in the floating-point area. zp_addr = FR0 offset_pages = zp_addr ; 1 byte table_ptr = zp_addr+1 ; 2 bytes dest_ptr = zp_addr+3 ; 2 bytes code_ptr = zp_addr+5 ; 2 bytes shfreg = zp_addr+7 ; 1 byte pagecount = zp_addr+8 ; 1 byte ; "manually" create the atari binary load header. .org start_addr - 6 .word $ffff .word start_addr .word end_addr - 1 _main: ; if MEMLO isn't on a page boundary, move it up to the next ; page, e.g. $1cfc => $1d00. lda MEMLO beq calc_offset inc MEMLO+1 lda #0 sta MEMLO calc_offset: lda code_start+1 sec sbc MEMLO+1 sta offset_pages bcs relocate_code whoops: ; whoops, MEMLO is too high! freeze: bcc freeze ; wait for the user to press Reset. relocate_code: ; adjust addresses while moving the code. ; point to the relocation table... lda #table sta table_ptr+1 ; ...and to the code we're moving... lda code_start sta code_ptr lda code_start+1 sta code_ptr+1 ; ...and to the destination we're moving to. lda MEMLO sta dest_ptr lda MEMLO+1 sta dest_ptr+1 ; pagecount = (code_end >> 8) - (code_start >> 8) + 2 lda code_end+1 sec sbc code_start+1 tax inx inx stx pagecount ldx #0 lda (table_ptr,x) sta shfreg ; this moves a page at a time, meaning if code_end isn't ; on an even page boundary, we move a little more than ; needed. it won't hurt anything, and it follows the ; KISS principle. ldy #0 move_loop: cpx #8 bne shiftit ; bump bitmap pointer inc table_ptr bne tpok inc table_ptr+1 tpok: ; get next byte of bitmap ldx #0 lda (table_ptr,x) sta shfreg shiftit: ; get next bit from bitmap byte inx lda (code_ptr),y asl shfreg bcc store_byte sbc offset_pages store_byte: ; store the (possibly adjusted) byte at the destination... sta (dest_ptr),y iny bne move_loop inc code_ptr+1 inc dest_ptr+1 dec pagecount bne move_loop ;;; end of relocate_code ; bump MEMLO to point one byte past the end of the moved code. lda code_end sec sbc code_start sta code_end lda code_end+1 sbc code_start+1 sta code_end+1 lda code_end sec ; add 1 extra (we want code len + 1) adc MEMLO sta MEMLO lda code_end+1 adc MEMLO+1 sta MEMLO+1 ; MEMLO now MEMLO + code length + 1 byte ; do we have a run address? lda code_run+1 beq do_init ; no: $00xx not valid. ; point RUNAD at adjusted run address. sec sbc offset_pages sta RUNAD+1 lda code_run sta RUNAD do_init: ; if there's an init address, call it (just like DOS would). lda code_init+1 beq done ; if hi byte is 0, assume lo byte is also 0. sec sbc offset_pages ; call at relocated location, of course. sta code_init+1 jmp (code_init) ; done done: rts end_addr: .out .sprintf("reloc.s code size $%04x (%d)", (* - start_addr), (* - start_addr)) ; this was for testing only. mkrelocxex.c adds the init address. ; .word INITAD ; .word INITAD+1 ; .word _main