; uncomment to enable user-friendly "MEMLO is too high" error. ; adds ~80 bytes to the code size (40% larger). ;verbose_memlo_check = 1 .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_pages = zp_addr ; 1 byte table_ptr = zp_addr+1 ; 2 bytes dest_ptr = table_ptr ; alias code_ptr = zp_addr+3 ; 2 bytes .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 memlo_00 inc MEMLO+1 lda #0 sta MEMLO memlo_00: lda code_start+1 sec sbc MEMLO+1 sta offset_pages bcs memlo_ok .ifdef verbose_memlo_check ; whoops, MEMLO is too high, print message and wait for keystroke whoops: ldx #0 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 .else lda #$40 ; turn the screen red... sta COLOR2 freeze: bne freeze ; wait for the user to press Reset. .endif memlo_ok: ; adjust addresses before moving the code. ; point to the relocation table... lda #table sta table_ptr+1 fixup_addrs: ; walk the reloc table ldy #1 lda (table_ptr),y sta code_ptr+1 dey lda (table_ptr),y sta code_ptr ; point to next table entry inc table_ptr bne tp1ok inc table_ptr+1 tp1ok: inc table_ptr bne tp2ok inc table_ptr+1 tp2ok: ora code_ptr+1 ; A still has code_ptr, quit if we hit $0000 in the table beq fixup_done lda (code_ptr),y ; Y still 0 sec sbc offset_pages sta (code_ptr),y sec ; *should* already be set... bcs fixup_addrs fixup_done: ; 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 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 ; 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+1 sec sbc offset_pages sta RUNAD+1 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 .ifdef verbose_memlo_check whoops_msg: .byte "MEMLO is too high! Press any key to exit.", EOL whoops_len = (*-whoops_msg) .endif 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