ProDOS8/MLI.SRC/BFMGR.S

403 lines
17 KiB
ArmAsm

TTL 'ProDOS Block File Manager'
**************************************************
* ProDOS block file manager
* Perform filing or housekeeping functions
* (X)=call # ($00-$13)
BFMgr LDA Dispatch,X ;Translate into command address
ASL ;(bit 7 indicates a pathname to preprocess)
STA cmdTmp
AND #$3F ;(bit6 is refnum preprocess, 5 is for time, so strip em.)
TAX
LDA cmdTable,X ;Move address for indirect jump
STA goAdr
LDA cmdTable+1,X ;(high byte)
STA goAdr+1
LDA #backupNeeded ;Init "backup bit flag"
STA bkBitFlg ; to say "file modified"
BCC NoPath
* For MLI calls $C0-$C4, $C8
JSR SetPath ;Go process pathname before calling command
BCS ErrorSys ;Branch if bad name
NoPath ASL cmdTmp ;Test for refnum preprocessing
BCC NoPreRef
* For MLI calls $C9-$CB, $CE-$D3
JSR FindFCB ;Go set up pointers to fcb and vcb of this file
BCS ErrorSys ;branch if any errors are encountered
NoPreRef ASL cmdTmp ;Lastly check for necessity of time stamp
BCC Execute
* For MLI calls $C0-$C4, $CC, $CD
JSR DateTime ;(No error posible)
Execute JSR GoCmd ;Execute command
BCC GoodOp ;Branch if successful
ErrorSys JSR SysErr ;Don't come back
GoodOp RTS ;Good return
*-------------------------------------------------
* Check caller's pathname & copy to pathname buffer
SetPath LDY #c_path
LDA (parm),Y ;Get low pointer addr
STA tPath
INY
LDA (parm),Y
STA tPath+1 ; & hi pointer addr
SynPath EQU * ;Entry used by rename for second pathname
LDX #$00 ;X-reg is used as index to pathBuf
LDY #$00 ;Y-reg is index to input pathname
STX prfxFlg ;Assume prefix is in use
STX pathBuf ;Mark pathbuf to indicate nothing processed
LDA (tPath),Y ;Validate pathname length>0, and <65
BEQ ErrSyn
CMP #65
BCS ErrSyn
STA pathCnt ;This is used to compare for
INC pathCnt ; end of pathname processing
INY ;Now check for full pathname...
LDA (tPath),Y ;(Full name if starts with "/")
ORA #$80
CMP #"/"
BNE NotFullPN ;Branch if prefix appended
STA prfxFlg ;Set prefix flag to indicate prefix not used
INY ;Index to first character of pathname
NotFullPN LDA #$FF ;Set current position of pathBuf
STA pathBuf,X ; to indicate end of pathname
STA namCnt ;Also indicate no characters processed in local name
STX namPtr ;Preserve pointer to local name length byte
SynPath3 CPY pathCnt ;done with pathname processing?
BCS EndPath ;Yes
LDA (tPath),Y ;Get character
AND #$7F ;We're not interested in high order bit
INX ;Prepare for next character
INY
CMP #'/' ;Is it a slash delimiter?
BEQ EndName ;Branch if it is
CMP #'a' ;Is it lower case character?
BCC NotLower ;Branch if not
AND #$5F ;Upshift to upper case
NotLower STA pathBuf,X ;Store charcter
INC namCnt ;Is it the first of a local name?
BNE NotFirst ;Branch if not
INC namCnt ;Kick count to 1
BNE TestAlfa ;First char. Must be alpha (branch always taken)
NotFirst CMP #'.' ;Is it "."?
BEQ SynPath3 ;It's ok if it is, do next char
CMP #'0' ;Is it at least "0"?
BCC ErrSyn ;Report syntax error if not
CMP #'9'+1 ;Is it numeric?
BCC SynPath3 ;ok if it is, do next character
TestAlfa CMP #'A' ;Is it at least an "a"?
BCC ErrSyn ;Report err if not
CMP #'Z'+1 ;Is it g.t. "z"?
BCC SynPath3 ;Get next char if valid alpha
ErrSyn SEC ;Make sure carry set
LDA #badPathSyntax
RTS ;Report error
EndPath LDA #$00 ;End pathname with 0
BIT namCnt ;Also make sure name count is positive
BPL :1
STA namCnt ;=0
DEX
:1 INX
STA pathBuf,X
BEQ ErrSyn ;Report error if "/" only
STX pathCnt ;Save true length of pathname
TAX ;X=0 causes end of process, after endname
EndName LDA namCnt ;Validate local name <16
CMP #15+1
BCS ErrSyn
PHX ;Save current pointer
LDX namPtr ;Get index to beginning of local name
STA pathBuf,X ;Save local name's length
PLX ;Restore x
BNE NotFullPN ;Branch if more names to process
CLC ;Indicate success!
LDA prfxFlg ; but make sure all pathnames are
BNE EndRTS ; prefixed or begin with a "/"
LDA NewPfxPtr ; must be non-zero
BEQ ErrSyn
EndRTS RTS
**************************************************
* SETPREFIX Call
SetPrefix JSR SetPath ;Call is made here so a 'null' path may be detected
BCC :1 ;Branch if pathname ok
LDY pathBuf ;Was it a nul pathname?
BNE PfxErr ;Branch if true syntax error
JSR ZeroPfxPtrs ;Indicate null prefix. NB. (Y)=0
CLC
RTS
:1 JSR FindFile ;Go find specified prefix directory
BCC :2 ;Branch if no error
CMP #badPathSyntax
BNE PfxErr ;Branch if error is real (not root dir)
:2 LDA d_file+d_stor ;Make sure last local name is DIR type
AND #directoryFile*16;(either root or sub)
EOR #directoryFile*16;Is it a directory?
BNE PfxTypErr ;Report wrong type
LDY prfxFlg ;New or appended prefix?
BNE :3 ;(A)=0 if branch taken
LDA NewPfxPtr ;Append new prefix to old
:3 TAY
SEC ;Find new beginning of prefix
SBC pathCnt
CMP #$C0 ;Too long? ($100-$40)
BCC ErrSyn ;Report it if so
TAX
JSR SetPfxPtrs
LDA d_dev ;Save device number
STA pathDev
LDA d_file+d_first ; & addr of first block
STA pathBlok
LDA d_file+d_first+1
STA pathBlok+1
MovPrefix LDA pathBuf,Y
STA pathBuf,X
INY
INX
BNE MovPrefix
CLC ;Indicate good prefix
RTS
PfxTypErr LDA #badStoreType ;Report not a directory
PfxErr SEC ;indicate error
RTS
**************************************************
* GETPREFIX Call
GetPrefix CLC ;Calculate how big a buffer is needed to
LDY #c_path ;Get index to user's pathname buffer
LDA (parm),Y
STA userBuf
INY
LDA (parm),Y
STA userBuf+1
STZ cBytes+1 ;Set buf length at max
LDA #64 ;(64 characters max)
STA cBytes
JSR ValDBuf ;Go validate prefix buffer addr
BCS PfxErr
LDY #$00 ;Y is indirect index to user buffer
LDA NewPfxPtr ;Get address of beginning of prefix
TAX
BEQ NullPrefix ;Branch if null prefix
EOR #$FF ;Get total length of prefix
ADC #$02 ;Add 2 for leading and trailing slashes
NullPrefix STA (userBuf),Y ;Store length in user's buffer
BEQ GotPrefix ;Branch if null prefix
SendPrefix INY ;Bump to next user buf loc
LDA pathBuf,X ;Get next char of prefix
SndLimit STA (userBuf),Y ;Give character to user
AND #$F0 ;Check for length descriptor
BNE :1 ;Branch if regular character
LDA #'/' ;Otherwise, substitute a slash
BNE SndLimit ;Branch always
:1 INX
BNE SendPrefix ;Branch if more to send
INY
LDA #'/' ;End with slash
STA (userBuf),Y
GotPrefix CLC ;Indicate no error
RTS
*-------------------------------------------------
* Validity check the ref # passed by caller
FindFCB LDY #c_refNum ;Index to reference number
LDA (parm),Y ;Is it a valid file number?
BEQ ErrRefNum ;Must not be 0!
CMP #8+1 ;Must be 1 to 8 only
BCS ErrRefNum ;User must be stoned...
PHA
DEC ;(subtracts 1)
LSR ;Shift low 3 bits to high bits
ROR
ROR
ROR ;Effective multiply by 32
STA fcbPtr ;Later used as an index
TAY ; to FCB like now
PLA ;Restore refnum in A-reg
CMP fcb+fcbRefNum,Y ;Is it an open reference?
BNE ErrNoRef ;Branch if not
FndFCBuf LDA fcb+fcbFileBuf,Y;Get page addr of file buffer
JSR GetBufAdr ;Get file's address into bufAddrL & H
LDX bufAddrH ;(Y)=fcbptr - preserved
BEQ FCBDead ;Report FCB screwed up!!!
STX dataPtr+1 ;Save pointer to data area of buffer
INX
INX ;Index block always 2 pages after data
STX tIndex+1
LDA fcb+fcbDevNum,Y ;Also set up device number
STA DevNum
LDA bufAddrL
STA dataPtr ;Index and data buffers
STA tIndex ; always on page boundaries
SrchVCBs TAX ;Search for associated VCB
LDA vcb+vcbDevice,X
CMP fcb+fcbDevNum,Y ;Is this VCB the same device?
BEQ TestVOpen ;If it is, make sure volume is active
NxtBufr TXA ;Adjust index to next VCB
CLC
ADC #vcbSize
BCC SrchVCBs ;Loop until volume found
LDA #vcbUnusable ;Report open file has no volume...
JSR SysDeath ; & kill the system
FCBDead LDA #fcbUnusable ;Report FCB trashed
JSR SysDeath ; & kill the system
TestVOpen LDA vcb,X ;Make sure this VCB is open
BEQ NxtBufr ;Branch if it is not active
STX vcbPtr ;Save pointer to good VCB
CLC ;Indicate all's well
RTS
ErrNoRef LDA #$00 ;Drop a zero into this FCB
STA fcb+fcbRefNum,Y ; to show free FCB
ErrRefNum LDA #invalidRefNum ;Tell user that requested refnum
SEC ; is illegal (out of range) for this call
RTS
**************************************************
* ONLINE call
Online JSR MovDBuf ;Move user specified buffer pointer to usrbuf
STZ cBytes ;Figure out how big buffer has to be
STZ cBytes+1
LDY #c_devNum
LDA (parm),Y ;If zero then cbytes=$100, else =$010 for one device
AND #$F0
STA DevNum
BEQ :1 ;Branch if all devices
LDA #$10
STA cBytes
BNE :2 ;Always
:1 INC cBytes+1 ;Allow for up to 16 devices
:2 JSR ValDBuf ;Go validate buffer range against allocated memory
BCS OnlinErr
LDA #$00 ;Zero out user buffer space
LDY cBytes
:loop1 DEY
STA (userBuf),Y ;Zero either 16 or 256 bytes
BNE :loop1 ;Branch if more to zero
STA namPtr ;Use namPtr as pointer to user buffer
LDA DevNum
BNE OnlineZ ;Branch if only 1 device to process
JSR MovDevNums ;Get list of currently recognized devices
:loop2 PHX ;Save index to last item on list
LDA lookList,X ;Get next device #
STA DevNum
JSR OnlineZ ;Log this volume and return it's name to user
LDA namPtr
CLC
ADC #$10
STA namPtr
PLX ;Restore index to device list
DEX ;Index to next device
BPL :loop2 ;Branch if there is another device
LDA #$00 ;No errors for muliple on-line
CLC ;Indicate good on all volumes
OnlinErr RTS
* Generate return data for a specific device
OnlineZ JSR ScanVCB ;See if it has already been logged in
BCS OnlinErr1 ;Branch if VCB is full
LDX #$00 ;Read in root (volume) directory
LDA #$02 ;(X,A)=block #
JSR RdBlkAX ;Read it into general purpose buffer
LDX vcbPtr ;Use x as an index to the vcb entry
* This fix is to remove VCB entries that correspond to devices that
* are no longer in the device list (i.e. removed by the user).
BCC VolFound ;Branch if the read was ok
TAY ;Save error value in Y-reg
LDA vcb+vcbStatus,X ;Don't take the VCB off line if
BNE RtrnErr ; there are active files present!
STA vcb,X ;Now take the volume off line
STA vcb+vcbDevice,X
RtrnErr TYA ;Now return error to A
BCS OnlinErr1
* 1st vol dir blk has been read successfully
VolFound LDA vcb,X ;Has it been logged in before?
BEQ :1 ;Branch if not
LDA vcb+vcbStatus,X ;It has, are there active files?
BMI :2 ;Branch if the volume is currently busy
:1 JSR LogVCBZ ;Go log it in
BCS OnlinErr1 ;Branch if there is some problem (like notsos)
LDA #dupVolume ;Anticipate a duplicate active volume exists
BIT duplFlag
BMI OnlinErr1 ;Branch if we guessed right
:2 LDX vcbPtr ;Restore vcbptr just in case we lost it
JSR CmpVCB ;Does read in volume compare with logged volume?
LDA #drvrDiskSwitch ;Anticipate wrong volume mounted in active device
BCC Online2 ;Branch if no problem!
* On fall thru, (A)=disk switch error
* Store error code in user's data buffer
OnlinErr1 PHA ;Save error code
JSR SavDevNbr ;Tell user what device we looked at
PLA ;Get error code again
INY ;Tell user what error was encountered on this device
STA (userBuf),Y
CMP #dupVolume ;Was it a duplicate volume error?
BNE :1 ;Branch if not,
INY ;Otherwise tell user which other device has same name
LDX vcbEntry
LDA vcb+vcbDevice,X
STA (userBuf),Y
STZ duplFlag ;Clear duplicate flag
LDA #dupVolume ;Restore error code
:1 SEC ;Indicate error
RTS
* Make online volume entry
Online2 LDA vcb,X ;Get volume name count
STA namCnt
LDY namPtr ;Index to user's buffer
:loop LDA vcb,X ;Move name to user's buffer
STA (userBuf),Y
INX
INY
DEC namCnt ;Loop until all characters moved
BPL :loop
SavDevNbr LDY namPtr ;Index to first byte of this entry
LDA DevNum ;Put device number in upper nibble of this byte
ORA (userBuf),Y ;Lower nibble is name length
STA (userBuf),Y
CLC ;Indicate no errors
RTS