;license:MIT ;(c) 2018-9 by 4am & qkumba ; ; ProRWTS2 glue functions ; ; Public functions ; - LoadFile ; - LoadFileAt ; - LoadDHRFile ; - SaveSmallFile ; - CloseHandles ; - SaveScreenHoles ; ; A general note about paths: ; ; LoadFile, LoadFileAt, LoadDHRFile, and SaveSmallFile support subdirectories. ; Directories are delimited by '/' like ProDOS. At program startup, we get the ; current directory and save it; that is the PROGRAM ROOT DIRECTORY. All ; pathnames are relative to the PROGRAM ROOT DIRECTORY. There is no concept of ; setting or changing the 'current' directory. ; ; The PROGRAM ROOT DIRECTORY is not guaranteed to be the root directory of the ; underlying ProDOS disk (although it can be). But it doesn't matter, because ; these functions provide no access to any directory above the PROGRAM ROOT ; DIRECTORY. You can't use '..' to access the parent directory, and you can't ; start a pathname with '/' to access the root directory of the underlying ; ProDOS disk. ; ; Examples: ; 'PREFS.CONF' points to a file named 'PREFS.CONF' in the PROGRAM ROOT ; DIRECTORY. ; ; 'FX/RIPPLE' points to a file named 'RIPPLE' in a directory named 'FX' in the ; PROGRAM ROOT DIRECTORY. gRootDirectory !word $FDFD ;------------------------------------------------------------------------------ ; LoadFile ; Load a file into memory all at once, using ProRWTS2, at the load address ; specified by the file's ProDOS metadata. Hey, do you have a text file that ; doesn't have a load address? This routine will happily load it at $0000, and ; your program will likely crash as a result. So give it a load address as if ; it were a binary file. ; ; supports paths, see note ; ; in: gPathname contains filename ; out: all flags clobbered ; all registers clobbered ; gPathname clobbered ;------------------------------------------------------------------------------ LoadFile +LDADDR gPathname +STAY namlo ; set filename +READ_RAM2_WRITE_RAM2 jsr traverse ; go to subdirectory, set up filename for read lda #cmdread ; read (instead of write) sta reqcmd lda #0 ; 0 = read into main memory sta auxreq sta sizelo sta sizehi ; 0 = query load address jsr hddopendir ; call ProRWTS2 lda ldrlo2 sta ldrlo lda ldrhi2 sta ldrhi dec sizehi ; read entire file (ProRWTS2 will figure out exact size) jsr hddopendir ; exit via ProRWTS2 (must re-open the file after query) +READ_RAM1_WRITE_RAM1 rts ;------------------------------------------------------------------------------ ; LoadFileAt ; Load a file into memory all at once, using ProRWTS2, at the load address ; specified by the passed parameter. ; ; supports paths, see note ; ; in: gPathname contains filename ; stack contains 2 bytes of parameters: ; +1 address to load file ; out: all flags clobbered ; all registers clobbered ; gPathname clobbered ; stack set to next instruction after parameters ;------------------------------------------------------------------------------ LoadFileAt +PARAMS_ON_STACK 2 +LDPARAM 1 +STAY ldrlo ; set load address +LDADDR gPathname +STAY namlo ; set filename +READ_RAM2_WRITE_RAM2 jsr traverse ; go to subdirectory, set up filename for read lda #cmdread ; read (instead of write) sta reqcmd lda #0 ; 0 = read into main memory sta auxreq lda #$FF ; read entire file (ProRWTS2 will figure out exact size) sta sizehi jsr hddopendir ; exit via ProRWTS2 +READ_RAM1_WRITE_RAM1 rts ;------------------------------------------------------------------------------ ; LoadDHRFile ; load .A2FC file (uncompressed double hi-res graphics) into memory ; all at once, using ProRWTS2 ; first $2000 bytes of file are loaded into auxiliary memory $2000..$3FFF ; second $2000 bytes of file are loaded into main memory $2000..$3FFF ; ; supports paths, see note ; ; in: gPathname contains filename ; out: all flags clobbered ; all registers clobbered ; stack set to next instruction after parameters ;------------------------------------------------------------------------------ LoadDHRFile +LDADDR gPathname +STAY namlo ; set filename +READ_RAM2_WRITE_RAM2 jsr traverse ; go to subdirectory, set up filename for read lda #$00 ; read first $2000 bytes sta sizelo sta ldrlo lda #$20 sta sizehi asl sta ldrhi lda #1 ; 1 = read into aux memory sta auxreq lda #cmdread ; read (instead of write) sta reqcmd jsr hddopendir ; call ProRWTS2 lda #$20 ; read next $2000 bytes sta sizehi asl sta ldrhi dec auxreq ; 0 = read into main memory clc ; not a subdirectory jsr hddrdwrpart ; call ProRWTS2 +READ_RAM1_WRITE_RAM1 rts ;------------------------------------------------------------------------------ ; SaveSmallFile ; Save a file into memory all at once, using ProRWTS2. ; /!\ Only first block (512 bytes) is written. Keep those files small. /!\ ; /!\ All 512 bytes are written to disk. Clear buffer before calling. /!\ ; ; supports paths, see note ; ; in: stack contains 2 bytes of parameters: ; +1 address of data buffer ; out: all flags clobbered ; all registers clobbered ; stack set to next instruction after parameters ;------------------------------------------------------------------------------ SaveSmallFile +PARAMS_ON_STACK 2 +LDPARAM 1 +STAY ldrlo ; set data buffer address for ProRWTS2 +LDADDR gPathname +STAY namlo ; set filename for ProRWTS2 +READ_RAM2_WRITE_RAM2 jsr traverse ; go to subdirectory, set up filename for read ;;if the write address is always a fixed value then we can discard the query lda #cmdread ; read (instead of write) sta reqcmd lda #0 ; 0 = read into main memory sta sizelo sta sizehi ; 0 = query load address jsr hddopendir ; call ProRWTS2 lda ldrlo2 sta ldrlo lda ldrhi2 sta ldrhi lda #cmdwrite ; write (instead of read) sta reqcmd sta sizelo ; non-zero jsr hddopendir ; exit via ProRWTS2 (must re-open the file after query) +READ_RAM1_WRITE_RAM1 rts ;------------------------------------------------------------------------------ ; traverse [private] ; ; in: (namlo) points to length-prefixed pathname+filename ; out: all flags clobbered ; all registers clobbered ;------------------------------------------------------------------------------ traverse +LDAY gRootDirectory sta (reloc + unrhddblocklo - unrelochdd) + 1 sty (reloc + unrhddblockhi - unrelochdd) + 1 sta @myreadblock+1 sty @myreadblock+3 ; reset 'root' directory (saved at program start) ;search for '/' character in filename ldx #0 ldy #0 lda (namlo), y tay - inx dey bmi @go ; no '/', just do the read lda (namlo), y cmp #'/' bne - sty sizelo txa pha @myreadblock @myx80_parms ldx #2 lda #0 jsr hddreaddirsel lda #NAME_LENGTH sta bloklo lda #>(hdddirbuf - 1) sta blokhi ;there can be only one page crossed, so we can increment here @mynextent1 inc blokhi @mynextent ldy #0 lda (bloklo), y pha and #$0f tax -- iny lda (bloklo), y cmp (namlo), y beq @myfoundname ;match failed, move to next directory in this block, if possible - pla @myskiphdr clc lda bloklo adc #ENTRY_SIZE sta bloklo bcs @mynextent1 cmp #$ff ;4 + ($27 * $0d) bne @mynextent ;read next directory block when we reach the end of this block lda hdddirbuf + NEXT_BLOCK_LO ldx hdddirbuf + NEXT_BLOCK_HI bcs + @myfoundname dex bne -- ;parse path until last directory is seen iny lda (namlo), y cmp #'/' bne - pla and #$20 ;Volume Directory Header XOR subdirectory bne @myskiphdr tya eor #$ff adc sizelo sta sizelo clc tya adc namlo sta namlo ;cache block number of current directory ;as starting position for subsequent searches ldy #(KEY_POINTER + 1) lda (bloklo), y tax dey lda (bloklo), y sta (reloc + unrhddblocklo - unrelochdd) + 1 stx (reloc + unrhddblockhi - unrelochdd) + 1 + sta @myx80_parms + 1 stx @myx80_parms + 3 ++ lda sizelo bne @myreadblock tay pla sta (namlo), y @go rts ;------------------------------------------------------------------------------ ; promote [private] ; ; tiny ProDOS-style interface for ProRWTS ; in: whatever ProDOS expects for the supported functions ; out: carry clear, A=0 ; X, Y, and other flags clobbered ;------------------------------------------------------------------------------ promote !pseudopc $bf00 { php sei lda $c012 !byte $24 !if * != $bf06 { !error "$BF06 misplaced (",*,")" } rts ;clock interface, must be RTS on real ProDOS if program uses $20x asl lda $c011 bit $c083 jmp ProDOS_LC !text "4Q" !if * != $bf13 { !error "$BF13 misplaced (",*,")" } !byte $c1 !word $c2d1, $c3d1, $c4d1, $c5d1, $c6d1, $c7d1 ProDOS_LC bit $c083 jmp ProDOS_enter ProDOS_exit sta $c082, x ProDOS_savedX ldx #$d1 ProDOS_savedY ldy #$d1 plp clc !byte $24 ProDOS_unit !if * != $bf30 { !error "$BF30 misplaced (",*,")" } !byte $d1 lda #0 rts ProDOS_fatal ;only for debugging, will be removed bit $c081 pha jsr $fe89 jsr $fe93 pla jsr $fdda jmp $ff65 ProDOS_prefix=$bfd0 ; !fill $2e } end_promote ;------------------------------------------------------------------------------ ; SaveOrRestoreScreenHoles ; preserve screen hole contents across demo execution ; to avoid crashing later on disk access ; ; in: nothing ; out: all flags clobbered ; all registers clobbered ;------------------------------------------------------------------------------ SaveOrRestoreScreenHoles lda #4 sta namhi ldx #0 stx namlo sta bloklo -- ldy #$78 - lda (namlo),y pha lda holey_stuff,x holepatch ;sta->lda lda (namlo),y pla sta holey_stuff,x inx tya eor #$80 tay bmi - iny bpl - inc namhi dec bloklo bne -- rts holey_stuff !fill 64 ;------------------------------------------------------------------------------ ; ProDOS_enter ; intercept certain ProDOS requests ; wrap them to ProRWTS2 file requests ; ; in: return address+1 is command and pointer to parameter block ; out: all flags clobbered ; A=0, X and Y preserved ; stack set to next instruction after parameters ;------------------------------------------------------------------------------ packet = first_zp ;word buffer = first_zp+2 ;word ProDOS_enter !set CloseHandles = @imp_close stx ProDOS_savedX+1 sty ProDOS_savedY+1 pha jsr @swap_zp pla tax pla tay pla sta @fetchaddr+1 pla sta @fetchaddr+2 jsr @fetchbyte sta @request+1 jsr @fetchbyte sta packet jsr @fetchbyte sta packet+1 lda @fetchaddr+2 pha lda @fetchaddr+1 pha tya pha php txa pha @request lda #$d1 cmp #$80 beq @do_readblock cmp #$81 ;I don't remember if I used this, but it's cheap to add beq @do_writeblock cmp #$c4 beq @do_getattrib cmp #$c6 beq @do_nothing cmp #$c7 beq @do_prefix cmp #$c8 beq @do_open cmp #$ca beq @do_read cmp #$cb beq @do_write cmp #$cc beq @do_close cmp #$ce beq @do_seek cmp #$d1 beq @do_eof ;;any others?? jmp ProDOS_fatal @do_readblock @do_writeblock jmp @imp_rdwrblock @do_getattrib jmp @imp_getattrib @do_nothing jmp @restore_zp @do_prefix jmp @imp_prefix @do_open jmp @imp_open @do_read jmp @imp_read @do_write jmp @imp_write @do_close jsr @imp_close ;subroutine special case because of dual-use jmp @restore_zp @do_seek jmp @imp_seek @do_eof jmp @imp_eof @imp_rdwrblock and #$7f tay iny sty @rdwrop+1 ldx #$44 ldy #2 jsr @setbuffer iny lda (packet), y tax iny lda (packet), y @rdwrop ldy #$d1 ; SMC jsr hddseekrd+2 bcc @jmp_zp2 ;always @imp_getattrib lda packet+1 pha lda packet pha ldx #namlo jsr @setbuffer1 lda #$60 sta attribpatch jsr hddopendir lda #$10 sta attribpatch pla sta packet pla sta packet+1 ldy #5 lda ldrlo2 sta (packet), y iny lda ldrlo2+1 sta (packet), y ldy #$13 lda (bloklo), y tax iny lda (bloklo), y ldy #9 sta (packet), y txa dey sta (packet), y @jmp_zp2 jmp @restore_zp @imp_prefix ldx #buffer jsr @setbuffer1 ldy ProDOS_prefix iny iny tya pha dey @copy_prefix lda ProDOS_prefix, y iny sta (buffer), y dey dey bne @copy_prefix pla sta (buffer), y lda #'/' iny sta (buffer), y bne @jmp_zp2 @imp_open ldx #namlo jsr @setbuffer1 iny inc @handles+1 @handles ldx #0 iny lda (packet), y sta @handle-1, x jsr @patch_buffer iny lda #1 sta (packet), y lda #0 sta reqcmd sta sizehi sta sizelo jsr hddopendir lda #0 sta blkidx beq @jmp_zp ;always @imp_seek jsr @set_rdwrbuff lda #0 sta blkidx sta blkofflo sta blkoffhi sta reqcmd jsr @reset ldx sizelo beq @seek64 sta sizehi sta sizelo jsr @seekreset inc ldrlo bne @seek64 inc ldrhi @seek64 lda ldrhi sta sizehi lda ldrlo sta sizelo jsr @seekreset lda ldrhi ora ldrlo bne @jmp_zp dec blkidx beq @jmp_zp ;always @seekreset jsr hddrdwrpart @reset lda #$ff sta blefthi sta bleftlo rts @imp_read clc @imp_write php lda #cmdread adc #0 sta reqcmd jsr @set_rdwrbuff plp bcc @skip_align lda sizelo adc #$fe lda sizehi adc #1 and #$fe sta sizehi @skip_align jsr hddrdwrpart ldy #6 lda sizelo2 sta (packet), y iny lda sizehi2 sta (packet), y @jmp_zp jmp @restore_zp @imp_eof ldy #2 lda bleftlo sta (packet), y iny lda blefthi sta (packet), y bcs @restore_zp ;always @imp_close lda @handles+1 beq @close_ret dec @handles+1 bne @close_ret lda #>hddencbuf @patch_buffer sta encbufpatch1+1 sta encbufpatch2+1 tax inx stx dirbufpatch1+1 inx stx dirbufpatch2+2 stx dirbufpatch3+2 stx dirbufpatch4+2 stx dirbufpatch6+1 stx dirbufpatch7+2 stx dirbufpatch9+2 stx dirbufpatch10+1 inx stx dirbufpatch5+2 stx dirbufpatch8+2 @close_ret rts @fetchbyte inc @fetchaddr+1 bne @fetchaddr inc @fetchaddr+2 @fetchaddr lda $d1d1 rts @set_rdwrbuff ldy #1 lda (packet), y tax lda @handle-1, x jsr @patch_buffer ldx #ldrlo iny jsr @setbuffer ldx #sizelo iny !byte $2c @setbuffer1 ldy #1 @setbuffer lda (packet), y sta $0,x iny lda (packet), y sta $1,x rts @swap_zp ldx #last_zp-first_zp @save_zp lda first_zp,x ldy @saved_zp,x sta @saved_zp,x sty first_zp,x dex bpl @save_zp rts @restore_zp jsr @swap_zp pla lsr lsr lsr lsr and #8 plp adc #0 tax jmp ProDOS_exit @handle !byte 0, 0 ;only up to two handles at a time @saved_zp !fill (last_zp - first_zp) + 1