Directory Reading (aka, EOF detection)

Basic and Machine Language

Moderator: Moderators

User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Directory Reading (aka, EOF detection)

Post by chysn »

After spending about a half hour pouring through Beamrider's hyperlink KERNAL disassembly for code related to getting a disk directory, I realized that the VIC-20 itself knows nothing about directories. A "directory" is an inside joke between the user and the 1541, and the VIC-20 is entirely in the dark.

So, it'll be up to me to make a directory. My first instinct was to load the directory in with the KERNAL's LOAD. Problem with that is, with things like SD2IEC, it's very easy to wind up with a bigger directory than the 3583 bytes a cartridge will have available. So I want to use CHRIN to bring it in one byte at a time.

Right now, my directory program runs forever. The Programmer's Reference Guide suggests CMP #CR to determine when CHRIN is done, and this doesn't make sense when you're CHRINing a BASIC program. I tried BCS, and, sadly, carry is not set when there's nothing else to do.

Here's what I have right now:

Code: Select all

Dir:        lda #1              ; SETNAM - (1) Set name length
            ldx #<lfs+1         ; - Set name as the $ used below
            ldy #>lfs+1         ; ,,
            jsr SETNAM          ; ,,
lfs:        lda #"$"            ; SETLFS - Set file number as $
            ldx #8              ; - Device number
            ldy #0              ; - Command
            jsr SETLFS          ; ,,
            jsr OPEN
            ldx #"$"
            jsr CHKIN
-loop:      jsr CHRIN
            bcs done
            jsr CHROUT
            jmp loop
done:       jsr CLRCHN
            lda #"$"
            jsr CLOSE
            rts
I know I have much more to do, as BASIC has pointers to next line, and line terminators. I plan on getting into the line, and using quotation marks as filename delimiters.

Anyway, do I just have to look for the zeroes? I guess I can stop at the first BASIC line that fails to contain a matching pair of quotation marks.

It seems weird that the KERNAL wouldn't be able to handle EOF in some way.
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5

WIP: MIDIcast BASIC extension

he/him/his
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: Directory Reading (aka, EOF detection)

Post by Mike »

chysn wrote:After spending about a half hour pouring through Beamrider's hyperlink KERNAL disassembly for code related to getting a disk directory, I realized that the VIC-20 itself knows nothing about directories.
It doesn't need to ... ;) ... you're using a computer network, where the necessary procedures to build a directory listing are hosted in the drive unit, within CBM DOS.

Here's a working solution to display a directory without LOAD and LIST, eliminating the issues you mention (overlength, and your own BASIC program is overwritten): Reading disk directory to screen.
CHRINing [...] I tried BCS, and, sadly, carry is not set when there's nothing else to do.
I also came across that, though not when reading a directory (there it suffices to check for a null link pointer at the end), here's what I do: CHRIN patch sub-routine -

Code: Select all

.GetByte
 JSR $FFCF ; call CHRIN
 PHA
 LDA $90
 CMP #$01  ; set C, if ST >= 1 (i.e. ST != 0)
 PLA       ; restore Accu, keep C flag, and set Z flag, if A=0
 RTS
Note EOF gets signalled already with the last byte sent, not after it.
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Directory Reading (aka, EOF detection)

Post by chysn »

Here's my refined version. It's actually very similar to the one you linked to, except I'm interested in only a filename, so I'm keeping track of quotes. Like I said in the OP, lack of a quote pair indicates the end of the directory. In the actual application, I'll be parsing the filenames for two specific extensions. Other stuff, like file size, I dispose of entirely.

Code: Select all

Dir:        lda #1              ; SETNAM - (1) Set name length
            ldx #<lfs+1         ; - Set name as the $ used below
            ldy #>lfs+1         ; ,,
            jsr SETNAM          ; ,,
lfs:        lda #"$"            ; SETLFS - Set file number as $
            ldx #8              ; - Device number
            ldy #0              ; - Command
            jsr SETLFS          ; ,,
            jsr OPEN
            ldx #"$"
            jsr CHKIN
            jsr CHRIN           ; Dispose of PRG header
            jsr CHRIN           ; ,,
newline:    lda #0              ; Set quote flag to 0. Needs to be 0 because
            sta QUOTE_FL        ;   this flag has three possible states
            ldx #4              ; Dispose of next line pointer and
-loop:      jsr CHRIN           ;   line number
            dex                 ;   ,,
            bne loop            ;   ,,
-loop:      jsr CHRIN           ; Get next character from line
            beq EOL             ; 0 indicates end-of-line
            cmp #$22            ; Is character a quotation mark?
            bne proc_name       ; If not, go process the name
            sec                 ; Set the quote flag to either %10000000 or
            ror QUOTE_FL        ;   %11000000
            bcc loop            ; Go back for next character
proc_name:  bit QUOTE_FL        ; Check the quote state
            bvs loop            ; If bit 6 is set, the quote is finished
            bpl loop            ; If bit 7 is clear, the quote hasn't started
            jsr CHROUT          ; If quote has started but not finished, it's
            jmp loop            ;   part of a name
EOL:        lda #$0d            ; In real application this will prepare for
            jsr CHROUT          ;   the next filename
            bit QUOTE_FL        ; Check the quote flag. Bit 6 is set for a PAIR
            bvs newline         ;   of quotes. If found, go back for more
EOF:        jsr CLRCHN          ; Clear input channel and close file when
            lda #"$"            ;   a quote pair isn't found
            jsr CLOSE           ;   ,,
            rts    
I did not know that EOF was signaled in $90, thanks for pointing that out! It's the kind of thing I'd have expected to find in the Programmer's Reference Guide in the KERNAL reference. I'm getting promising results with this, but I guess it assumes we don't care about the last byte. I guess this test can be done after the byte is handled.

Code: Select all

bit $90
bvs EOF
But I can see the value of testing for all status flags.
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Directory Reading (aka, EOF detection)

Post by chysn »

Here's an update with the failure checking for each CHRIN. In the application, I'll also put carry checks on all the intermediate steps (OPEN, etc.)

Code: Select all

QUOTE_FL    = $fa               ; Quote flag
IOSTATUS    = $90               ; I/O Status

*=$1800
Dir:        lda #1              ; SETNAM - (1) Set name length
            ldx #<lfs+1         ; - Set name as the $ used below
            ldy #>lfs+1         ; ,,
            jsr SETNAM          ; ,,
lfs:        lda #"$"            ; SETLFS - Set file number as $
            ldx #8              ; - Device number
            ldy #0              ; - Command
            jsr SETLFS          ; ,,
            jsr OPEN
            ldx #"$"
            jsr CHKIN
            jsr CharIn          ; Dispose of PRG header
            jsr CharIn          ; ,,
newline:    lda #0              ; Set quote flag to 0. Needs to be 0 because
            sta QUOTE_FL        ;   this flag has three possible states
            ldx #4              ; Dispose of next line pointer and
-loop:      jsr CharIn          ;   line number
            dex                 ;   ,,
            bne loop            ;   ,,
-loop:      jsr CharIn          ; Get next character from line
            beq EOL             ; 0 indicates end-of-line
            cmp #$22            ; Is character a quotation mark?
            bne proc_name       ; If not, go process the name
            sec                 ; Set the quote flag to either %10000000 or
            ror QUOTE_FL        ;   %11000000
            bcc loop            ; Go back for next character
proc_name:  bit QUOTE_FL        ; Check the quote state
            bvs loop            ; If bit 6 is set, the quote is finished
            bpl loop            ; If bit 7 is clear, the quote hasn't started
            jsr CHROUT          ; If quote has started but not finished, it's
            jmp loop            ;   part of a name
EOL:        lda #$0d            ; In real application this will prepare for
            jsr CHROUT          ;   the next filename
            bit QUOTE_FL        ; If the line ends without a filename in quotes
            bvs newline         ;   go to end of file. Else go back for more.
            .byte $3c           ; Skip byte (pha would also be okay)
Fail:       pla                 ; CharIn has failed           
EOF:        jsr CLRCHN          ; Clear input channel and close file when
            lda #"$"            ;   a quote pair isn't found
            jsr CLOSE           ;   ,,
            rts    
            
; Character Input
; With tests for failure            
CharIn:     jsr CHRIN
            pha
            lda IOSTATUS
            bne Fail
            pla
            rts
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: Directory Reading (aka, EOF detection)

Post by Mike »

chysn wrote:I did not know that EOF was signaled in $90, thanks for pointing that out! It's the kind of thing I'd have expected to find in the Programmer's Reference Guide in the KERNAL reference.
Well, the entry in the PRG regarding CHRIN, on page 187, reads: "Error returns: See READST". That routine in turn (see pp. 198..199) does nothing other than return the STatus byte (located in $90) in A. As you've noted, bit 6 (value: 64) handles EOI/EOF.
I'm getting promising results with this, but I guess it assumes we don't care about the last byte. I guess this test can be done after the byte is handled.

Code: Select all

bit $90
bvs EOF
I deviced my CHRIN "patch" to guard file I/O against anything extraordinary (before that, my own directory routine in MINIPAINT would hang when someone opened the drive while reading the directory!), but your "BIT $90/BVS" combo points out EOF in a nice way. Still, the first byte sent along with EOF is valid. Of course that differs from the handling of EOF in, say, C where EOF is a return value that can be differentiated from all other byte values.
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Directory Reading (aka, EOF detection)

Post by chysn »

Mike wrote: Wed Aug 18, 2021 10:47 pm I deviced my CHRIN "patch" to guard file I/O against anything extraordinar
Yeah, and your way is ultimately what I used in my routine. A good directory shouldn't reach EOF until the "BLOCKS FREE" line is reached, at which time my missing-quote-pair condition should have caught it. So if it doesn't happen exactly that way, I'll display a "DISK ERROR" message.

Cassette support is a few dozen bytes. Disk support is some real work.
wimoos
Vic 20 Afficionado
Posts: 348
Joined: Tue Apr 14, 2009 8:15 am
Website: http://wimbasic.webs.com
Location: Netherlands
Occupation: farmer

Re: Directory Reading (aka, EOF detection)

Post by wimoos »

In WimBasic I have a 'directory' implementation that even supports redirection of the directory listing to a file, look here:

viewtopic.php?f=2&t=9782#p109534

The only Basic interpreter routine it uses is $DDCD (print unsigned integer) :-)
VICE; selfwritten 65asmgen; tasm; maintainer of WimBasic
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Directory Reading (aka, EOF detection)

Post by chysn »

wimoos wrote: Fri Aug 20, 2021 2:37 am In WimBasic I have a 'directory' implementation that even supports redirection of the directory listing to a file, look here:
Thank you, this is instructive. I've been working on error handling, simulating errors in VICE by detaching the disk image with True Drive Emulation. I see that you close the file after an error. I just learned that even when OPEN fails, CLOSE must be called or the file will not open again once the drive is re-attached!

I'm calling CLALL ($ffe7) at the top of my error routine, which allows for recovery. I can do this because I'm in a cartridge environment, and it's a very short list of things that CLALL might affect.

Another thing I'd recommend to anybody doing this sort of work is, watch your stack usage for error recovery. In my code above, I have a subroutine (CharIn) that jumps to the Dir routine on error. I've updated this so that CharIn simply returns with carry set on failure, to avoid stack problems. But also consider what happens when you go from the directory subroutine to an error recovery state. You don't want to leave return addresses hanging all over the place!
User avatar
srowe
Vic 20 Scientist
Posts: 1340
Joined: Mon Jun 16, 2014 3:19 pm

Re: Directory Reading (aka, EOF detection)

Post by srowe »

chysn wrote: Fri Aug 20, 2021 6:20 am I'm calling CLALL ($ffe7) at the top of my error routine, which allows for recovery. I can do this because I'm in a cartridge environment, and it's a very short list of things that CLALL might affect.
CLALL only resets the file table on the VIC, it does absolutely nothing to close the channels open on any external device.

Rather than using OPEN, CHKIN etc I would recommend using TALK, ACPTR etc to directly talk to devices on the IEC bus.
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Directory Reading (aka, EOF detection)

Post by chysn »

srowe wrote: Fri Aug 20, 2021 10:14 am
chysn wrote: Fri Aug 20, 2021 6:20 am I'm calling CLALL ($ffe7) at the top of my error routine, which allows for recovery. I can do this because I'm in a cartridge environment, and it's a very short list of things that CLALL might affect.
CLALL only resets the file table on the VIC, it does absolutely nothing to close the channels open on any external device.
Whatever it does, it does the trick. Without CLALL, once there's an error, carry is set on every subsequent OPEN attempt with the same file number. With CLALL, the OPEN succeeds once the error condition (usually detachment in VICE or power deprivation in hardware) is rectified. This is the case in both VICE and real VIC. Literally the only difference between an error state being recoverable without restart or not is JSR CLALL in the error handler.
srowe wrote: Fri Aug 20, 2021 10:14 am Rather than using OPEN, CHKIN etc I would recommend using TALK, ACPTR etc to directly talk to devices on the IEC bus.
Aw, man! I'm always happy to learn a new thing, but I've just got everything working nice and reliably! What's the upside of TALK when getting a directory?
User avatar
srowe
Vic 20 Scientist
Posts: 1340
Joined: Mon Jun 16, 2014 3:19 pm

Re: Directory Reading (aka, EOF detection)

Post by srowe »

chysn wrote: Fri Aug 20, 2021 12:07 pm Aw, man! I'm always happy to learn a new thing, but I've just got everything working nice and reliably! What's the upside of TALK when getting a directory?
Going through the higher layer KERNAL calls wastes cycles as it checks for the target being on the IEC bus.
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Directory Reading (aka, EOF detection)

Post by chysn »

srowe wrote: Fri Aug 20, 2021 12:54 pm
chysn wrote: Fri Aug 20, 2021 12:07 pm Aw, man! I'm always happy to learn a new thing, but I've just got everything working nice and reliably! What's the upside of TALK when getting a directory?
Going through the higher layer KERNAL calls wastes cycles as it checks for the target being on the IEC bus.
So it makes disk I/O faster? I figured the bottlenecks were elsewhere.

But anyway, I've been Googling for a while, and I haven't been able to find one single code example of ACPTR being used outside the ROM. How would I specify that "$" should be sent before calling ACPTR?
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Directory Reading (aka, EOF detection)

Post by chysn »

I think I see what you're getting at, though, there in the ROM disassembly. CHRIN and friends need to do a lot of stuff to see what they're actually communicating with before they finally get to calling ACPTR themselves. So the type of communication is fundamentally identical, but skipping CHRIN might bypass checks that I don't necessarily care about.

I'm not sure that skipping those checks is that compelling. I mean, I use $FFD8 instead of $F675 in my code, even though it'll never target anything but a VIC-20 with a normal ROM. On some level I must value the "standardization" over some tiny amount of time.

Update: I did try JSR ACPTR versus JSR CHRIN, to skip those checks, and there's no time difference for the number of calls that must be made to get a directory.
User avatar
srowe
Vic 20 Scientist
Posts: 1340
Joined: Mon Jun 16, 2014 3:19 pm

Re: Directory Reading (aka, EOF detection)

Post by srowe »

chysn wrote: Fri Aug 20, 2021 1:35 pm Update: I did try JSR ACPTR versus JSR CHRIN, to skip those checks, and there's no time difference for the number of calls that must be made to get a directory.
There's no difference in the number of calls but there is a difference in the number of cycles. To reach FACPTR from CHRIN takes 28 cycles, from ACPTR it takes 3. That's for each byte received.
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Directory Reading (aka, EOF detection)

Post by chysn »

srowe wrote: Sat Aug 21, 2021 2:11 am
chysn wrote: Fri Aug 20, 2021 1:35 pm Update: I did try JSR ACPTR versus JSR CHRIN, to skip those checks, and there's no time difference for the number of calls that must be made to get a directory.
There's no difference in the number of calls but there is a difference in the number of cycles. To reach FACPTR from CHRIN takes 28 cycles, from ACPTR it takes 3. That's for each byte received.
This poses an interesting philosophical question, because we all face questions about what level of abstraction we want to work at.

Clearly there's a 25-cycle difference per call in looking at the ROM. But that difference doesn't translate to a savings in cycles on physical hardware*. The cycles are just used for something else, probably waiting for the drive to do whatever it's doing. On VICE's perfect drive, the difference is 25 cycles-per-call, as expected, but it accounts for only about 3% of the total I/O operation. I was unable to test SD2IEC, but my guess it that it's closer to VICE. It doesn't seem as fast, so that might involve some waiting around, too.

Test results are below

Loading 508 bytes, watching only the outer loop that calls CHRIN (using the third version of the program posted earlier), took an average of 1536698 cycles with VICE's true drive emulation*. Using ACPTR instead, the same I/O operation took an average of 1534469 cycles. This was across six trials with each type of call.

With true drive emulation off, the routine was much quicker and consistent. CHRIN was exactly 347089 cycles, and ACPTR was exactly 334142 cycles. (347089c-334142c)/508b = 25.486 cycles per byte, which is the actual difference, plus a fraction that I don't immediately understand.

* I did make the assumption that VICE's true drive emulation is a reasonable emulation of a hardware 1541
Post Reply