diff options
Diffstat (limited to 'reloc.s')
-rw-r--r-- | reloc.s | 193 |
1 files changed, 193 insertions, 0 deletions
@@ -0,0 +1,193 @@ + + .export _main + .include "atari.inc" + + start_addr = $6c00 + + ; 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 + + zp_addr = FR0 + offset_lo = zp_addr + offset_hi = zp_addr+1 + table_ptr = zp_addr+2 ; 2 bytes + dest_ptr = table_ptr + code_ptr = zp_addr+4 ; 2 bytes + fixup = zp_addr+6 + + .org start_addr - 6 + .word $ffff + .word start_addr + .word end_addr - 1 + +_main: + lda code_start + sec + sbc MEMLO + + sta offset_lo + lda code_start+1 + sbc MEMLO+1 + sta offset_hi + + bcs memlo_ok + + ; whoops, MEMLO is too high +whoops: + ldx #0 + lda #<whoops_msg + sta ICBAL + lda #>whoops_msg + sta ICBAL+1 + lda #whoops_len + sta ICBLL + stx ICBLH + lda #PUTCHR + sta ICCOM + jsr CIOV +exitwait: + lda CH + cmp #$ff + beq exitwait + lda #$ff + sta CH + lda #0 + sta COLOR2 + rts + +memlo_ok: + ; 1st fixup pass, hi bytes: table comes right after our code + sta fixup + lda #<table + sta table_ptr + lda #>table + sta table_ptr+1 + jsr fixup_addrs + + ; 2nd fixup pass, lo bytes: table_ptr already points to table + lda offset_lo + sta fixup + jsr fixup_addrs + + ; absolute addresses are fixed up, now move the code. + lda code_start + sta code_ptr + lda code_start+1 + sta code_ptr+1 + lda MEMLO + sta dest_ptr + lda MEMLO+1 + sta dest_ptr+1 + + ; x = (code_end >> 8) - (code_start >> 8) + 2 + lda code_end+1 + sec + sbc code_start+1 + tax + inx + inx + ldy #0 + + ; 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. +move_loop: + lda (code_ptr),y + sta (dest_ptr),y + iny + bne move_loop + inc code_ptr+1 + inc dest_ptr+1 + dex + bne move_loop + + + ; 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 + inc code_end + bne ceok + inc code_end+1 ; code_end is now the code length + 1 byte +ceok: + lda code_end + clc + adc MEMLO + sta MEMLO + lda code_end+1 + adc MEMLO+1 + sta MEMLO+1 ; MEMLO now MEMLO + code length + 1 byte + + ; is RUNAD in our code space? If not, it points somewhere + ; within DOS, and shouldn't be altered. + lda RUNAD+1 + cmp code_start+1 + bcc do_init + + ; fix up RUNAD + lda RUNAD + sec + sbc offset_lo + lda RUNAD+1 + sbc offset_hi + +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. + + lda code_init ; subtract offset + sec + sbc offset_lo + sta code_init + lda code_init+1 + sbc offset_lo + sta code_init+1 + + jmp (code_init) + + ; done +done: + rts + +fixup_addrs: + ldy #1 + lda (table_ptr),y + sta code_ptr+1 + dey + lda (table_ptr),y + sta code_ptr + inc table_ptr ; point to next entry + bne tp1ok + inc table_ptr+1 +tp1ok: + inc table_ptr + bne tp2ok + inc table_ptr+1 +tp2ok: + ora code_ptr+1 ; quit if we hit $0000 in the table + beq done + lda (code_ptr),y ; Y still 0 + sec + sbc fixup + sta (code_ptr),y + jmp fixup_addrs + +whoops_msg: .byte "MEMLO is too high! Press any key to exit.", EOL + whoops_len = (*-whoops_msg) + +end_addr: + +; this was for testing only. mkrelocxex.c adds the init address. +; .word INITAD +; .word INITAD+1 +; .word _main |