;;; 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