.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+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+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