; ; Hypervisor virtual filesystem - reading directory ; ; XXX optimize the flow; we do not need 256-byte SD_DIRENT buffer, directory entries can be constructed directly fs_hvsr_read_dir_open: ; Open the directory lda #$12 ; dos_opendir sta HTRAP00 +nop ; XXX handle read errors sta SD_DESC ; store directory descriptor XXX invent better name ; Reset status to OK lda #$00 sta PAR_TRACK sta PAR_SECTOR jsr util_status_SD ; Provide pointer to the header lda #$20 sta SD_ACPTR_LEN+0 lda #$00 sta SD_ACPTR_LEN+1 lda #dir_hdr_sd sta SD_ACPTR_PTR+1 ; Set directory phase to 'file name' lda #$01 sta SD_DIR_PHASE ; End jmp dos_EXIT fs_hvsr_read_dir: ; Read dirent structure into $1000 and then copy it to SD_DIRENT ; Starting at $1000 VIC sees chargen, so this should be a safe place ; Content of the original memory location will be preserved jsr fs_hvsr_dirent_prepare ldx SD_DESC ; directory descriptor ldy #$10 ; target page number lda #$14 ; dos_readdir sta HTRAP00 +nop jsr fs_hvsr_dirent_swap ; processor status is preserved ; If nothing to read, output 'blocks free' +bcc fs_hvsr_read_dir_blocksfree ; Dirent structure - important fields: ; - $00-$3F - file name ; - $40 - file name length ; - $52-$55 - file length ; - $56-$57 - file type ($10 = dir, $20 = regular) and attributes ($01 for read-only) ; Retrieve read-only status, store mark in SD_DIRENT+$FE lda SD_DIRENT+$56 and #$01 bne @rdonly lda #$00 +skip_2_bytes_trash_nvz @rdonly: lda #'<' sta SD_DIRENT+$FE ; Validate/fix characters in file name ldx SD_DIRENT+$40 dex @lp1: lda SD_DIRENT,x cmp #$22 beq fs_hvsr_read_dir ; quotation mark is illegal cmp #$2A beq fs_hvsr_read_dir ; asterisk is illegal, it is used for filtering cmp #$3F beq fs_hvsr_read_dir ; the question mark - in the MS-DOS world it is illegal cmp #$20 bcc fs_hvsr_read_dir ; control characters are not allowed cmp #$5B bcc @lp1_next ; up to PETSCII 'Z' is OK cmp #$61 bcc fs_hvsr_read_dir ; some characters are not allowed cmp #$7E bne @lp1_1 lda #$A6 ; replace tilde / pi with something more sane bra @lp1_store_next @lp1_1: cmp #$7B bcs fs_hvsr_read_dir ; PETSCII-art and control characters are illegal clc sbc #$20 ; make everything the same case @lp1_store_next: sta SD_DIRENT,x @lp1_next: dex bpl @lp1 ; Determine type (file/directory), store marker offset in SD_DIRENT+$FF lda SD_DIRENT+$56 and #$FE beq @cklen_file ; workaround for older hypervisors and #$20 bne @cklen_file ; branch on file lda SD_DIRENT+$56 and #$10 beq fs_hvsr_read_dir ; this type is unknown @cklen_dir: ; Validate file name length (directories) lda SD_DIRENT+$40 beq fs_hvsr_read_dir cmp #$11 bcs fs_hvsr_read_dir ; Set type offset for directory in SD_DIRENT+$FF lda #$10 bra @all_prepared @cklen_file: ; Validate file name length (regular files) lda SD_DIRENT+$40 sec sbc #$04 ; strip 4 bytes - for dot and extension sta SD_DIRENT+$40 +bmi fs_hvsr_read_dir ; if name too short, try next entry +beq fs_hvsr_read_dir ; if name too short, try next entry cmp #$11 +bcs fs_hvsr_read_dir ; if stripped name is 17 characters or longer, try the next entry sta SD_DIRENT+$40 ; store corrected file name length tax ; Detect file type lda SD_DIRENT, x cmp #$2E +bne fs_hvsr_read_dir ; 3-character extension is required! inx ldy #$00 @lpdetect: lda SD_DIRENT+0, x cmp dir_types+0, y bne @lpdetect_next lda SD_DIRENT+1, x cmp dir_types+1, y bne @lpdetect_next lda SD_DIRENT+2, x cmp dir_types+2, y beq @lpdetect_found @lpdetect_next: iny iny iny iny cpy #(dir_types__end - dir_types) bcc @lpdetect jmp fs_hvsr_read_dir ; file extension not detected @lpdetect_found: cpy #$10 +beq fs_hvsr_read_dir ; file extension 'DIR' does not mean this is a directory tya @all_prepared: sta SD_DIRENT+$FF ; store offset to file type in SD_DIRENT+$FF cmp #$14 bcc @noforcedro lda #'<' ; for some file types, force the read-only flag sta SD_DIRENT+$FE @noforcedro: ; Prepare output entry, starting from SD_DIRENT+$80 lda #$01 ; link to the next line - dummy, will be regenerated by BASIC sta SD_DIRENT+$80 sta SD_DIRENT+$81 ldx SD_DIRENT+$40 ; make sure file name is terminated by 0 lda #$00 sta SD_DIRENT, x lda SD_DIRENT+$52 ; calculate file length in blocks to display sta PAR_FSIZE_BYTES+0 lda SD_DIRENT+$53 sta PAR_FSIZE_BYTES+1 lda SD_DIRENT+$54 sta PAR_FSIZE_BYTES+2 lda SD_DIRENT+$55 sta PAR_FSIZE_BYTES+3 jsr util_dir_filesize_bytes lda PAR_FSIZE_BLOCKS+0 sta SD_DIRENT+$82 lda PAR_FSIZE_BLOCKS+1 sta SD_DIRENT+$83 lda #' ' ldx #$04 ; indent number of blocks @lpindent1: dey bmi @lpindent1_done sta SD_DIRENT+$80, x inx bra @lpindent1 @lpindent1_done: lda #$22 ; opening quote sta SD_DIRENT+$80, x ldy #$FF ; put file name @lp2: inx iny lda SD_DIRENT, y sta SD_DIRENT+$80,x bne @lp2 lda #$22 ; closing quote sta SD_DIRENT+$80, x inx lda #' ' ; put spaces for indentation ldy SD_DIRENT+$40 @lp3: sta SD_DIRENT+$80, x inx iny cpy #$11 bcc @lp3 ; No 'damaged' mark make sense for this filesystem ldy SD_DIRENT+$FF ; put file type @lp4: lda dir_types, y beq @lp4_done sta SD_DIRENT+$80, x inx iny bra @lp4 @lp4_done: lda SD_DIRENT+$FE ; read-only mark (if needed) beq @skipro sta SD_DIRENT+$80, x inx @skipro: lda #$00 sta SD_DIRENT+$80, x inx stx SD_ACPTR_LEN+0 sta SD_ACPTR_LEN+1 lda #<(SD_DIRENT+$80) sta SD_ACPTR_PTR+0 lda #>(SD_DIRENT+$80) sta SD_ACPTR_PTR+1 clc rts fs_hvsr_read_dir_blocksfree: ; Set pointer to 'BLOCKS FREE.' line ; XXX determine free space lda #$13 sta SD_ACPTR_LEN+0 lda #$00 sta SD_ACPTR_LEN+1 lda #dir_end sta SD_ACPTR_PTR+1 ; Mark end of directory lda #$00 sta SD_DIR_PHASE ; Close the directory within the hypervisor XXX maybe move it to close routine ldx SD_DESC lda #$16 ; dos_closedir sta HTRAP00 +nop clc rts ; ; Helper routines ; fs_hvsr_dirent_prepare: ldx #$00 txa @1: ldy $1000, x sty SD_DIRENT, x sta $1000, x inx bne @1 rts fs_hvsr_dirent_swap: php ldx #$00 @1: lda $1000, x ldy SD_DIRENT, x sta SD_DIRENT, x sty $1000, x inx bne @1 plp rts