;license:BSD-3-Clause
;minimal open/read binary file in ProDOS filesystem
;copyright (c) Peter Ferrie 2016-2017
!cpu 6502
*=$800

                ;zpage usage, arbitrary selection except for the "PROM constant" ones
                tmpsec    = $3c
                reqsec    = $3d         ;PROM constant
                A1L       = $3c
                A1H       = $3d
                curtrk    = $40         ;PROM constant
                adrlo     = $44
                adrhi     = $45

                sizehi    = $53
                entries   = $f8         ;(internal) total number of entries in directory
                step      = $fd         ;(internal) state for stepper motor
                tmptrk    = $fe         ;(internal) temporary copy of current track
                phase     = $ff         ;(internal) current phase for seek

                ;constants
                scrn2p2   = $f87b
                dirbuf    = $e00        ;for size-optimisation
                PHASEOFF  = $c080
                Q6L       = $c08c

                !byte 1
                lsr
                bne init
                inc reqsec
                txa
                jsr scrn2p2
                ora #$c0
                pha
                lda #$5b
                pha
                rts

fakeMLI         pla
                tax
                inx
                inx
                inx
                txa
                pha
                rts

filename        !byte filename_e - filename_b
filename_b      !text "LEGENDOS.SYSTEM" ;your start-up file, file is min 513 bytes, max 64kb
filename_e

init            txa
                tay
                ora #$80
                sta unrseek+1
                ora #$0c
                sta unrread1+1
                sta unrread2+1
                sta unrread3+1
                sta unrread4+1

-               lda fakeMLI-1, x
                sta $beff, x
                lda #$ff
                sta $bf0f, x
                dex
                bne -
                sty $bf30

                sta $200

opendir         ;read volume directory key block
                txa
                ldx #2
                jsr readdirsel

                ;include volume directory header in count

firstent        lda #<(dirbuf+4)
                sta A1L
                lda #>(dirbuf+4)
                sta A1H
nextent         ldy #0
                lda (A1L), y

                ;match name lengths before attempting to match names

                lda (A1L), y
                and #$0f
                tax
                inx
                !byte $2c
-               lda (A1L), y
                cmp filename, y
                beq foundname

                ;move to next directory in this block

+               clc
                lda A1L
                adc #$27
                sta A1L
                bcc +

                ;there can be only one page crossed, so we can increment instead of adc

                inc A1H
+               cmp #<(dirbuf+$1ff) ;4+($27*$0d)
                lda A1H
                sbc #>(dirbuf+$1ff)
                bcc nextent

                ;read next directory block when we reach the end of this block

                ldx dirbuf+2
                lda dirbuf+3
                jsr readdirsec
                bne firstent

foundname       iny
                dex
                bne -

                ;cache EOF (file size)

                ldy #$15
                lda (A1L), y
                cmp #1
                iny
                lda (A1L), y
                adc #1
                lsr
                sta sizehi

                ;cache KEY_POINTER

                ldy #$11
                lda (A1L), y
                tax
                iny
                lda (A1L), y

                ;read index block in case of sapling

                jsr readdirsec

                ;restore load offset

                asl adrhi

readfile
                ;fetch data block and read it

                ldy $41 ;zeroed by boot PROM
                inc $41
                ldx dirbuf, y
                lda dirbuf+256, y
                jsr seekread

                ;loop while size-$200 is non-zero

                dec sizehi
                bne readfile

readdone        jmp $2000

                ;no tricks here, just the regular stuff

seek            sty step
                asl phase
                txa
                asl
copy_cur        tax
                sta tmptrk
                sec
                sbc phase
                beq +++
                bcs +
                eor #$ff
                inx
                bcc ++
+               sbc #1
                dex
++              cmp step
                bcc +
                lda step
+               cmp #8
                bcs +
                tay
                sec
+               txa
                pha
                ldx step1, y
+++             php
                bne +
---             clc
                lda tmptrk
                ldx step2, y
+               stx tmpsec
                and #3
                rol
                tax
                lsr
unrseek
                lda PHASEOFF, x
--              ldx #$13
-               dex
                bne -
                dec tmpsec
                bne --
                bcs ---
                plp
                beq seekret
                pla
                inc step
                bne copy_cur

step1           !byte 1, $30, $28, $24, $20, $1e, $1d, $1c
step2           !byte $70, $2c, $26, $22, $1f, $1e, $1d, $1c

readadr
-               jsr readd5aa
                cmp #$96
                bne -
                ldy #3
-               sta curtrk
                jsr readnib
                rol
                sta tmpsec
                jsr readnib
                and tmpsec
                dey
                bne -
seekret         rts

readd5aa
--              jsr readnib
-               cmp #$d5
                bne --
                jsr readnib
                cmp #$aa
                bne -
                tay                    ;we need Y=#$AA later

readnib
unrread1
-               lda Q6L
                bpl -
                rts

readdirsel      sta adrlo
readdirsec
                ldy #>dirbuf
                sty adrhi

                ;convert block number to track/sector

seekread        lsr
                txa
                ror
                lsr
                lsr
                sta phase
                txa
                and #3
                php
                asl
                plp
                rol
                sta reqsec
                jsr readadr

                ;if track does not match, then seek

                ldx curtrk
                cpx phase
                beq checksec
                jsr seek

                ;force sector mismatch

                lda #$ff

                ;match or read sector

checksec        jsr cmpsec
                inc reqsec
                inc reqsec

                ;force sector mismatch

cmpsecrd        lda #$ff

cmpsec          cmp reqsec
                beq readdata
                jsr readadr
                beq cmpsec

                ;read sector data

readdata        jsr readd5aa
                eor #$ad                ;zero A if match
;;                bne *                   ;lock if read failure
unrread2
-               ldx Q6L
                bpl -
                eor nibtbl - $96, x
                sta bit2tbl - $aa, y
                iny
                bne -
unrread3
-               ldx Q6L
                bpl -
                eor nibtbl-$96, x
                sta (adrlo), y          ;the real address
                iny
                bne -
unrread4
-               ldx Q6L
                bpl -
                eor nibtbl-$96, x
                bne cmpsecrd
--              ldx #$a9
-               inx
                beq --
                lda (adrlo), y
                lsr bit2tbl - $aa, x
                rol
                lsr bit2tbl - $aa, x
                rol
                sta (adrlo), y
                iny
                bne -
readret         inc adrhi
                rts

bit2tbl         = $300 ;PROM constant
nibtbl          = bit2tbl + 108 ;PROM constant
dataend         = nibtbl + 106 ;PROM constant
readbuff
!byte $D3,$C1,$CE,$A0,$C9,$CE,$C3,$AE
