aboutsummaryrefslogtreecommitdiff
path: root/reloc.s
diff options
context:
space:
mode:
Diffstat (limited to 'reloc.s')
-rw-r--r--reloc.s193
1 files changed, 193 insertions, 0 deletions
diff --git a/reloc.s b/reloc.s
new file mode 100644
index 0000000..c4c3c58
--- /dev/null
+++ b/reloc.s
@@ -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