diff options
Diffstat (limited to 'src/aexec.s')
-rw-r--r-- | src/aexec.s | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/src/aexec.s b/src/aexec.s new file mode 100644 index 0000000..27e8909 --- /dev/null +++ b/src/aexec.s @@ -0,0 +1,202 @@ + +; Load an Atari DOS executable, including support for init and run vectors +; Parameters: nul-terminated filename pointer in A/X (lo/hi) +; Return value: 0 for success, nonzero on error, never returns if exe has +; a run address. + +; This is for the Atari, but it gets built with "-t none" to +; avoid ld65 adding the C runtime startup code. Also, there is +; NO run address in this file. + +; "Link" to your program with "cat aexec.xex yourprog.xex > newprog.xex" + +; int __fastcall__ (*atari_exec_p)(char *) = (int __fastcall__ (*)(char *))0x600; +; #define atari_exec(x) ((*atari_exec_p)(x)) +; then call with: atari_exec("D:PROGRAM.XEX"); + +; If the file can't be opened for whatever reason, atari_exec() returns a +; nonzero result (the Atari error number). + +; If the file loads OK and has a run address, atari_exec() never returns +; (runs the loaded program, then exits to DOS if/when the program exits). +; This means there's no memory conflict between caller and callee. + +; If there was no run address, atari_exec returns 0 to the caller. +; The caller is responsible for making sure the loaded program doesn't +; step on the memory used by the caller or atari_exec itself! + +; If the file is openable but invalid (not a XEX, or truncated), current +; atari_exec() implementation hangs the machine with a red screen, since +; there's no way to know whether the partially-loaded program overwrote +; the caller... + + + .include "atari.inc" + +tmp = $d4 ; aka FR0 +loadaddr = $d6 + +main = $0600 + + .org main-6 + .word $FFFF + .word main + .word endmain-1 + + .org main + pha + txa + pha + jsr fclose + pla + tax + pla + + sta ICBAL+16 + stx ICBAH+16 + sta tmp + stx tmp+1 + ldy #0 + +; do an OPEN #1,4,0,$filename + +; get length of filename +fnameloop: + lda (tmp),y + beq fndone + iny + bne fnameloop + + ; set IOCB #1 buffer addr, buffer len, etc +fndone: + sty ICBLL+$10 + lda #0 + sta ICBLH+$10 + sta ICAX2+$10 + + ; init these to 0 so we can tell if they change + sta RUNAD + sta RUNAD+1 + sta INITAD + sta INITAD+1 + + ldx #4 + stx ICAX1+$10 + dex + stx ICCOM+$10 ; cmd #3 = OPEN + + ldx #$10 + jsr CIOV ; do the OPEN + bpl readheader + + tya ; CIO returns error code in Y reg + ldx #0 + rts + +fail: + lda #$48 + sta 710 +hang: bne hang + +readheader: +; read 2 bytes into local buffer + jsr read2bytes + bpl ok + cpy #136 ; EOF + bne fail + beq close_file ; if at EOF, close the file + +; if they're $ffff, try again +ok: + lda tmp + tax + and tmp+1 + cmp #$ff + beq readheader + +; store those 2 bytes in loadaddr + lda tmp+1 + sta loadaddr+1 + stx loadaddr + +; read 2 more bytes into local buffer (end addr) + jsr read2bytes + bmi fail +; subtract loadaddr from end addr, add 1, to get length +; store length into IOCB + lda loadaddr + sta ICBAL+$10 + lda loadaddr+1 + sta ICBAH+$10 + + sec + lda tmp + sbc loadaddr + sta ICBLL+$10 + lda tmp+1 + sbc loadaddr+1 + sta ICBLH+$10 + inc ICBLL+$10 + bne noinc + inc ICBLH+$10 +noinc: + jsr read_segment + bmi fail + +; if INITAD modified, JSR there + lda INITAD + ora INITAD+1 + beq readheader + + jsr do_init + + lda #0 + sta INITAD + sta INITAD+1 + beq readheader ; branch always + +do_init: + jmp (INITAD) + +close_file: + jsr fclose + +; JSR through RUNAD if it's not zero + lda RUNAD + ora RUNAD+1 + beq norun + + jsr do_run + jmp (DOSVEC) ; does not return + +; If there was no run address, we were probably loading an R: driver, so +; our caller is safe to return to +norun: + lda #0 ; return 0 + tax + rts + +read2bytes: + lda #tmp + sta ICBAL+$10 + lda #2 + sta ICBLL+$10 + lda #0 + sta ICBAH+$10 + sta ICBLH+$10 +read_segment: + lda #GETCHR + sta ICCOM+$10 + ldx #$10 + jmp CIOV + +do_run: + jmp (RUNAD) + +fclose: + lda #CLOSE + sta ICCOM+$10 + ldx #$10 + jmp CIOV + +endmain: |