diff --git a/targetutil/apple2/loader.cfg b/targetutil/apple2/loader.cfg new file mode 100644 index 000000000..aa7a8c437 --- /dev/null +++ b/targetutil/apple2/loader.cfg @@ -0,0 +1,17 @@ +################################################################################# +# # +# LOADER.SYSTEM - an Apple][ ProDOS 8 loader for cc65 programs (Oliver Schmidt) # +# # +################################################################################# + +MEMORY { + MEMORY_2000: start = $2000, size = $0200, file = %O; + MEMORY_0300: start = $0300, size = $0100; +} + +SEGMENTS { + CODE_2000: load = MEMORY_2000, type = ro; + DATA_2000: load = MEMORY_2000, type = rw; + CODE_0300: load = MEMORY_2000, run = MEMORY_0300, type = ro, define = yes; + DATA_0300: load = MEMORY_2000, run = MEMORY_0300, type = rw, define = yes; +} diff --git a/targetutil/apple2/loader.s b/targetutil/apple2/loader.s new file mode 100644 index 000000000..ab4ae44e3 --- /dev/null +++ b/targetutil/apple2/loader.s @@ -0,0 +1,318 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Apple][ ProDOS 8 system program for loading binary programs (Oliver Schmidt) ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +A1L := $3C +A1H := $3D +HIMEM := $73 +STACK := $0100 +BUF := $0200 +PATHNAME := $0280 +DOSWARM := $03D0 +DOSCOLD := $03D3 +SOFTEV := $03F2 +PWREDUP := $03F4 +MLI := $BF00 +MEMTABL := $BF58 +RESET := $FA62 +VERSION := $FBB3 +RDKEY := $FD0C +PRBYTE := $FDDA +COUT := $FDED + +QUIT_CALL = $65 +GET_FILE_INFO_CALL = $C4 +OPEN_CALL = $C8 +READ_CALL = $CA +CLOSE_CALL = $CC +FILE_NOT_FOUND_ERR = $46 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + .import __CODE_0300_SIZE__, __DATA_0300_SIZE__ + .import __CODE_0300_LOAD__, __CODE_0300_RUN__ + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +.segment "DATA_2000" + +GET_FILE_INFO_PARAM: + .byte $0A ;PARAM_COUNT + .addr PATHNAME ;PATHNAME + .byte $00 ;ACCESS + .byte $00 ;FILE_TYPE +FILE_INFO_ADDR: .word $0000 ;AUX_TYPE + .byte $00 ;STORAGE_TYPE + .word $0000 ;BLOCKS_USED + .word $0000 ;MOD_DATE + .word $0000 ;MOD_TIME + .word $0000 ;CREATE_DATE + .word $0000 ;CREATE_TIME + +OPEN_PARAM: + .byte $03 ;PARAM_COUNT + .addr PATHNAME ;PATHNAME + .addr MLI - 1024 ;IO_BUFFER +OPEN_REF: .byte $00 ;REF_NUM + +LOADING: + .byte $0D + .asciiz "Loading " + +ELLIPSES: + .byte " ...", $0D, $0D, $00 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +.segment "DATA_0300" + +READ_PARAM: + .byte $04 ;PARAM_COUNT +READ_REF: .byte $00 ;REF_NUM +READ_ADDR: .addr $0000 ;DATA_BUFFER + .word $FFFF ;REQUEST_COUNT + .word $0000 ;TRANS_COUNT + +CLOSE_PARAM: + .byte $01 ;PARAM_COUNT +CLOSE_REF: .byte $00 ;REF_NUM + + +.ifndef REBOOT + +QUIT_PARAM: + .byte $04 ;PARAM_COUNT + .byte $00 ;QUIT_TYPE + .word $0000 ;RESERVED + .byte $00 ;RESERVED + .word $0000 ;RESERVED + +.endif + +FILE_NOT_FOUND: + .asciiz "... File Not Found" + +ERROR_NUMBER: + .asciiz "... Error $" + +PRESS_ANY_KEY: + .asciiz " - Press Any Key " + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +.segment "CODE_2000" + + jmp :+ + .byte $EE + .byte $EE + .byte 65 +STARTUP:.res 65 + + ; Reset stack +: ldx #$FF + txs + + ; Relocate CODE_0300 and DATA_0300 + ldx #<(__CODE_0300_SIZE__ + __DATA_0300_SIZE__) +: lda __CODE_0300_LOAD__ - 1,x + sta __CODE_0300_RUN__ - 1,x + dex + bne :- + +.ifndef REBOOT + + ; Jump to dispatcher on program exit + ldy #$4C ; jmp + lda #EXIT + sty DOSWARM + sta DOSWARM + 1 + stx DOSWARM + 2 + sty DOSCOLD + sta DOSCOLD + 1 + stx DOSCOLD + 2 + + ; Jump to dispatcher on RESET + sta SOFTEV + stx SOFTEV + 1 + txa + eor #$A5 + sta PWREDUP + +.else + + ; Jump to RESET on program exit + ldy #$4C ; jmp + lda #RESET + sty DOSWARM + sta DOSWARM + 1 + stx DOSWARM + 2 + sty DOSCOLD + sta DOSCOLD + 1 + stx DOSCOLD + 2 + + ; Reboot on RESET + inc PWREDUP + +.endif + + ; That's what it's all about ! + lda #MLI + sta HIMEM + stx HIMEM + 1 + + ; Overwrite the whole system bit map + ldx #($C0 / 8) - 1 + + ; Set protection for pages $B8 - $BF + lda #%00000001 + sta MEMTABL,x + dex + + ; Set protection for pages $08 - $B7 + lda #%00000000 +: sta MEMTABL,x + dex + bne :- + + ; Set protection for pages $00 - $07 + lda #%11011111 ; include page $03 + sta MEMTABL,x + + ; Remove ".SYSTEM" from pathname + lda PATHNAME + sec + sbc #.strlen(".SYSTEM") + sta PATHNAME + + ; Add trailing '\0' to pathname + tax + lda #$00 + sta PATHNAME + 1,x + + ; Copy ProDOS startup filename and trailing '\0' to stack + ldx STARTUP + lda #$00 + beq :++ ; bra +: lda STARTUP + 1,x +: sta STACK,x + dex + bpl :-- + + ; Provide some user feedback + lda #LOADING + jsr PRINT + lda #<(PATHNAME + 1) + ldx #>(PATHNAME + 1) + jsr PRINT + lda #ELLIPSES + jsr PRINT + + jsr MLI + .byte GET_FILE_INFO_CALL + .word GET_FILE_INFO_PARAM + bcc :+ + jmp ERROR + +: jsr MLI + .byte OPEN_CALL + .word OPEN_PARAM + bcc :+ + jmp ERROR + + ; Copy file reference number +: lda OPEN_REF + sta READ_REF + sta CLOSE_REF + + ; Get load address from aux-type + lda FILE_INFO_ADDR + ldx FILE_INFO_ADDR + 1 + sta READ_ADDR + stx READ_ADDR + 1 + + ; It's high time to leave this place + jmp __CODE_0300_RUN__ + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +.segment "CODE_0300" + + jsr MLI + .byte READ_CALL + .word READ_PARAM + bcs ERROR + + jsr MLI + .byte CLOSE_CALL + .word CLOSE_PARAM + bcs ERROR + + ; Copy REM token and startup filename to BASIC input buffer + ldx #$00 + lda #$B2 + bne :++ ; bra +: inx + lda a:STACK - 1,x +: sta BUF,x + bne :-- + + ; Go for it ... + jmp (READ_ADDR) + +PRINT: + sta A1L + stx A1H + ldx VERSION + ldy #$00 +: lda (A1L),y + beq :++ + cpx #$06 ; //e ? + beq :+ + cmp #$60 ; lowercase ? + bcc :+ + and #$5F ; -> uppercase +: ora #$80 + jsr COUT + iny + bne :-- ; bra +: rts + +ERROR: + cmp #FILE_NOT_FOUND_ERR + bne :+ + lda #FILE_NOT_FOUND + jsr PRINT + beq :++ ; bra +: pha + lda #ERROR_NUMBER + jsr PRINT + pla + jsr PRBYTE +: lda #PRESS_ANY_KEY + jsr PRINT + jsr RDKEY + +.ifndef REBOOT + +EXIT: + ; Reset stack + ldx #$FF + txs + + jsr MLI + .byte QUIT_CALL + .word QUIT_PARAM + +.endif + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; \ No newline at end of file diff --git a/targetutil/apple2/loader.txt b/targetutil/apple2/loader.txt new file mode 100644 index 000000000..62c75a46e --- /dev/null +++ b/targetutil/apple2/loader.txt @@ -0,0 +1,85 @@ +Apple][ ProDOS 8 system program for loading binary programs (Oliver Schmidt) +============================================================================ + + +Background +---------- + +The ordinary way to run a binary program using ProDOS 8 is to load BASIC.SYSTEM +first and enter then from the BASIC prompt 'BRUN MYPROGRAM' or '- MYPROGRAM'. + +Using LOADER.SYSTEM instead to run a binary program has four advantages: + +1. The binary program can be selected directly from the ProDOS 8 dispatcher. + +2. The size of BASIC.SYSTEM is 21 blocks while the size of LOADER.SYSTEM is + only 1 block. The benefits are: + + - Running a binary program with LOADER.SYSTEM is faster. + + - If the only use of BASIC.SYSTEM is to run binary programs it can be removed + altogether thus freeing up precious floppy disk space. + +3. BASIC.SYSTEM can load binary programs into the range $0800-$9600 (35,5 kB) + while LOADER.SYSTEM can load much larger binary programs into the range + $0800-$BB00 (44,75 kB). + +4. If a binary program needs to reclaim the memory used by BASIC.SYSTEM it has + to update the system bit map after being loaded and on exit it must call the + ProDOS 8 dispatcher itself. But when run by LOADER.SYSTEM the binary program + automatically has access to the range $0800-$BF00 (45,75 kB) just by checking + HIMEM. On exit the binary program simply jumps to DOSWARM or DOSCOLD as usual + which are set up by LOADER.SYSTEM to call the ProDOS 8 dispatcher (or reboot + the computer - see below). + + +Usage +----- + +Obviously LOADER.SYSTEM has to be told which binary program to run. As the +ProDOS 8 dispatcher has no notion of system program parameters the ordinary +approach would have been to make LOADER.SYSTEM bring up yet another menu to +select the binary program to run. + +But to allow to select the binary program directly from the ProDOS 8 dispatcher +anyway LOADER.SYSTEM detects the path to the binary program from its own path +by just removing the '.SYSTEM' from its name. So if you want to run the binary +program MYPROGRAM you'll need a copy of LOADER.SYSTEM in the same directory +being renamed to MYPROGRAM.SYSTEM. + +This means you'll end up with a copy of LOADER.SYSTEM for every binary program +you intend to run it. But as LOADER.SYSTEM is a seedling file using up only a +single block in the ProDOS 8 file system this should be no issue. + + +Build +----- + +In case you want to build 'loader.system' from the source code yourself you can +do so using the following commands: + +ca65 loader.s +ld65 -C loader.cfg -o loader.system loader.o + +If you want LOADER.SYSTEM to reboot the computer on exit of the binary program +instead of calling the ProDOS 8 dispatcher then use the following commands: + +ca65 -D REBOOT loader.s +ld65 -C loader.cfg -o loader.system loader.o + + +Installation +------------ + +The file 'loader.system' as generated by the cc65 linker does NOT have a 4-byte +address/length header as it is generated for ordinary Apple][ binaries. This is +because the start address for ProDOS 8 system programs is fixed to $2000. + +The recommended way to transfer 'loader.system' from your native file system to +a ProDOS 8 file system disk image is to use AppleCommander which is available at +http://applecommander.sourceforge.net/ + +If you want to put the file 'loader.system' onto a disk image 'mydisk.dsk' as +system program MYPROGRAM.SYSTEM you can do so using the following command: + +java -jar ac.jar -p mydisk.dsk MYPROGRAM.SYSTEM sys < loader.system