;====================================
;
; COMPACT FLASH CONTROLLER ROM   >4000-5FFF
;
;====================================
;
$R0        EQU     >8000
$R1        EQU     >4000
$R2        EQU     >2000
$R3        EQU     >1000
$R4        EQU     >0800
$R5        EQU     >0400
$R6        EQU     >0200
$R7        EQU     >0100
$R8        EQU     >0080
$R9        EQU     >0040
$R10       EQU     >0020
$R11       EQU     >0010

C.DATAR    EQU     >5F81
C.DATAW    EQU     >5FC1
C.ERRR     EQU     >5F83
C.FEATW    EQU     >5FC3
C.SCCNTR   EQU     >5F85
C.SCCNTW   EQU     >5FC5
C.LBA0R    EQU     >5F87
C.LBA0W    EQU     >5FC7
C.LBA8R    EQU     >5F89
C.LBA8W    EQU     >5FC9
C.LBA16R   EQU     >5F8B
C.LBA16W   EQU     >5FCB
C.LBA24R   EQU     >5F8D
C.LBA24W   EQU     >5FCD
C.STAT     EQU     >5F8F
C.CMD      EQU     >5FCF
C.DCTLW    EQU     >5FDD

C.SETFEAT  EQU     >EF
C.SET8BIT  EQU     >01
C.CLR8BIT  EQU     >81

VHIMEM     EQU     >8370
IORQST     EQU     >834D
SECCNT     EQU     >834D
TRKCNT     EQU     >834D
SECTRK     EQU     >834D
BUF^       EQU     >834E
ERRFLG     EQU     >8350
SIDCNT     EQU     >8351
PAB^       EQU     >8354
FDR^       EQU     >8356
VIB^       EQU     >8358
STK^       EQU     >8366
VHP^       EQU     >8370
VDPWD      EQU     >8C00
VDPRD      EQU     >8800
VDPWA      EQU     >8C02
SEC#       EQU     >834A
DRV#       EQU     >834C
WRKSP      EQU     >8300

DIBS       EQU     267

;===[ MACROES ]================================
VPSH       MACRO
           BLWP *R9
           DATA %1
           ENDM

VPOP       MACRO
           BLWP *R9
           DATA %1+1
           ENDM

LVRA       MACRO
           BLWP *R9
           DATA %1*32+2
           ENDM

LVWA       MACRO
           BLWP *R9
           DATA %1*32+3
           ENDM

CALL       MACRO
           BL   @VCALL
           DATA %1
           ENDM

           AORG    >4000
           DEF     SFIRST,SLAST

SFIRST     BYTE    >AA                     ;STANDARD HEADER MARK
           BYTE    >01                     ;VERSION 7
           DATA    >0000                   ;NO PROGRAMS
           DATA    PWRLNK                  ;POWER-UP CHAIN PTR
           DATA    >0000                   ;PROGRAMS CHAIN PTR (NONE)
           DATA    DSRLNK                  ;DSR CHAIN PTR
           DATA    SBRLNK                  ;SUBPROGRAMS CHAIN PTR
           DATA    >0000                   ;ISR CHAIN PTR (NONE)
           DATA    >0000

SBRLNK     DATA    LNK01                   ;LINK TO NEXT SUBPROGRAM
           DATA    SECTIO                  ;ADDRESS OF THAT ONE
           DATA    >0110                   ;SUBPROGRAM NAME: >10 (SECTOR R/W)
           EVEN
LNK01      DATA    LNK02
           DATA    FMTDSK
           DATA    >0111                   ;SUBPROGRAM >11 (FORMAT DISK)
           EVEN
LNK02      DATA    LNK03
           DATA    PROT
           DATA    >0112                   ;SUBPROGRAM >12 (FILE UN/PROTECT)
           EVEN
LNK03      DATA    LNK04
           DATA    RENAME
           DATA    >0113                   ;SUBPROGRAM >13 (FILE RENAME)
           EVEN
LNK04      DATA    LNK05
           DATA    RAWRD
           DATA    >0114                   ;SUBPROGRAM >14 (FILE DIRECT INPUT)
           EVEN
LNK05      DATA    LNK06
           DATA    RAWWR
           DATA    >0115                   ;SUBPROGRAM >15 (FILE DIRECT OUTPUT)
           EVEN
LNK06      DATA    LNK07
           DATA    $FILES
           DATA    >0116                   ;SUBPROGRAM >16 (NUMBER OF FILES)
           EVEN
LNK07      DATA    LNK08                   ;NO MORE
           DATA    FILES
           BYTE    >05
           TEXT    'FILES'                 ;SAME AS >16, CALLED FROM BASIC
           EVEN
LNK08      DATA    LNK09
           DATA    MOUNT
           BYTE    >05
           TEXT    'MOUNT'
           EVEN
LNK09      DATA    LNK10
           DATA    UNMOUNT
           BYTE    7
           TEXT    'UNMOUNT'
           EVEN
LNK10      DATA    0        
           DATA    FORMAT 
           BYTE    6
           TEXT    'FORMAT'
           EVEN
;==[ PWRLNK ]==================================================================
PWRLNK     DATA    >0000             power-up chain (no more)
           DATA    PWRUP             address of power-up routine
           DATA    >0000             no name

DSRLNK     DATA    DSR1              DSR chain
           DATA    DSK               address of DSR
           BYTE    >03               name size
           TEXT    'DSK'             DSR name
DSR1       DATA    DSR2
           DATA    DSK1
H04        BYTE    >04
           TEXT    'DSK1'
           EVEN
DSR2       DATA    DSR3
           DATA    DSK2
           BYTE    >04
           TEXT    'DSK2'
           EVEN
H00
DSR3       DATA    >0000             no more
           DATA    DSK3
           BYTE    >04
           TEXT    'DSK3'

MSG        TEXT    'NANOPEB-SIO V1'
MSGL       EQU     $-MSG
           EVEN

;==[ POWER-UP ROUTINE ]========================================================
PWRUP      MOV     R11,R7                  ;SAVE RETURN ADDRESS
           LI      R2,13*32+9
           BL      @LVWAR2                 ;SHOW "INSTALLED" MESSAGE
           LI      R0,MSG
           LI      R1,MSGL
PWRUP05    MOVB    *R0+,@VDPWD
           DEC     R1
           JNE     PWRUP05
           MOV     @VHP^,R2                ;HIGHEST FREE ADDRESS IN VDP MEM (>8370)
           MOV     R2,R0                   ;SAVE IT
           AI      R2,-2096                ;WE'LL NEED VRAM
           MOV     R2,@VHP^                ;UPDATE ADDRESS
           INC     R2
           BL      @LVWAR2                 ;SET VDP ADDRESS
           LI      R1,2096 
PWRUP10    CLR     @VDPWD                  ;CLEAR THIS AREA OF VRAM
           DEC     R1
           JNE     PWRUP10
           BL      @LVWAR2                 ;SET VDP ADDRESS (SAME)
           LI      R2,C.CLR8BIT*256+C.SETFEAT
           LI      R11,VDPWD
           MOVB    @HAA,*R11               ;WRITE BUFFER ID MARK
           MOVB    R2,@C.FEATW
           SWPB    R2
           MOVB    R0,*R11                 ;WRITE OLD HIGHEST FREE ADDRESS
           SWPB    R0
           MOVB    R0,*R11      
           MOVB    R2,@C.CMD
           MOVB    R12,*R11                ;WRITE CRU BASE OF DISK CONTROLLER (1 BYTE)
           MOVB    @H03,*R11               ;WRITE NUMBER OF FILES (3)
           BL      @RESET           
           BL      @INIT2                  ;PREPARATION ROUTINE; ALSO DELAY FOR IDE RESET
           CLR     @PAB^                   ;NAME LENGTH = 0 FOR POWER-UP
           CALL    RDDIB                   ;READ DIB (EMBEDDED IN HD#1,vOL#1'S VIB)
           CALL    SVDIB                   ;LOAD
           CALL    WRDIB                   ;WRITE DIB IN CASE DEFAULT WAS USED
           VPOP    $R11                    ;RETRIEVE R7 (SAVED BY INIT2) & PUT IT INTO R11
           MOV     @H0404,@>836C           ;???
PWRRTN     RT

H0404      DATA    >0404
D1600      DATA    1600                    ;SIZE OF VIRTUAL DISK
;-------------------------------------------------------
; FUNCTION:     LVWAR2
; DESCRIPTION:  SET VDP ADDRESS TO WRITE
; CALLED BY:    SEC$RW,FMT
;-------------------------------------------------------
LVWAR2     ORI     R2,>4000
           ANDI    R2,>7FFF                ;NOT TO A VDP REGISTER
           JMP     $4622
;--------------------------------------------------
; FUNCTION:     LVRAR2
; DESCRIPTION:  SET VDP ADDRESS TO READ
; CALLED BY:    SEC$RW,FMT
;--------------------------------------------------
LVRAR2     ANDI    R2,>3FFF
$4622      SWPB    R2
           MOVB    R2,@VDPWA               ;SET ADDRESS
           SWPB    R2
           MOVB    R2,@VDPWA
           ANDI    R2,>3FFF
           RT
;---------------------------------------
; 
; -- V C A L L --
;
; call subroutine, return address in stack
;
;---------------------------------------
VCALL      DECT    @STK^
           MOV     @STK^,R10
           LVWA    R10
           MOV     *R11+,R10               ;GET NEXT DATA WORD
           SWPB    R11                     ;WRITE RETURN ADDRESS TO VDP
           MOVB    R11,@VDPWD
           SWPB    R11
           MOVB    R11,@VDPWD
           B       *R10                    ;BRANCH TO ADDRESS IN DATA WORD
;---------------------------------------
; get return address from VDP stack
;---------------------------------------
VRETN      MOV     @STK^,R11
           LVRA    R11                     ;SET VDP TO READ FROM ADDRESS IN R11
           MOVB    @VDPRD,R11
           SWPB    R11
           MOVB    @VDPRD,R11
           INCT    @STK^
           MOV     R11,R11
           RT

*---------------------------------------
* Custom routines, dealing with VDP memory
* Format is >pppr where r is routine number (0-3)
* and ppp contains parameters
* The workspace it >8300. Called by BLWP @>835A.
*---------------------------------------
CUSTM      MOV     R13,R10                 ;SAVE WREGS PTR
           MOV     *R14+,R8                ;GET NEXT DATA WORD
           MOV     R8,R9                   ;SAVE IT
           SRL     R9,4                    ;KEEP PARAMETER
           ANDI    R8,>0003                ;FOUR POSSIBLE OPERATIONS
           A       R8,R8                   ;MAKE IT A WORD PTR
           MOV     @>001E(R13),R11         ;GET OLD R15, I.E. VDP PORT (>8C02)
           MOV     @CJTBL(R8),R8           ;GET VECTOR FOR OPERATION
           B       *R8                     ;BRANCH TO IT

CJTBL      DATA    $VPUSH                  ;SAVE REGISTERS TO VDP STACK
           DATA    $VPOP                   ;RETRIEVE REGISTERS
           DATA    $LVRA                   ;SET VDP ADDRESS TO READ
           DATA    $LVWA                   ;SET VDP ADDRESS TO WRITE
;---------------------------------------
; 0: save registers in VDP mem
;---------------------------------------
$VPUSH     AI      R10,>0016
$46B4      SRL     R9,1                    ;START WITH R12
           JNC     $46D8                   ;BITS IN >PPP TELL WHICH REGISTER TO SAVE (R0-R11)
           DECT    R12                     ;PREVIOUS ADDRESS IN VDP STACK (GROWS DOWN)
           MOV     *R10,R8                 ;GET REGISTER CONTENTS
           SWPB    R12                     ;SET VDP ADDRESS TO WRITE
           MOVB    R12,@VDPWA              ;R12 IS >8366: VDP STACK PTR
           SWPB    R12
           ORI     R12,>4000
           MOVB    R12,@VDPWA
           SWPB    R8                      ;SAVE REGISTER TO VDP MEM
           MOVB    R8,@VDPWD     
           SWPB    R8
           MOVB    R8,@VDPWD     
           MOV     R9,R9                   ;MORE TO COME?
           JEQ     $4706                   ;NO:RETURN
$46D8      DECT    R10                     ;POINT TO PREVIOUS REGISTER
           JMP     $46B4
;---------------------------------------
; 1: retrieve registers from VDP mem
;---------------------------------------
$VPOP      SLA     R9,4
$46DE      SLA     R9,1                    ;EACH BIT TELLS WHETHER TO LOAD
           JNC     $4702                   ;DON'T RETRIEVE THAT ONE
           SWPB    R12                     ;SET VDP ADDRESS TO READ
           MOVB    R12,@VDPWA              ;R12 IS >8366: VDP STACK PTR
           SWPB    R12
           ANDI    R12,>3FFF
           MOVB    R12,@VDPWA
           NOP
           MOVB    @VDPRD,R8               ;GET A 2-BYTE VALUE FROM VDP
           SWPB    R8
           MOVB    @VDPRD,R8
           MOV     R8,*R10                 ;SAVE IT TO REGISTER
           INCT    R12                     ;INCREMENT PTR (STACK GROWS DOWNWARDS)
           MOV     R9,R9                   ;MORE TO COME?
           JEQ     $4706                   ;NO: RETURN
$4702      INCT    R10                     ;NEXT REGISTER
           JMP     $46DE
$4706      RTWP
;---------------------------------------
; 2: set VDP for a write
;---------------------------------------
$LVWA      A       R13,R9
           MOV     *R9,R8
           ORI     R8,>4000                ;GET ADDRESS FROM REG IN >..P2 (*2)
           JMP     $471A
;---------------------------------------
; 3: set VDP for a read
;---------------------------------------
$LVRA      A       R13,R9
           MOV     *R9,R8
           ANDI    R8,>3FFF                ;GET REGISTER FROM REG IN >..P3 (*2)
$471A      SWPB    R8                      ;SET VDP ADDRESS
           MOVB    R8,@VDPWA
           SWPB    R8
           MOVB    R8,@VDPWA
           RTWP
;---------------------------------------
; PREPARATION SUBROUTINE
; SETS UP THE 4 CUSTOM SUBROUTINES
; GETS A FEW POINTERS TO VDP BUFFERS
;  >8358: COPY OF VIB  >8366: VDP STACK PTR (GROWS DOWN FROM DRIVE INFO)
;  >8354: PAB          >8356: PTR TO END-OF-BUFFER
;---------------------------------------
INIT       INCT    R7                      ;STOP SCANNING UPON RETURN
INIT2      MOV     R11,R10                 ;SAVE RETURN ADDRESS
           LI      R0,CUSTM                ;ENTRY TO 4 CUSTOM ROUTINES
           MOV     R0,@>835C               ;PUT IT IN >835C
           LI      R0,>834E                ;WORKSPACE FOR THESE FOUR (>834E)
           LI      R9,>835A
           MOV     R0,*R9                  ;PUT IT IN >835A
           MOV     @VHIMEM,R8              ;HIGHEST FREE ADDRESS IN VDP MEM
$4744      INCT    R8                      ;POINT TO "END-OF-BUFFER" WORD
           BL      @LV$R8                  ;READ 2 BYTES FROM VDP ADDRESS R8, INTO R0
           MOV     R8,R2                   ;SAVE CURRENT R8
           MOV     R0,R8                   ;GET END-OF-BUFFER WORD
           MOVB    @VDPRD,R1               ;GET CRU OF CONTROLLER THAT RESERVED THIS MEM
           CB      R12,R1                  ;SAME AS OURS?
           JNE     $4744                   ;NO: USE END-OF-BUFFER TO LINK TO NEXT BUFFER
           AI      R8,-274                 ;YES: POINT TO VOLUME INFORMATION BLOCK
           MOV     R8,@VIB^                ;SAVE IT IN >8358
           AI      R8,-10                  ;POINT TO DISK DRIVE INFO (DRIVE #, LAST TRACKS)
           MOV     R8,@STK^                ;SAVE IN >8366: VDP STACK PTR (DECT BEFORE WRITING)
           VPSH    $R7                     ;SAVE R7 (RETURN ADDRESS)
           MOV     @FDR^,R7                ;PTR TO PAB: END OF DSR NAME
           MOV     R7,R3                   ;SAVE IT
           S       @PAB^,R7                ;BEG OF DSR NAME
           MOV     R2,@FDR^                ;>8356: PTR TO "END-OF-BUFFER" WORD IN VDP MEM
           DEC     R7                      ;POINT TO NAME LENGTH BYTE
           CLR     R2
           LVRA    R7
           MOVB    @VDPRD,R2               ;GET NAME LENGTH BYTE
           SWPB    R2                      ;MAKE IT A WORD
           S       @PAB^,R2                ;MINUS DSR NAME SIZE: LENGHT OF .PARAMETERS
           AI      R7,-9                   ;POINT TO TOP OF PAB
           MOV     R7,@PAB^                ;SAVE IT IN >8354
           B       *R10
;------------------------------------------------------------------
; FUNCTION:     CREATE
; DESCRIPTION:  create file
; CALLED BY:    OPEN,SAVE
; CALLS:        FNFDR2,FRSEVB,INFDR1,FSCVIB,UPDERR,RWBUF1,RWSEC$R7
;------------------------------------------------------------------
CREATE     CALL    FNFDR2                  ;FIND A FDR IN DISK
;------------------------------------------------------------------
; FUNCTION:     CREAT2
; DESCRIPTION:  create file
; CALLED BY:    RAWWR
; CALLS:        FRSEVB,INFDR1,FSCVIB,UPDERR,RWBUF1,RWSEC$R7
;------------------------------------------------------------------
CREAT2     MOV     R4,R4                   ;FOUND?
           JNE     CREAT3                  ;NO
           CALL    FRSEVB                  ;YES: DELETE OLD FILE & FREE FILE SECTOR IN VIB BITMAP
           JMP     $4834                   ;CLEAR ITS FDR
;------------------------------------------------------------------
; FUNCTION:     CREAT3
; DESCRIPTION:  create file
; CALLED BY:    OPEN,CREAT2(jmp)
; CALLS:        INFDR1,FSCVIB,UPDERR,RWBUF1,RWSEC$R7
;------------------------------------------------------------------
CREAT3     BL      @INFDR1                 ;NEW FILE: INSERT A FDR IN SECTOR 1
           SETO    R1
           CALL    FSCVIB                  ;FIND A FREE SECTOR IN VIB BITMAP
           MOV     R0,R0                   ;FOUND?
           JNE     $47C0                   ;YES: # IN R0
           BL      @UPDERR                 ;NO: UPDATE DATA, THEN RETURN WITH ERROR
           DATA    >8000                   ;"MEMORY FULL"
$47C0      LVWA    R8
           MOVB    R0,@VDPWD               ;WRITE SECTOR # FOR FDR IN SECTOR 1
           SWPB    R0
           MOVB    R0,@VDPWD     
           SWPB    R0
           MOV     @FDR^,R1                ;PTR TO FDR IN VDP BUFFERS
           AI      R1,-4                   ;PTR TO SECTOR # FOR FDR
           LVWA    R1
           MOVB    R0,@VDPWD               ;WRITE SECTOR # FOR FDR IN VDP BUFFER
           SWPB    R0
           MOVB    R0,@VDPWD     
           AI      R1,3                    ;PTR TO DRIVE #
           LVWA    R1
           MOVB    R6,@VDPWD               ;WRITE DRIVE # BEFORE FDR IN VDP BUFFER
           CLR     R2                      ;CODE FOR WRITE
           INC     R1
           CALL    RWBUF1                  ;WRITE SECTOR 1
           MOV     @VIB^,R5                ;PTR TO VIB IN VDP BUFFERS
           CLR     R4                      ;SECTOR 0
           CALL    RWSEC$R7                ;WRITE VIB TO SECTOR 0
           MOV     R5,R3                   ;COPY FILENAME FROM COMPARE BUFFER TO FDR
           AI      R3,257                  ;PTR TO FILE COMPARE BUFFER
           LI      R2,10                   ;10 CHARS PER FILENAME
$4818      LVRA    R3
           MOVB    @VDPRD,R0               ;GET 1 CHAR
           INC     R3                      ;INCREMENT SOURCE PTR
           LVWA    R1
           MOVB    R0,@VDPWD               ;COPY 1 CHAR
           INC     R1                      ;INCREMENT DESTINATION PTR
           DEC     R2                      ;# OF CHARS IN R2
           JNE     $4818                   ;NEXT
$4834      MOV     @FDR^,R1                ;FDR PTR
           AI      R1,10                   ;SKIP FILENAME
           LI      R2,246                  ;REMAINING BYTES IN FDR
           LVWA    R1
$4846      MOVB    R2,@VDPWD               ;WRITE >00
           DEC     R2                      ;CLEAR REMAINDER OF NEW FDR
           JNE     $4846                   ;NEXT BYTE
           JMP     $4870                   ;WRITE FDR TO DISK RETURN TO CALLER
;-----------------------------
; FUNCTION:     UPFDR?
; DESCRIPTION:  check if FDR must be updated
; CALLED BY:    UPFDRD
; CALLS:        RWFDR(jmp)
;-----------------------------
UPFDR?     MOV     @FDR^,R5                ;CHECK IF FDR MUST BE UPDATED
           LVRA    R5
           MOVB    @VDPRD,R4               ;GET FILENAME IN FILE CTRL BLOCK
           JLT     $4862                   ;FLAG: FDR WAS MODIFIED, UPDATE IT
           JMP     $4876
$4862      ANDI    R4,>7F00                ;CLEAR FLAG BIT
           LVWA    R5
           MOVB    R4,@VDPWD               ;WRITE BACK FILENAME WITHOUT FLAG BIT
$4870      CLR     R2                      ;CODE FOR WRITE
           B       @RWFDR                  ;WRITE FDR TO DISK
$4876      B       @VRETN                  ;RETURN TO CALLER
;---------------------------------------
; FUNCTION:     UPFDRD
; DESCRIPTION:  update FDR + data
; CALLED BY:    UPDERR,CLOSE,LOAD,SAVE,PROT,RAWRD
; CALLS:        UPBFR,UPFDR?,RDVIB$R6(jmp)
;---------------------------------------
UPFDRD     CALL    UPBFR                   ;UPDATE DATA BUFFER IF NECESSARY
           CALL    UPFDR?                  ;UPDATE FDR IF NECESSARY
$4886      MOV     @FDR^,R5
           CLR     R6
           LVWA    R5
           MOVB    R6,@VDPWD               ;CLR FIRST BYTE OF FILENAME IN FDR
           B       @RDVIB$R6
;-------------------------------------
; FUNCTION:     UPBFR
; DESCRIPTION:  check if data buffer must be updated
; CALLED BY:    LDSEC,WRITE,RDREC,REWND,UPFDRD
; CALLS:        WRSEC$OFS(jmp)
;-------------------------------------
UPBFR      MOV     @FDR^,R4
           DEC     R4
           LVRA    R4
           MOVB    @VDPRD,R1               ;GET DRIVE #
           JLT     $48AE                   ;FLAG: MUST BE UPDATED
           JMP     $48DA                   ;NO NEED TO UPDATE: RETURN
$48AE      ANDI    R1,>7F00                ;ERASE FLAG
           LVWA    R4
           MOVB    R1,@VDPWD               ;WRITE BACK DRIVE # WITHOUT FLAG
           AI      R4,>FFFB                ;POINT TO CURRENT LOGICAL RECORD OFFSET
           LVRA    R4
           MOVB    @VDPRD,R3               ;GET CURRENT LOGICAL RECORD OFFSET
           SWPB    R3
           MOVB    @VDPRD,R3
           SWPB    R3
           MOV     R4,R7
           AI      R7,>0106                ;POINT TO DATA BUFFER AREA
           JMP     WRSEC$OFS
$48DA      B       @VRETN                  ;RETURN TO CALLER
;----------------------------------------------
; FUNCTION:     FRSEVB
; DESCRIPTION:  free file sectors in VIB bitmap
; CALLED BY:    DELETE,CREATE,CREAT2
; CALLS:        UPDERR,DECODE,FRESE2
;----------------------------------------------
FRSEVB     MOV     @FDR^,R2
           AI      R2,>000C
           LVRA    R2
           MOVB    @VDPRD,R0               ;GET FILE STATUS BYTE FROM FDR
           ANDI    R0,>0800                ;WRITE PROTECTED?
           JEQ     $48FC                   ;NO
           BL      @UPDERR                 ;UPDATE DATA IF NEEDED, THE RETURN WITH ERROR
           DATA    >2000                   ;"WRITE PROTECTED"
$48FC      MOV     @FDR^,R8                ;GET PTR TO FDR
           AI      R8,>001C                ;POINT TO CLUSTERS LIST
           SETO    R4                      ;PREVIOUS OFFSET
$4906      BL      @DECODE                 ;DECODE CLUSTER INFO: SECTOR IN R1, OFFSET IN R2
           AI      R8,>0003                ;NEXT CLUSTER PTR
           MOV     R1,R1
           JEQ     $4406                   ;SECTOR 0 = NO MORE CLUSTERS
           MOV     R2,R0                   ;TOTAL OFFSET
           S       R4,R2                   ;MINUS PREVIOUS CLUSTER OFFSET = CLUSTER SIZE
           MOV     R0,R4                   ;NEW "PREVIOUS CLUSTER OFFSET"
           CALL    FRESE2                  ;FREE SECTORS IN BITMAP (FROM R1, # IN R2)
           MOV     R8,R1
           S       @FDR^,R1
           CI      R1,>0100                ;END OF FDR REACHED?
           JNE     $4906                   ;NOT YET
$4406      B       @VRETN                  ;RETURN TO CALLER
;------------------------------------------------
; FUNCTION:     RDSEC$OFS
; DESCRIPTION:  read a sector from offset in file
; CALLED BY:    OPEN,LDSEC,RDSEC,LOAD,RAWRD
; CALLS:        LV$FDR,UPDERR,FNSECL,RWSEC$R7(jmp)
;------------------------------------------------
RDSEC$OFS  BL      @LV$FDR
           DATA    14                      ;GET # OF SECTORS
           C       R0,R3                   ;IS DESIRED SECTOR IN FILE?
           JH      $493E                   ;YES --\
           BL      @UPDERR                 ;NO: UPDATE DATA, THEN RETURN WITH ERROR
           DATA    >A000                   ;"PAST END-OF-FILE"
$493E      CALL    FNSECL                  ;FIND SECTOR FROM CLUSTER LIST
           A       R1,R4                   ;PUT SECTOR IN R4
           SETO    R2                      ;CODE FOR READ
           JMP     $4948                   ;WRITE SECTOR --\
;----------------------------------------------------
; FUNCTION:     WRSEC$OFS
; DESCRIPTION:  write sector to offset in file offset in R3
; CALLED BY:    SAVE,RAWWR,UPBFR
; CALLS:        APNSEC,RWSEC$R7(jmp)
;----------------------------------------------------
WRSEC$OFS  VPSH    $R0+$R7                 ;SAVE R0 AND R7
           CALL    APNSEC                  ;APPEND ENOUGH SECTORS TO REACH THAT OFFSET
           VPOP    $R0+$R7                 ;RETRIEVE R0 AND R7
           CLR     R2                      ;CODE FOR WRITE
$4948      MOV     R7,R5                   ;BUFFER PTR
           B       @RWSEC$R7               ;READ/WRITE SECTOR
;-------------------------------------------------------------
; FUNCTION:     APNSEC
; DESCRIPTION:  append sector(s) to a file to get offset in R3
; CALLED BY:    OPEN,WRITE,RAWWR,WRSEC$OFS
; CALLS:        LV$FDR,FNSECL,FSCVIB,AD2FDR,WRCLUS,FRESEC
;-------------------------------------------------------------
APNSEC     BL      @LV$FDR
           DATA    14                      ;GET # OF SECT FROM FDR
$496A      C       R0,R3                   ;IS DESIRED SECTOR IN FILE?
           JH      $49FC                   ;YES: FIND IT AND RETURN
           MOV     R0,R0                   
           JEQ     $4974                   ;EMPTY FILE
           DEC     R0                      ;LAST SECTOR #
$4974      MOV     R3,R5                   ;SAVE DESIRED SECTOR OFFSET
           MOV     R0,R3                   ;LAST SECTOR IN FILE
           CALL    FNSECL                  ;FIND SECTOR # FROM OFFSET IN FILE
           VPSH    $R1                     ;SAVE R1 (SECTOR #)
           A       R4,R1                   ;# OF LAST SECTOR IN CLUSTER
           MOV     R5,R4                   ;OFFSET OF DESIRED SECTOR
$4988      CALL    FSCVIB                  ;FIND FREE SECTOR IN BITMAP,  STARTING FROM R1
           MOV     R0,R0                   ;FOUND?
           JNE     $499A                   ;YES: # IN R0
           MOV     R2,R5                   ;NO: SAVE # OF SECTORS
           BL      @AD2FDR                 ;UPDATE FDR
           JMP     $49D0                   ;RETURN WITH "MEMORY FULL"
$499A      MOV     R1,R1
           JEQ     $49DE                   ;EMPTY CLUSTER
           INC     R1                      ;ONE MORE SECTOR IN CLUSTER
           C       R0,R1                   ;SAME AS THE ONE FOUND IN BITMAP?
           JEQ     $49DC                   ;YES: JUST INCREASE CLUSTER SIZE
           VPOP    $R1                     ;NO: RETRIEVE R1 (SECTOR #)
           VPSH    $R0+$R2                 ;SAVE R0 + R2
           BL      @WRCLUS                 ;UPDATE CURRENT CLUSTER INFO
           VPOP    $R0+$R2                 ;RETRIEVE R0 + R2
           AI      R8,3                    ;PTR TO NEXT CLUSTER INFO
           MOV     R8,R1
           S       @FDR^,R1                ;CURRENT FDR SIZE
           CI      R1,>0100                ;ROOM ENOUGH?
           JNE     $49D4                   ;YES
           CALL    FRESEC                  ;NO: FREE SECTOR IN BITMAP
$49D0      B       @ERR80                  ;RETURN WITH "MEMORY FULL"
$49D4      MOV     R0,R1                   ;NEW SECTOR #
           VPSH    $R1                     ;SAVE R1
$49DC      JMP     $49EC                   ;MAKE A NEW CLUSTER
$49DE      VPOP    $R1                     ;RETRIEVE R1
           VPSH    $R0                     ;SAVE R0
           SETO    R2                      ;EMPTY FILE: START FROM OFFSET 0
$49EC      MOV     R0,R1                   ;SECTOR #
           INC     R2                      ;INCREMENT CLUSTER SIZE
           C       R2,R4                   ;ENOUGH?
           JNE     $4988                   ;NO: GET ONE MORE SECTOR
           MOV     R4,R5
           BL      @AD2FDR                 ;ADD SECTOR TO FDR
           JMP     $4A04                   ;DONE
$49FC      CALL    FNSECL                  ;FIND A SECTOR FROM OFFSET IN FILE
           A       R1,R4                   ;LAST SECTOR IN CLUSTER
$4A04      B       @VRETN                  ;RETURN TO CALLER
;----------------------------------------------------
; FUNCTION:     AD2FDR
; DESCRIPTION:  ADD SECTOR TO FDR
; CALLED BY:    APNSEC
; CALLS:        WRCLUS
;----------------------------------------------------
AD2FDR     MOV     R11,R10
           MOV     R1,R4
           VPOP    $R1                     ;RETRIEVE R1
           MOV     R1,R1                   ;SECTOR #
           JEQ     $4A4C                   ;NONE: RETURN
           BL      @WRCLUS                 ;WRITE INFO FOR 1 CLUSTER
           MOV     @FDR^,R2                ;POINTER TO FDR
           LVRA    R2
           MOVB    @VDPRD,R1               ;FIRST CHAR OF FILENAME
           ORI     R1,>8000                ;FLAG
           LVWA    R2
           MOVB    R1,@VDPWD
           AI      R2,14                   ;PTR TO # OF SECTORS
           INC     R5                      ;NEW # OF SECTORS
           LVWA    R2
           MOVB    R5,@VDPWD               ;UPDATE # OF SECTORS
           SWPB    R5
           MOVB    R5,@VDPWD
$4A4C      B       *R10
;----------------------------------------------------
; FUNCTION:     FNSECL
; DESCRIPTION:  find sector from offset in file
; CALLED BY:    RDSEC$OFS,APNSEC
; CALLS:        DECODE
;----------------------------------------------------
FNSECL     MOV     @FDR^,R8
           AI      R8,>001C                ;CLUSTER LIST
           MOV     R3,R4                   ;DESIRED SECTOR OFFSET IS IN R3
$4A58      BL      @DECODE                 ;DECODE INFO: SECTOR IN R1, OFFSET IN R2
           C       R2,R3                   ;REACHED YET?
           JLT     $4A62                   ;NO
           JMP     $4530                   ;YES: RETURN
$4A62      AI      R8,3                    ;POINT TO NEXT CLUSTER INFO
           MOV     R3,R4
           S       R2,R4
           DEC     R4                      ;SIZE OF THE DESIRED CLUSTER (IF IT'S THE NEXT)
           JMP     $4A58                   ;KEEP TRYING
$4530      B       @VRETN                  ;RETURN TO CALLER
;---------------------------------------------------------------------------------------------
; FUNCTION:     DELETE
; DESCRIPTION:  Opcode 7: Delete
;               ----------------
;               PAB 0: >07
;               1:            <--- error code
;             2-3:
;               4:
;               5:
;             6-7:
;               8:
; CALLS:        FNDFIL,FNFDR3,RDBUF1,LV$FDR,LV$R8,FRSEVB,RMFDR,RWBUF1,LV$FDR,FRESEC,$4886(jmp)
;---------------------------------------------------------------------------------------------
DELETE     CALL    FNDFIL                  ;FIND FILE FDR IN VDP BUFFERS
           MOV     4,4                     ;FOUND?
           JEQ     $4A8A                   ;YES
           CALL    FNFDR3                  ;NO: FIND FDR ON DISK
           MOV     4,4                     ;FOUND?
           JEQ     $4AAC                   ;YES
           B       @VRETN                  ;NO: RETURN TO CALLER
$4A8A      INC     1                       ;FDR FOUND, POINT TO IT
           MOV     1,@FDR^                 ;SAVE IT FOR OTHER ROUTINES
           CALL    RDBUF1                  ;READ SECTOR 1
           BL      @LV$FDR                 ;READ 2 BYTES FROM VDP AT >8356+OFFSET
           DATA    >FFFC                   ;I.E. GET SECTOR # OF FRD IN R0
           MOV     0,3                     ;SAVE IT FOR LATER
           MOV     5,8                     ;PTR TO TOP OF SECTOR 1
$4AA0      BL      @LV$R8                  ;READ 2 BYTES FROM VDP AT R8 INTO R0
           INCT    8                       ;NEXT FDR PTR IN SECTOR 1
           C       0,3                     ;IS THIS THE ONE WE WANT?
           JNE     $4AA0                   ;NOT YET
           DECT    8                       ;YES: POINT BACK TO IT
$4AAC      VPSH    $R8                     ;SAVE R8
           CALL    FRSEVB                  ;FREE FILE SECTORS IN VIB BITMAP
           VPOP    $R8                     ;RETRIEVE R8
           BL      @RMFDR                  ;REMOVE FDR PTR FROM SECTOR 1
           CLR     2                       ;CODE FOR WRITE
           CALL    RWBUF1                  ;WRITE SECTOR 1
           BL      @LV$FDR                 ;GET 2 BYTES FROM VDP AT >8356+OFFSET IN R0
           DATA    >FFFC                   ;I.E. SECTOR # OF FDR
           CALL    FRESEC                  ;FREE SECTOR IN R0 IN VIB BITMAP
           B       @$4886                  ;CLEAR 1ST BYTE OF FILENAME IN VDP
;---------------------------------------------------------------------------------------
; FUNCTION:     RMFDR
; DESCRIPTION:  Sector 1 contains an alphabetically sorted list of FDR pointers
;               Each pointer is 2 bytes long and contains the sector # of this FDR
;               The list must end with a >0000 mark. Thus there can be 127 files at most
; CALLED BY:    RENAME,DELETE
;---------------------------------------------------------------------------------------
RMFDR      MOV     @FDR^,R5                ;REMOVE FDR PTR FROM SECTOR 1
           AI      R5,256  
           MOV     R5,R3                   ;POINTER TO DATA BUFFER
           AI      R3,256                  ;POINT TO NEXT CTRL BLOCK
           MOV     R8,R2                   ;SHIFT UP FDR LIST, ERASING FDR POINTED BY R8
           INCT    R2                      ;POINT TO NEXT FDR PTR IN SECTOR 1
$4AEC      LVRA    R2
           MOVB    @VDPRD,R0               ;GET 1 BYTE
           INC     R2                      ;INC SOURCE PTR
           LVWA    R8
           MOVB    R0,@VDPWD               ;COPY 1 BYTE
           INC     R8                      ;INC DEST POINTER
           C       R2,R3                   ;DONE?
           JNE     $4AEC                   ;NOT YET
           RT
;----------------------------------------------------
; FUNCTION:     INFDR1
; DESCRIPTION:  Insert a FDR in sector 1
; CALLED BY:    RENAME,CREATE,CREAT2,CREAT3
; CALLS:        LV$FDR,ERR80(jmp)
;----------------------------------------------------
INFDR1     MOV     R11,R10
           MOV     R8,R1
           BL      @LV$FDR                 ;GET 2 BYTES FROM VDP AT >8356+OFFSET
           DATA    >01FC                   ;I.E. LAST FDR SLOT IN SECTOR 1 (LIST ENDS WITH 0)
           MOV     R0,R0
           JEQ     $4B1C
           B       @ERR80                  ;"MEMORY FULL" ERROR
$4B1C      DEC     R8                      ;PREVIOUS BYTE
           LVRA    R8
           MOVB    @VDPRD,R0               ;GET 1 BYTE
           INCT    R8
           LVWA    R8
           MOVB    R0,@VDPWD               ;COPY BYTE TWO BYTES FURTHER DOWN
           DECT    R8
           C       R8,R1                   ;DONE ?
           JNE     $4B1C                   ;NOT YET
           B       *R10
;----------------------------------------------------
; FUNCTION:     CMPNM
; DESCRIPTION:  compare filenames
; CALLED BY:    FNFDRV,FNFDR2,FNFDRD,FDRFFL
;----------------------------------------------------
CMPNM      MOV     @VIB^,R2
           AI      R2,256
$4B44      LVRA    R1                      ;(PTR TO FILENAME)
           MOVB    @VDPRD,R0               ;GET 1 CHAR
           ANDI    R0,>7FFF                ;CLEAR FLAG (ON DRIVE #)
           LVRA    R2                      ;(FILENAME COMPARE BUFFER)
           MOVB    @VDPRD,R3               ;GET 1 CHAR
           CB      R0,R3                   ;COMPARE CHARS
           JNE     $4B68                   ;MISMATCH
           INC     R1                      ;MATCH: INCREMENT POINTERS
           INC     R2
           DEC     R4
           JNE     $4B44                   ;NEXT CHAR
$4B68      RT                              ;IF COMPLETE MATCH: RET WITH EQ SET
;----------------------------------------------------
; FUNCTION:     LV$PAB
; DESCRIPTION:  get two bytes from PAB
; CALLED BY:    OPEN,OPNDIR,RDDIR
;----------------------------------------------------
LV$PAB     MOV     @PAB^,R8
           JMP     LV$R8O
;------------------------------------------------------------------------
; FUNCTION:     LV$FDR
; DESCRIPTION:  get two bytes from FDR
; CALLED BY:    LOAD,RDDIR,PROT,RENAME,FNFDRF,RDSEC$OFS,APNSEC,DELETE,INFDR1
;------------------------------------------------------------------------
LV$FDR     MOV     @FDR^,R8
;------------------------------------------------------------------------
; FUNCTION:     GTFDR9
; DESCRIPTION:  get two bytes from FDR from offset
; CALLED BY:    GTRCOF
;------------------------------------------------------------------------
LV$R8O     A       *R11+,R8                ;OFFSET IN DATA WORD
;------------------------------------------------------------------------
; FUNCTION:     LV$R8
; DESCRIPTION:  get two bytes from FDR from offset at call+1
; CALLED BY:    FDRFFL,DECODE,RDDIR,FDVFCB,FILES,$FILES,INIT,DELETE
;------------------------------------------------------------------------
LV$R8      LVRA    R8
           MOVB    @VDPRD,R0               ;GET TWO BYTES OF DATA
           SWPB    R0
           MOVB    @VDPRD,R0
           SRC     R0,8                    ;WHY NOT SWPB ?
           RT
;------------------------------------------------------------------
; FUNCTION:     FNDDRV
; DESCRIPTION:  Find disk from name (vdp memory ptr to name in R3).
; CALLS:        CPFNM,RDVIB$R6,RWSEC$R4,CMPNM
; CALLED BY:    DSK
;------------------------------------------------------------------
FNDDRV     MOV     @VIB^,R1
           AI      R1,256   
           BL      @CPFNM                  ;COPY FILENAME FROM VDP AT R3 TO COMPARE BUFFER
           VPSH    $R2+$R3                 ;SAVE R2 + R3
           MOV     R0,R0                   ;LAST CHAR COPIED (S/B DRIVE NUMBER)
           JLT     $4650                   ;FLAGGED
           JMP     $4BAC
$4650      MOV     R0,R6                   ;GET DRIVE # FOUND
           ANDI    R6,>0300                ;LAST DRIVE?
           JEQ     SCRE0                   ;RETURN WITH "FILE ERROR"
           JMP     $4C0A                   ;RETURN TO CALLER
$4BAC      CLR     R6                      ;DON'T READ ANY DRIVE
           CALL    RDVIB$R6                ;SAVE CURRENT VIB
$4BB4      AI      R6,256                  ;NEXT DRIVE
           SETO    R7                      ;ERROR FLAGS
           SETO    R2                      ;CODE FOR READ
           CLR     R4                      ;SECTOR 0
           CALL    RWSEC$R4                ;READ SECTOR 0
           MOV     R7,R7                   ;OK?
           JNE     $4C02                   ;NO: TRY NEXT DRIVE
           LI      R4,10                   ;YES: DISKNAME SIZE
           MOV     @VIB^,R1                ;VIB PTR
           VPSH    $R1+$R2+$R3             ;SAVE R1-R3
           BL      @CMPNM                  ;COMPARE NAMES (PTR IN R1 + COMPARE BUFFER)
           VPOP    $R1+$R2+$R3             
           MOV     R4,R4                   ;NAME MATCHED?
           JNE     $4C02                   ;NO: TRY NEXT DRIVE
           MOV     R6,R0                   ;DRIVE #
           ORI     R0,>8000                ;ADD "UPDATE" FLAG
           LVWA    R3
           MOVB    R0,@VDPWD               ;COPY DRIVE+FLAG BEFORE DISKNAME IN SOURCE
           DEC     R5                      ;BEFORE SECTOR 0 COPY
           LVWA    R5
           MOVB    R6,@VDPWD               ;COPY DRIVE # BEFORE VIB
           JMP     $4C0A                   ;DONE
$4C02      CI      R6,>0300                ;DID WE DO ALL DRIVES?
           JNE     $4BB4                   ;NO: TRY NEXT ON
           JMP     SCRE0                   ;RETURN WITH "FILE ERROR"
$4C0A      VPOP    $R2+$R3                 ;RETRIEVE R2 + R3
           B       @VRETN                  ;RETURN TO CALLER
;----------------------------------------------------
; FUNCTION:     CPFNM
; DESCRIPTION:  copy (+ check) filename from R3 to R1
; CALLED BY:    FNDFIL,FNDDRV
; CALLS:        SCRTCH
;----------------------------------------------------
CPFNM      DEC     R2
           JH      $4C1E
SCRE0      BL      @SCRTCH                 ;RETURN WITH ERROR
HE000      DATA    >E000                   ;"FILE ERROR"
$4C1E      LI      R0,>2000                ;COPY (+CHECK) FILENAME IN VDP FROM R3 TO R1
           LVWA    R1                      
           LI      R8,10                   ;10 CHARS PER FILENAME
$4C2C      MOVB    R0,@VDPWD               ;FILL FILENAME WITH SPACES
           DEC     R8
           JNE     $4C2C
           LI      R8,11                   ;11 CHARS COUNTRING DRIVE #
$4C38      INC     R3                      ;INCREMENT SOURCE PTR
           LVRA    R3
           MOVB    @VDPRD,R0               ;GET A CHAR
           JEQ     SCRE0                   ;>00: RETURN WITH "FILE ERROR"
           JLT     $4C68                   ;FLAG BIT SET
           CI      R0,'.'*256
           JEQ     $4C68                   ;'.'
           DEC     R8                      
           JEQ     SCRE0                   ;NAME TOO LONG: RETURN WITH "FILE ERROR"
           CI      R0,' '*256
           JEQ     SCRE0                   ;NAME CAN'T CONTAIN SPACES: "FILE ERROR" AGAIN
           LVWA    R1
           MOVB    R0,@VDPWD               ;COPY 1 CHAR
           INC     R1                      ;INCREMENT DESTINATION POINTER
           DEC     R2                      ;MORE?
           JNE     $4C38                   ;YES
$4C68      CI      R8,11                   ;NAME CAN'T BE 0 CHARS LONG
           JEQ     SCRE0                   ;ELSE RETURN WITH "FILE ERROR"
           RT

;-----------------------------------------------------------------------------------
; FUNCTION:     UPDERR
; DESCRIPTION:  update data, then error
; CALLED BY:    RWSECT,FNFDR2,FNFDRD,FNFDR3,DSK$X,OPEN,READ,FNFDR4,GTREC#,LOAD,
;               OPNDIR,RDDIR,RENAME,FNFDRF,CREATE,CREAT2,CREAT3,UPFDRD,FRSEVB,RDSEC$OFS
; CALLS:
;-----------------------------------------------------------------------------------
UPDERR     MOV     R11,R0
           MOV     @FDR^,R3
           DEC     R3                      ;SEE WHAT >8356 IS POINTING AT
           LVRA    R3
           MOVB    @VDPRD,R2               ;GET 1 BYTE
           CB      R2,@HAA                 ;IS IT >AA (DISK BUFFER AREA MARK)
           JEQ     $4C9C                   ;YES: ANNOUNCE ERROR (CODE IN DATA WORD)
           VPSH    $R0                     ;NO: >8356 POINTS TO A FDR. SAVE R0
           CALL    UPFDRD                  ;UPDATE FRD AND DATA BUFFER IF NEEDED
           VPOP    $R0                     ;RETRIEVE R0
$4C9C      MOV     R0,R11
SCRTCH     MOV     @PAB^,R1                ;ANNOUNCE ERROR
           JEQ     $4CBE                   ;NO PAB
           INC     R1                      ;FOR DSRS: PTR TO STATUS BYTE
           LVRA    R1
           MOVB    @VDPRD,R2               ;GET FILE STATUS BYTE
           SOC     *R11+,R2                ;ADD ERROR CODE
           LVWA    R1
           MOVB    R2,@VDPWD               ;WRITE BACK STATUS BYTE
           JMP     $4CC2
$4CBE      MOV     *R11+,@ERRFLG           ;FOR SUBS: ERROR CODE IN >8350
$4CC2      MOV     @VIB^,R8                ;GET STACK PTR
           AI      R8,-12                  ;POINT BACK TO INITIAL CALLER
           MOV     R8,@STK^                ;UPDATE POINTER
           B       @VRETN                  ;RETURN TO CALLER
;-------------------------------------------------------------------
;
; -- R D V I B $ R 6 --
;
;   READ VIB FOR DSK# SPECIFIED IN R6(HIGH). IF ALREADY THERE THEN 
;   RETURN, OTHERWISE SAVE VIB TO IT'S DSK.
;
;          MOVB    R6,@<DRV3>
;          CALL    RDVIB$R6                ;R4 & R5 DESTROYED
;
;-------------------------------------------------------------------
RDVIB$R6   VPSH    $R2+$R3                 ;SAVE R2 & R3
           MOV     @VIB^,R5                ;GET ADDRESS OF VIB
           DEC     R5                      ;POINT TO DSK# OF VIB
           LVRA    R5                      ;SET VRRAM ADDRESS FOR READ
           MOVB    @VDPRD,R2               ;GET DSK#
           MOV     R2,R3                   
           CLR     R4                      ;SET SECTOR 0 FOR VIB
           ANDI    R2,>0300                ;CLEAR FLAG (>80 = UPDATE DATA)
           CB      R2,R6                   ;VIB ALREADY IN VRAM?
           JEQ     $4D28                   ;YES: RETURN
           MOV     R3,R3                   ;NO: DOES VIB NEED TO BE WRITTEN?
           JLT     $4CFA                   ;YES
           JMP     $4D0E                   ;NO
$4CFA      LVWA    R5
           MOVB    R4,@VDPWD               ;CLEAR DSK#
           INC     R5                      ;POINT BACK TO VIB BUFFER
           CALL    RWSECT                  ;WRITE VIB TO SECTOR 0 OF OLD DRIVE (IN R2)
           DEC     R5                      ;POINT TO DSK#
$4D0E      SETO    R2                      ;SET FLAG FOR "READ-SECTOR"
           MOVB    R6,R2                   ;SET REQUESTED DSK#
           JEQ     $4D1E                   ;NONE SPECIFIED
           INC     R5                      ;POINT AT VIB BUFFER
           CALL    RWSEC$R4                ;READ VIB (SEC# IN R4) TO BUFFER (IN R5)
           DEC     R5                      ;POINT AT DRIVE #
$4D1E      LVWA    R5
           MOVB    R6,@VDPWD               ;WRITE DRIVE # IN CONTROL BLOCK, BEFORE VIB
$4D28      INC     R5                      ;POINT BACK TO VIB
           VPOP    $R2+$R3                 ;RESTORE R2 & R3
           B       @VRETN                  ;BRANCH TO ADDRESS ON STACK
;-------------------------------------------------------------------
;
; -- R D D I B  --
;
;   READ DIB. MUST CALL RDVIB$R6 WITH R6=0 TO FLUSH CURRENT VIB.   
;
;          CALL    RDDIB
;
;-------------------------------------------------------------------
RDDIB      CALL    SETUP                   ;PREP IDE INTERFACE
           BL      @CFCMD                  ;SEND COMMAND
           DATA    >2000                   ;"READ SECTOR"
           BL      @DRQ?                   ;WAIT FOR DRQ
           LVWA    R5
           LI      R1,256
RDDIB20    MOVB    @C.DATAR,@VDPWD
           DEC     R1
           JNE     RDDIB20 --/
RDDIBRET   B       @VRETN                  ;BRANCH TO ADDRESS ON STACK
;-------------------------------------------------------------------
;
; -- W R D I B  --
;
;   WRITE DIB. MUST CALL RDVIB$R6 WITH R6=0 TO FLUSH CURRENT VIB.   
;
;          CALL    WRDIB
;
;-------------------------------------------------------------------
WRDIB      CALL    SETUP
           BL      @CFCMD                  ;SEND COMMAND
           DATA    >3000                   ;"WRITE SECTOR"
           BL      @DRQ?                   ;WAIT FOR DRQ
           LVRA    R5                      ;SET VRAM ADDRESS FOR READ
           LI      R1,256
WRDIB20    MOVB    @VDPRD,R0
           MOVB    R0,@C.DATAW
           DEC     R1
           JNE     WRDIB20 --/
           JMP     RDDIBRET --/
;-------------------------------------------------------------------
;
; -- S E T U P  --
;
;   SETUP IDE PORT TO READ/WRITE SEC#0 ON HD#1
;
;          CALL    SETUP
;
;-------------------------------------------------------------------
SETUP      CLR     R1
           CLR     R2
           CLR     R3
           BL      @SETLBA!
           DATA    R2
           MOV     @VIB^,R5
           BL      @BUSY?
           JMP     RDDIBRET --/
           
;-------------------------------------------------------------------
;
; -- S V D I B  --
;
;   LOAD DIB INTO VIB. VIB FROM PHYSICAL VOL#1 ON HD#1 MUST IN
;   VIB BUFFER.
;
;          CALL    LVDIB
;
;-------------------------------------------------------------------
LVDIB      MOV     @VIB^,R0                ;R0 = VIB VRAM ADDRESS
           MOV     R0,R1                   ;R0 = VIB VRAM ADDRESS
           AI      R1,DIBS                 ;R1 = DIB VRAM ADDRESS
           AI      R0,>14                  ;R0 = DIB VRAM ADDRESS IN VIB
           JMP     SVDIB10 --\
;-------------------------------------------------------------------
;
; -- S V D I B  --
;
;   STORE VIB'S DIB INTO DIB. VIB FROM PHYSICAL VOL#1 ON HD#1 MUST IN
;   VIB BUFFER.
;
;          CALL    SVDIB                   
;
;   R4=MARKER FOUND IN VIB'S DIB
;
;-------------------------------------------------------------------
SVDIB      MOV     @VIB^,R0                ;R0 = VIB VRAM ADDRESS
           MOV     R0,R1                   ;R1 = VIB VRAM ADDRESS
           AI      R0,DIBS                 ;R0 = DIB VRAM ADDRESS
           AI      R1,>14                  ;R1 = DIB VRAM ADDRESS IN VIB
           LVRA    R1
           CB      @VDPRD,@HAA             ;VALIDATE MARKER
           JNE     SVDIB50 --\
           CB      @VDPRD,@H03
           JNE     SVDIB50 --\
SVDIB10    LI      R2,8
SVDIB20    LVRA    R1                      ;COPY DIB
           MOVB    @VDPRD,R3
           LVWA    R0
           MOVB    R3,@VDPWD
           INC     R0
           INC     R1
           DEC     R2
           JNE     SVDIB20 --/
           JMP     RDDIBRET --/
SVDIB50    LVWA    R0
           LI      R1,VOLTBL
           LI      R2,8
SVDIB60    MOVB    *R1+,@VDPWD
           DEC     R2
           JNE     SVDIB60 --/
           JMP     RDDIBRET --/
HAA        EQU     $
H03        EQU     $+1
VOLTBL     DATA    >AA03,1,2,3              ;DEFAULT DIB SETTING
;----------------------------------------------------
; FUNCTION:     RDBUF1
; DESCRIPTION:  read sector 1
;----------------------------------------------------
RDBUF1     SETO    R2                      ;SET READ CODE
;----------------------------------------------------
; FUNCTION:     RWBUF1
; DESCRIPTION:  write sector 1
;----------------------------------------------------
RWBUF1     LI      R4,1                    ;SET SECTOR 1
           LI      R5,256                  ;INTO DATA BUFFER AREA OF CURRENT CTRL BLOCK
           JMP     RWFDR$R5
;----------------------------------------------------
; FUNCTION:     RWFDR
; DESCRIPTION:  Write an FDR
; INPUT:        R4 = sector#
; CALLS:        LV$FDR
;----------------------------------------------------
RWFDR      BL      @LV$FDR
           DATA    -4                      ;GET SECTOR # OF FDR FROM CONTROL BLOCK
;----------------------------------------------------
; FUNCTION:     RWFDR$R5
; DESCRIPTION:  Read an FDR pointed to by R0
;----------------------------------------------------
RWFDR$R0   MOV     R0,R4                   ;SET SECTOR TO READ OR WRITE.
           CLR     R5
;----------------------------------------------------
; FUNCTION:     RWFDR$R5
; DESCRIPTION:  Read an FDR
; INPUT:        R4 = sector#
;----------------------------------------------------
RWFDR$R5   A       @FDR^,R5                ;POINT TO FDR OR DATA BUFFER
;----------------------------------------------------
; FUNCTION:     RWSEC$R7
; DESCRIPTION:  Write VIB referenced by R0
;----------------------------------------------------
RWSEC$R7   CLR     R7
;----------------------------------------------------
; FUNCTION:     RWSEC$R4
; DESCRIPTION:  Read sector (in R4) to buffer (in R5)
; CALLED BY:    RDVIB$R6,FNDDRV
;----------------------------------------------------
RWSEC$R4   MOVB    R6,R2                   ;ADD DRIVE # FOR R/W FLAG
;----------------------------------------------------
; FUNCTION:     RWSECT
;          THIS ROUTINE READS OR WRITES A SECTOR.
;
;          MOV     R2,<DSK#>+<R/W FLAG>
;          MOV     R4,<SEC#>
;          MOV     R5,<BUFFER>
;          CALL    RWSECT
;----------------------------------------------------
RWSECT     VPSH    $R0+$R1+$R2+$R3+$R4+$R5+$R6+$R7+$R8
           MOV     R4,@SEC#                ;SECTOR #
           MOV     R2,@DRV#                ;DRIVE | R/W FLAG
           MOV     R5,@BUF^                ;BUFFER PTR
           CALL    SEC$RW                  ;SECTOR READ/WRITE
           VPOP    $R0+$R1+$R2+$R3+$R4+$R5+$R6+$R7+$R8
           MOVB    @ERRFLG,R7              ;GET ERROR CODE | FLAGS
           SWPB    R7
           JEQ     $4D9C
           MOV     R7,R7
           JGT     $48D2
           JMP     $4D94
$48D2      ANDI    R7,>00FF
           CI      R7,>0034
           JNE     $4D8E
           BL      @UPDERR
           DATA    >2000                   ;"WRITE PROTECTED"
$4D8E      BL      @UPDERR
           DATA    >C000                   ;"DEVICE ERROR"
$4D94      MOVB    @IORQST,@IORQST         ;READ OR WRITE?
           JEQ     $48D2                   ;WRITE
$4D9C      ANDI    R7,>00FF
           B       @VRETN                  ;RETURN TO CALLER
;-------------------------------------------------
; FUNCTION:     FNDFIL
; DESCRIPTION:  Save filename in compare buffer and find its FDR
; CALLED BY:    FNFDR2,FNFDR4,STATUS,DELETE
; CALLS:        CPFNM,FNFDRV(jmp),SCRE0(jmp)
;-------------------------------------------------
FNDFIL     MOV     @VIB^,R1
           AI      R1,256
           LVWA    R1                      ;FILENAME COMPARE BUFFER
           MOVB    R6,@VDPWD               ;WRITE DRIVE #
           INC     R1
           BL      @CPFNM                  ;COPY (+ CHECK) FILENAME FROM R3 TO R1
           MOV     R2,R2                   ;ALL CHARS COPIED (OR WAS THERE A "." OR A >00 ?)
           JEQ     FNFDRV
$4DC0      B       @SCRE0                  ;RETURN WITH "FILE ERROR"
;-------------------------------
; FUNCTION:     FNFDRV
; DESCRIPTION:  find filename in buffered FDRs
; CALLED BY:    FDR2BF,RAWWR,FNFDRF,FNDFIL
; CALLS:        CMPNM
;-------------------------------
FNFDRV     MOV     @FDR^,R1
           AI      R1,3
           LVRA    R1
           MOVB    @VDPRD,R2               ;GET # OF FILES
           SRL     R2,8                    ;MAKE IT A WORD
           MOV     R2,R3                   ;SAVE IT
           AI      R1,6                    ;PTR TO DRIVE #
$4DDE      LI      R4,11                   ;COMPARE 11 BYTES (DRIVE + FILENAME)
           VPSH    $R1+$R2+$R3             ;SAVE R1-R3
           BL      @CMPNM                  ;COMPARE WITH NAME IN COMPARE BUFFER
           VPOP    $R1+$R2+$R3             ;RETRIEVE R1-R3
           MOV     R4,R4                   ;FULLY COMPARED?
           JEQ     $4DFE                   ;YES: DONE
           AI      R1,>0206                ;MOVE TO NEXT FDR IN VDP MEM
           DEC     R2
           JNE     $4DDE                   ;NEXT FILE
$4DFE      B       @VRETN                  ;RETURN TO CALLER (R4=0 IF SUCCESSFUL)
;----------------------------------------------------------------------------------------------
;
; -- F N F D R 2 --
;
; THIS ROUTINE SAVES NAME IN COMPARE BUFFER AND FINDS FDR IN VDP BUFFERS
;
;----------------------------------------------------------------------------------------------
FNFDR2     CALL    FNDFIL
;----------------------------------------------------------------------------------------------
;
; -- F N F D R D --    
;
;   THIS ROUTINE FINDS FDR ON DISK (FROM FILENAME).
;
;----------------------------------------------------------------------------------------------
FNFDRD     MOV     R4,R4
           JEQ     $4DC0                   ;"FILE ERROR"
FNFDR3     MOV     @FDR^,R5                ;PTR TO TOP OF DISK BUFFER IN VDP MEM
           AI      R5,10                   ;PTR TO DRIVE # IN FIRST FILE CONTROL BLOCK
$4E14      LVRA    R5
           MOVB    @VDPRD,R2               ;GET DRIVE #
           JEQ     $4E2E                   ;FREE CONTROL BLOCK FOUND
           AI      R5,518                  ;PTR TO  FILE CONTROL BLOCK
           DEC     R3
           JNE     $4E14                   ;MORE FILES ?
ERR80      BL      @UPDERR                 ;NO:
           DATA    >8000                   ;"MEMORY FULL"
$4E2E      MOV     R5,@FDR^                ;SAVE PTR TO FREE CTRL BLOCK (DRIVE #)
           CALL    RDBUF1                  ;READ SECTOR 1
;----------------------------------------------------------------------------------------------
;
; -- F D R F F L --
;
; THIS ROUTINE FINDS THE ENTRY POINT IF SECTOR 1 ALREADY READ
;
;----------------------------------------------------------------------------------------------
FDRFFL MOV  @FDR^,8
       AI   8,>017E           point to middle of sector 1
       LI   2,>0040           distance: start with 1/4 sector
$4E44  SETO 4
       BL   @LV$R8            read 2 bytes in R0 from VDP address in R8
       MOV  0,0
       JEQ  $4EAA             no file here: move up
       VPSH $R2               save R2
       SETO 2                 code for read
       CALL RWFDR$R0            read FDR, sector # in R0
       MOV  5,1               RWFDR$R0 sets R5 as FDR ptr
       AI   5,>FFFC           point to "sector # of FDR" in ctrl block
       LVWA R5
       MOVB 4,@VDPWD      RWFDR$R0 puts sector # in R4
       SWPB 4                 copy it to ctrl block
       MOVB 4,@VDPWD
       MOV  1,5               FDR ptr
       DEC  1                 now point to drive #
       LVWA R1
       MOVB 6,@VDPWD      save drive #
       LI   4,11              size to compare (drive # + filename)
       BL   @CMPNM            compare filenames
       VPOP $R2               retrieve R2
       JEQ  $4EA6             compared ok: return
       LVWA R5
       MOVB 4,@VDPWD      remaining chars to compare
       C    0,3               what kind of mismatch occured?
       JH   $4EAA             too far down the alphabet
       A    2,8               too far up: move half-way down
       MOV  2,2
       JNE  $4EB0             then divide distance by 2
       INCT 8                 already checked: reset EQ
$4EA6  B    @VRETN            return (from stack)
$4EAA  S    2,8               move half-way up
       MOV  2,2
       JEQ  $4EA6             can't: already checked
$4EB0  SRL  2,2               divide distance by 2
       A    2,2               but keep it even
       JMP  $4E44
;----------------------------------------------------------------------------------------------
;
; -- D E C O D E --
;
; DESCRIPTION:  The cluster info list is located in the FDR, bytes >1C to >FF
;               A cluster info consists in 3 bytes, i.e 6 nibbles
;               3 nibbles specify the beginning sector for that cluster, and
;               3 nibbles specify the total file offset in sectors, including this cluster.
;               The nibbles are arranged as EG SB OF, to be combined as BEG OFS
; 
;----------------------------------------------------------------------------------------------
DECODE     MOV     R11,R10                 ;DECODE CLUSTER INFO
           BL      @LV$R8                  ;READ 2 BYTES VDP AT R8 IN R0
           SWPB    R0
           MOVB    @VDPRD,R2               ;GET THIRD BYTE FROM CLUSTER LIST
           MOV     R0,R1
           ANDI    R1,>0FFF                ;START SECTOR IN R1
           SZC     R1,R0                   ;REMOVE IT FROM R0
           SRL     R2,8
           SOC     R0,R2                   ;COMBINE OFFSET NIBBLES
           SRC     R2,12                   ;OFFSET IN R2
           B       *R10
;----------------------------------------------------------------------------------------------
;
; -- W R C L U S --
;
;   THIS ROUTINE WRITES OUT INFORMATION FOR 1 CLUSTER.
;
;----------------------------------------------------------------------------------------------
WRCLUS     SRC     R2,4
           MOV     R2,R0
HF000      EQU     $+2
           ANDI    R0,>F000                ;SECTOR IN R1, OFFSET IN R2
           SOC     R0,R1                   ;COPY NIBBLE 3 OF OFFSET BEFORE NIBBLE 1 OF SECTOR
           LVWA    R8
           SWPB    R1                      ;WRITE CLUSTER INFO
           MOVB    R1,@VDPWD               ;AS 3 BYTES
           SWPB    R1
           MOVB    R1,@VDPWD
           SWPB    R2
           MOVB    R2,@VDPWD
           RT
;--------------------------------------------------------------------------------------
; FUNCTION:     FSCVIB
; DESCRIPTION:  Find a free sector in bitmap. The sector bitmap is located
;               in the VIB (i.e. sector 0) at bytes >38 to >FF. In each
;               byte a bit defines a sector, from right to left: "0"=free, "1"=used
; CALLED BY:    CREATE,APNSEC
; CALLS:        RDVIB$R6
;--------------------------------------------------------------------------------------
FSCVIB VPSH $R1+$R2+$R3+$R4
       CALL RDVIB$R6            load VIB for drive in R6
       MOV  1,1               first sector specified in R1 ?
       JNE  $4F0A             yes
       LI   1,>0021           no: start with sector 34
$4F0A  INC  1
       MOV  1,0
       SRL  1,3               div by 8 since 8 sect/byte in bitmap
       LI   2,>00FF
       ANDI 0,>0007           bit number in bitmap byte
       JEQ  $4F1C
       SLA  2,0               get that bit
$4F1C  MOV  1,3               byte #
       A    5,3               add VIB ptr
       AI   3,>0038           ptr to sector in bitmap
       CI   1,>00C8           end of VIB?
       JLT  $4F2C             no
       CLR  1                 yes: top of bitmap
$4F2C  A    5,1
       AI   1,>0038           make another bitmap ptr
       LVRA R1
$4F38  SETO 0
       MOVB @VDPRD,0      get bitmap byte
       MOV  0,4               save it
       SOC  2,0               mask previous sectors
       CLR  2
       INC  0                 get 1 more sector
       JNE  $4F68             ok
       INC  1                 byte full: try next
       MOV  1,0
       AI   0,>FF00           won't change if byte # became >100
       C    0,5               still in VIB?
       JNE  $4F5E             yes
       AI   1,>FF38           no: to top of bitmap
       LVRA R1
$4F5E  C    1,3               are we back to where we started?
       JNE  $4F38             no: seach that byte for a free sector
       MOV  4,0               get original byte
       INC  0                 try sectors just before ours (no mask this time)
       JEQ  $4FB2             full: return with EQ
$4F68  DEC  0                 restore original byte
       MOV  0,2
       CLR  0                 bit counter
       SWPB 2
$4F70  INC  0                 increment bit count
       SRC  2,1               find first "0" bit from the right
       JOC  $4F70             not yet
       LI   2,>0080
       SLA  2,0               make a mask for that bit
       DEC  0                 bit # (0-7)
       SOC  2,4               mark sector as used in bitmap byte
       LVWA R1
       MOVB 4,@VDPWD      update bitmap
       AI   1,>FFC8
       S    5,1               byte # in bitmap
       SLA  1,3               times 8 (8 sect per byte)
       SOC  0,1               add bit #: = sector #
       MOV  1,0               save it
$4F96  DEC  5                 point to drive #
       LVRA R5
       MOVB @VDPRD,1      get drive #
       ORI  1,>8000           add flag: update VIB
       LVWA R5
       MOVB 1,@VDPWD      write back flagged drive #
       INC  5                 point to VIB (& return with NEQ)
$4FB2  VPOP $R1+$R2+$R3+$R4   retrieve R1-R4
       B    @VRETN            return to caller
;----------------------------------------------------
; FUNCTION:     FRESEC
; DESCRIPTION:  free sector(s) in bitmap
; CALLED BY:    APNSEC,DELETE
;----------------------------------------------------
FRESEC MOV  0,1
       LI   2,>0001           1 sector only
;----------------------------------------------------
; FUNCTION:     FRESE2
; DESCRIPTION:  entry point if more than 1 sector
; CALLED BY:    FRSEVB
; CALLS:        RDVIB$R6
;----------------------------------------------------
FRESE2 VPSH $R1+$R2+$R3+$R4
       CALL RDVIB$R6            load VIB for drive in R6
       MOV  1,0               sector #
       ANDI 0,>0007           bit in bitmap byte (8 per byte)
       SRL  1,3               byte in bitmap
       A    5,1               add VIB buffer
       AI   1,>0038           add bitmap offset in VIB
       MOV  0,3
       NEG  0
       AI   0,>0008           change 0-7 into 8-1
       LI   4,>00FF           mask to erase
       C    2,0               how many to erase?
       JLT  $4FEE             less than in that byte
       JMP  $5004
$4FEE  LI   0,>0008           free sectors in first byte
       S    2,0
       SRC  4,0               adjust mask
       MOV  3,0               original bit # of starting sector
       JEQ  $4FFC
       SLA  4,0               don't erase before starting sector
$4FFC  JMP  $5000
$4FFE  SRL  9,3               what the heck is that???
$5000  SWPB 4
       JMP  $5036             goto erase last byte
$5004  SRC  4,0               adjust mask
$5006  S    0,2               that many will be freed
       LVRA R1
       MOVB @VDPRD,0      get bitmap byte
       SZC  4,0               mark sectors as free
       LVWA R1
       MOVB 0,@VDPWD      write it back
       LI   4,>FF00           clear full byte
       INC  1                 next byte
       LI   0,>0008           i.e. 8 sectors
       C    2,0               how many more setors?
       JLT  $502E             less than 8
       JMP  $5006             8 or more: next byte
$502E  MOV  2,0               remaining sectors
       LI   4,>00FF
       SLA  4,0               coin mask
$5036  LVRA R1
       MOVB @VDPRD,0      get bitmap byte
       SZC  4,0               mark sectors as free
       LVWA R1
       MOVB 0,@VDPWD      write it back
       JMP  $4F96             done: flag drive # and return
*====================================
* DSR entry points
* R2 = Filepath length?
* R3 = VDP memory pointer to filepath
*====================================
DSK    MOV  11,7              DSK
       BL   @INIT             prepare file operations
       CALL FNDDRV            find disk in drive (name ptr in R3)
       JMP  $5072
DSK1   LI   6,>0100           DSK1
       JMP  DSK$X             ----
DSK2   LI   6,>0200           DSK2
       JMP  DSK$X             ----
DSK3   LI   6,>0300           DSK3
;                             ----
DSK$X  MOV  11,7              save return address
       BL   @INIT             prepare file operations
$5072  MOV  @PAB^,0      PAB ptr
       LVRA R0
       MOVB @VDPRD,1      get opcode
       SRL  1,8
       CI   1,>0009           check range
       JH   ERR60             illegal opcode
       CI   2,>0001           filename lenght (including . )
       JNE  $5098             no filename: dir
       AI   1,>000A           only allow open, close and read
       CI   1,>000C
       JH   ERR60             others are illegal
$5098  A    1,1               make it a word ptr
       MOV  @FJTBL(1),1       get vector
       B    *1                branch to it
*
FJTBL  DATA OPEN              open
       DATA CLOSE             close
       DATA READ              read
       DATA WRITE             write
       DATA REWND             rewind
       DATA LOAD              load
       DATA SAVE              save
       DATA DELETE            delete
       DATA SCRTCH            scratch record: return with "bad attribute" error
       DATA STATUS            status

       DATA OPNDIR            open directory
       DATA CLSDIR            close directory
       DATA RDDIR             read directory
*
ERR60  BL   @UPDERR            return with error
       DATA >6000             "illegal opcode"
*---------------------------------
* Opcode 0: Open
* --------------
* PAB 0: >00
*     1: file type  <--- error code
*   2-3:
*     4: record length
*     5:
*   6-7: # of records (if output)
*     8:
*---------------------------------
OPEN   CLR  0
       MOVB @VDPRD,0      get file attributes
       VPSH $R0
       ANDI 0,>1600           keep fix/var and access mode
       CI   0,>0600
       JNE  $50DC
ERR40  BL   @UPDERR            dis/fix, open as append: return with error
       DATA >4000             "bad attribute"
ERR40A BL   @UPDERR
       DATA >4000
ERR40B BL   @UPDERR
       DATA >4000
ERR40C BL   @UPDERR
       DATA >4000

$50DC  JLE  $50F2
       MOV  0,1               var
       BL   @LV$PAB            get 2 bytes from PAB into R0
       DATA >0004             rec len and char count
       CI   0,>FF00           is rec len 255?
       JHE  ERR40A            yes: bad attribute
       MOV  1,0               retrieve attributes
       ANDI 0,>0600           keep only access mode
$50F2  CI   0,>0200           is it output?
       JNE  $51A6             no
       CALL CREATE             create file
$50FE  BL   @PRPSTA            coin status byte in FDR style
       LVWA R4                status byte in FDR buffer
       MOVB 2,@VDPWD      write file status in FDR
       MOV  @PAB^,3       PAB ptr
       AI   3,>0004           ptr to rec len
       CLR  5
       LVRA R3
       MOVB @VDPRD,5      get record length
       JNE  $5130
       LI   5,>5000           >00: default it 80
       LVWA R3
       MOVB 5,@VDPWD      write default rec len
$5130  AI   4,>0005           point to rec len byte in FDR
       LVWA R4
       MOVB 5,@VDPWD      write rec len in FDR buffer
       LI   1,>0100           256 bytes/sector
       MOV  2,2               var or dis?
       JLT  $5148             var
       JMP  $514C             dis
$5148  A    1,5               var: rec len +1
       DEC  1                 254 bytes only (needs size byte)
$514C  SWPB 5                 make it a word
       CLR  0
       DIV  5,0               how many times in 254/255 bytes?
       AI   4,>FFFC           point to # of rec/sect in FDR
       MOV  0,1               save result for later
       SWPB 0
       LVWA R4
       MOVB 0,@VDPWD      write # of rec/sect in FDR
       MOV  @FDR^,8      point to filename in FDR
       LVRA R8
       MOVB @VDPRD,0      get first char
       ORI  0,>8000           flag it: update FDR before leaving
       LVWA R8
       MOVB 0,@VDPWD      write it back
       VPOP $R0               retrieve R0 (access mode)
       BL   @LV$PAB           get 2 bytes from PAB into R0
       DATA >0006             required size in records
       MOV  0,4
       JEQ  $51A2             no size specified
       JLT  ERR40B            return with "bad attribute" error
       A    1,4               round up to record size
       DEC  4
       CLR  3
       DIV  1,3               how many sectors will this be?
       DEC  3                 offset start from 0
       CALL APNSEC            add sectors to FDR to match offset in R3
$51A2  B    @RWND2            initialise file control block and return to caller
$51A6  VPSH $R0               not output
       CALL FNFDR2            find FDR on disk
       VPOP $R0               retrieve R0 (access mode)
       MOV  4,4               found FDR?
       JEQ  $51CE             yes
       CI   0,>0400           no: is file open as input?
       JEQ  $51CA             yes: must exist
       CALL CREAT3            create file
       JMP  $50FE
$51CA  B    @ERR40C           return with "bad attribute" error
$51CE  MOV  0,7               save access mode
       BL   @PRPSTA           prepare status byte for FDR
       LVRA R4                (status byte in FDR)
       MOVB @VDPRD,0      get current file status
       MOV  0,3               save it
       ANDI 3,>0800           is file write protected?
       JEQ  $51F2             no
       CI   7,>0400           yes: is it open as input?
       JEQ  $51F2             no
       BL   @UPDERR           yes: return with error
       DATA >2000             "write protected"
$51F2  ANDI 0,>8300           keep only file type bits (V/F, D/I, Prg/Data)
       XOR  2,0               compare with new (coined by PRPSTA)
       JNE  $51CA             different: bad attribute
       MOV  @PAB^,3      PAB ptr
       AI   3,4               ptr to rec len in PAB
       AI   4,5               ptr to rec len in FDR
       LVRA R4
       MOVB @VDPRD,0      get rec len from FDR
       LVRA R3
       MOVB @VDPRD,2      get rec len from PAB
       JEQ  $5220             0 = keep current one
       CB   0,2               are they identical?
       JNE  $51CA             no: "bad attribute"
$5220  LVWA R3
       MOVB 0,@VDPWD      update rec len in PAB (in case it was 0)
       VPOP $R0               retrieve R0 (open mode)
       ANDI 0,>0600           keep only access mode
       CLR  2
       SETO 3
       CI   0,>0600           is it "append"
       JNE  $5278             no
       MOV  @FDR^,4      yes: get FDR ptr
       MOV  4,7               save it
       AI   4,>000E           ptr to # of sectors
       LVRA R4
       MOVB @VDPRD,3      get # of sectors in file
       SWPB 3
       MOVB @VDPRD,3
       SWPB 3
       MOVB @VDPRD,2      get eof offset
       DEC  3                 offset starts from 0
       JLT  $5278             file is empty (0 sectors)
       VPSH $R2+$R3           save R2 + R3
       AI   7,>0100           ptr to data buffer area for this file
       CALL RDSEC$OFS            read a sector, from offset in R3
       VPOP $R2+$R3           retrieve R2 + R3
$5278  BL   @UPSCRC           update current record offset in file ctrl block
       B    @VRETN            return to caller
;----------------------------------------------------
; FUNCTION:     PRPSTA
; DESCRIPTION:  prepare file status byte for FDR
; CALLED BY:    OPEN
;----------------------------------------------------
PRPSTA VPOP $R0               access mode in R0
       VPSH $R0               save it back
       LI   2,>0002           "int" in FDR status byte
       MOV  @FDR^,4      FDR pointer
       SLA  0,4               fix or var?
       JNC  A529E             fix
       LI   2,>0082           "int var" in FDR
       MOV  0,0               dis or int?
A529E  JLT  A52A2             int
       DECT 2                 dis: remove the "int" from FDR status
A52A2  AI   4,>000C           point at file status byte in FDR
       SWPB 2
       RT
;------------------------------------------------------------------
; FUNCTION:     UPSCRC
; DESCRIPTION:  update sect + rec offsets in file control block
; CALLED BY:    REWND,OPEN
;------------------------------------------------------------------
UPSCRC MOV  @FDR^,4
       AI   4,>FFFA
       LVWA R4
       MOVB 3,@VDPWD      write current sect offset
       SWPB 3
       MOVB 3,@VDPWD
       AI   4,>0004           point to logical rec offset (for var files)
       LVWA R4
       MOVB 2,@VDPWD      write record offset
       RT
;
;----------------------------------
; Opcode 1: Close
; --------------
; PAB 0: >01
;     1:           <--- error code
;   2-3:
;     4:
;     5:
;   6-7:
;     8:
;----------------------------------
CLOSE      CALL    FNFDR4                  ;FIND FILE FDR
           B       @UPFDRD                 ;UPDATE FDR + DATA AREA, GET VIB, RETURN TO CALLER
;
;----------------------------------
; Opcode 2: Read
; --------------
; PAB 0: >02
;     1: file type <--- error code
;   2-3: data buffer address in VDP mem
;     4:
;     5: bytes read
;   6-7: record #
;     8:
;
; Logical records organisation whithin sectors
;
; Fixed records (e.g. rec length = 6)
; 11 11 11 11 11 11 22 22 22 22 22 22 33 33 33 33 33 33 xx xx xx
; Where 11=data for record 1, 22=record 2, 33=record 3, xx=junk bytes
;
; Variable records:
; sz 11 11 11 11 11 11 11 sz 22 22 22 22 22 sz 33 33 FF xx xx xx
; Where sz=record size, 11,22,33=record data, FF=end-of-sector mark, xx=junk
;----------------------------------
READ   CALL FNFDR4            find FDR in VDP buffers, get status from PAB
       ANDI 0,>0200           what type of access?
       JEQ  $52EC             "update" or "input": ok
$52E8  B    @ERR60            "append" or "output": error "illegal opcode"
$52EC  BL   @GTFDRS           get status byte in R0, from FDR
       JLT  $5306             var
       BL   @GTREC#           fix: get rec # compare to # of recs/file
       JL   $52FE             ok: in file
$52F8  BL   @UPDERR           update data and return with error
       DATA >A000             "eof reached"
$52FE  CALL RDREC             load record from disk into FDR data buffer area
       JMP  A5328
$5306  CALL LDSEC             var: load proper sector, point to rec in it
       JMP  $52F8             skipped if ok: return with "eof reached"
       INC  2                 next byte in data buffer
       A    4,0               add rec size to offset
       INC  0                 room for end-of-sector mark
       MOV  @FDR^,5      FDR ptr
       DECT 5                 point to var record offset in sector
       SWPB 0
       LVWA R5
       MOVB 0,@VDPWD      update var record offset in sector
       MOV  4,0               save # of bytes to be read
A5328  MOV  @PAB^,4      PAB ptr
       AI   4,>0005           point to char count
       SWPB 0
       LVWA R4
       MOVB 0,@VDPWD      write # of char to be read
       SWPB 0                 make it a word
DTACP  MOV  0,0               check it
       JEQ  $535E             none: return
$5342  LVRA R2
       MOVB @VDPRD,3      read 1 byte from FDR data buffer area
       INC  2                 increment source ptr
       LVWA R1
       MOVB 3,@VDPWD      write the byte in PAB data buffer
       INC  1                 increment destination ptr
       DEC  0                 more to read?
       JNE  $5342             yes
$535E  B    @VRETN            return to caller
;-----------------------------------------------------
; FUNCTION:     LDSEC
; DESCRIPTION:  load sector, point to record in it
; CALLS:        GTRCOF,UPBFR,UPSRCB,RDSEC$OFS,DTABF
; CALLED BY:    STATUS,READ
;-----------------------------------------------------
LDSEC  BL   @GTRCOF
       MOV  3,3               sector offset
       JLT  $536C             -1: top of file
       JMP  $5390             in file
$536C  MOV  3,0
       INC  0                 next sector
       C    0,2               compare to # of sect/file
       JEQ  $535E             end-of-file reached: return to caller (JMP to err)
       CALL UPBFR             update data buffer, if needed
       MOV  0,3               desired offset
       CLR  5
       BL   @UPSRCB           update sect + rec offsets in control block
       AI   7,>0100           point to data buffer area (R7 set by UPSRCB)
       CALL RDSEC$OFS            read a sector, from offset in file (in R3)
       CLR  0
       JMP  $5398
$5390  MOV  0,0               in file: test var rec offset (from GTRCOF)
       JNE  $5398             inside sector
       C    0,2               at beg of sector
       JEQ  $535E             file is empty: return
$5398  MOV  0,2               FDR data buffer will be added to R2 by DTABF
       CALL DTABF             R2=byte in FDR data buf, R1=top of PAB data buf
       LVRA R2
       MOVB @VDPRD,4      get first byte (rec length)
       SRL  4,8               make it a word
       MOV  0,0               var rec offset
       JEQ  $53BC
       CI   4,>00FF           is it >FF (end of sector mark) ?
       JNE  $53BC             no
       BL   @GTRCOF           yes: get sect + rec offsets from control block
       JMP  $536C             try again with next sector
$53BC  VPOP $R11              retrieve return address from stack in R11
       INCT 11                skip the JMP to "eof reached" error
       RT
*
*------------------------------------
* Opcode 3: Write
* --------------
* PAB 0: >03
*     1: file type <--- error code
*   2-3: data buffer address in VDP mem
*     4:
*     5: bytes to write
*   6-7: record #
*     8:
*------------------------------------
WRITE  CALL FNFDR4            find FDR in VDP buffers
       ANDI 0,>0600           keep only access mode
       CI   0,>0400           is it "input"
       JEQ  $52E8             yes: return with error "illegal opcode"
       BL   @GTFDRS           get file status byte from FDR
       JLT  $5402             var
       BL   @GTREC#           fix: get rec # from PAB, sect # in R0
       JL   $53FA             less that total rec/file
       VPSH $R0+$R1+$R3+$R4   past eof: expand file
       MOV  0,3               desired sector offset
       CALL APNSEC            append enough sectors to reach offset in R3
       VPOP $R0+$R1+$R3+$R4   restore R0, R1, R3, R4
       BL   @UPRCFL           update # of rec/file in FDR
$53FA  CALL RDREC             in file: fetch rec from disk into FDR data buffer area
       JMP  $54A8             set "update data" flag, write data, return
$5402  BL   @GTRCOF           var: R2=sect/file R3=sect offset R0=rec offset
       MOV  3,3               sector offset in file
       JLT  $540C             -1: top of file
       JMP  $5422             in file
$540C  VPSH $R2+$R3           "next sector" loop
       CALL UPBFR             update data buffer if needed
       VPOP $R2+$R3
       INC  3                 next sector
       CLR  0                 init char offset in sector
$5422  C    3,2               did we reach last sector?
       JNE  $5438             no
       VPSH $R0+$R3           yes: expand file
       CALL APNSEC            get last sector then append sectors to reach R3
       VPOP $R0+$R3
$5438  MOV  @PAB^,5      PAB ptr
       AI   5,>0005           point to char count
       LVRA R5
       MOVB @VDPRD,4      get # of chars to write
       SRL  4,8               make it a word
       MOV  4,5
       A    0,5               add current char offset in sector
       INC  5                 make room for size byte
       CI   5,>00FF           past end of sector?
       JH   $540C             yes: not enough room, try next sector
       SETO 2                 ok: rec will fit in sector
       MOV  @FDR^,1      FDR ptr
       A    5,1               past-last-byte offset
       AI   1,>0100           ptr to data buffer area
       LVWA R1
       MOVB 2,@VDPWD      write end-of-sect mark to FDR data buffer area
       VPSH $R0               save R0 (current byte offset in sector)
       MOV  3,0
       BL   @UPSRCB           update sect + rec offsets in control block
       AI   1,>0012           point to eof offset in FDR (R1 modified by UPSRCB)
       LVWA R1
       MOVB 5,@VDPWD      update eof offset in last sector, in FDR
       BL   @UPRCFL           update # of rec/file in FDR
       VPOP $R2               retrieve old R0 in R2 (current byte offset)
       MOV  4,0               record size
       CALL DTABF             get FDR data buffer in R2, PAB data buffer in R1
       SWPB 4
       LVWA R2
       MOVB 4,@VDPWD      write size byte to FDR data buffer
       INC  2                 increment dest pointer
$54A8  MOV  2,3               invert source and dest
       MOV  1,2               so we can use the same read-write loop
       MOV  3,1               than the "read" opcode
       MOV  @FDR^,4      FDR ptr
       DEC  4                 pointer to drive # for that file
       LVRA R4
       MOVB @VDPRD,5      get drive #
       ORI  5,>8000           add "update data area" flag
       LVWA R4
       MOVB 5,@VDPWD      write back flagged byte
       B    @DTACP            to read-write loop
;----------------------------------------------------
; FUNCTION:     FNFDR4
; DESCRIPTION:  find FDR in VDP buffer
; CALLED BY:    REWND,CLOSE,READ,WRITE
; CALLS:        ADJFDR(jmp),UPDERR
;----------------------------------------------------
FNFDR4 CALL FNDFIL            find file FDR
       MOV  4,4               found?
       JEQ  ADJFDR            yes
       BL   @UPDERR           no: return with error
       DATA >E000             "file error"
;----------------------------------------------------
; FUNCTION:     ADJFDR
; DESCRIPTION:  adjust FDR ptr, get PAB file type into R4
; CALLED BY:    STATUS,ADJFDR(jmp)
;----------------------------------------------------
ADJFDR INC  1                 point to filename in FDR
       MOV  1,@FDR^      new FDR ptr
       MOV  @PAB^,4      get PAB ptr
       INC  4                 point to status byte
       CLR  0
       LVRA R4
       MOVB @VDPRD,0      get file status
       B    @VRETN            return to caller
;----------------------------------------------------
; FUNCTION:     GTFDRS
; DESCRIPTION:  get status byte from FDR
; CALLED BY:    STATUS,READ,WRITE
;----------------------------------------------------
GTFDRS MOV  @FDR^,4
       AI   4,12              point to status byte
       LVRA R4
       MOVB @VDPRD,0      read status byte
       RT
;----------------------------------------------------
; FUNCTION:     GTREC#
; DESCRIPTION:  get record # from PAB, check if valid
; CALLED BY:    RDREC,READ,WRITE
; CALLS:        UPDERR
;----------------------------------------------------
GTREC# MOVB @VDPRD,5
       SRL  5,8               -------------------------------------
       JNE  $551C             get # of rec/sector from FDR
       LI   5,>0100           0: default to 256
$551C  MOV  @PAB^,3      PAB ptr
       AI   3,>0006           point to rec #
       LVRA R3
       MOVB @VDPRD,1      get record # from PAB
       SWPB 1
       MOVB @VDPRD,1
       SWPB 1
       MOV  1,0               save it
       JLT  $553C             too big
       JMP  $5542             ok
$553C  BL   @UPDERR           update data then return with error
       DATA >8000             "memory full"
$5542  INC  0                 next record
       LVWA R3
       MOVB 0,@VDPWD      write back # of future record
       SWPB 0
       MOVB 0,@VDPWD
       CLR  0
       MOV  1,3               save # of desired rec
       DIV  5,0               divide by # of rec/sector = sect # in R0
GT#SEC MOV  @FDR^,2      FDR ptr
       AI   2,>0012           point to total # of rec (# of sectors for var)
       LVRA R2
       MOVB @VDPRD,2      get total # of recs/file (sect/file for var)
       SWPB 2
       MOVB @VDPRD,2      remember: bytes are swapped
       C    3,2               compare with desired record (ignored by var)
       RT
;----------------------------------------------------
; FUNCTION:     RDREC
; DESCRIPTION:  Fetch record into FDR data buffer area
; CALLED BY:    READ,WRITE
; CALLS:        UPBFR,RDSEC$OFS
;----------------------------------------------------
RDREC  VPSH $R1
       AI   4,>FFEE           ptr to top of control block
       LVRA R4
       MOVB @VDPRD,5      get current sector offset in file
       SWPB 5
       MOVB @VDPRD,5
       SRC  5,8
       JLT  $559E             -1: top of file
       C    5,0               compare with desired offset (from GTREC#)
       JEQ  $55AE             same
       CALL UPBFR             update data buffer if needed
$559E  MOV  0,3               desired sector offset in file
       BL   @UPSRCB           update sect + rec offsets in control block
       AI   7,>0100           point to data buffer area (R7 set by UPSRCB)
       CALL RDSEC$OFS            read a sector from offset in file (in R3)
$55AE  VPOP $R1               retrieve R1
       MOV  @FDR^,3      FDR ptr
       AI   3,>0011           point to record length
       LVRA R3
       MOVB @VDPRD,0      get rec length in bytes
       SRL  0,8               make it a word
       MPY  0,1               calc file offset in bytes
DTABF  A    @FDR^,2      add FDR ptr
       AI   2,>0100           point inside data buffer area
       MOV  @PAB^,3      PAB ptr
       INCT 3                 point to data buffer address
       LVRA R3
       MOVB @VDPRD,1      get PAB data buffer address
       SWPB 1
       MOVB @VDPRD,1
       SWPB 1
       B    @VRETN            return
;----------------------------------------------------
; FUNCTION:     UPSRCB
; DESCRIPTION:  update sect + rec offsets in control block
; CALLED BY:    LDSEC,WRITE,RDREC
;----------------------------------------------------
UPSRCB MOV  @FDR^,7
       MOV  7,1               FDR ptr
       AI   1,>FFFA           top of file control block
       LVWA R1
       MOVB 0,@VDPWD      current sector offset in file
       SWPB 0
       MOVB 0,@VDPWD
       AI   1,>0004           point to var rec offset in sector
       SWPB 5
       LVWA R1
       MOVB 5,@VDPWD      first free byte in current sector
       RT
;---------------------------------------
; FUNCTION:     UPRCFL
; DESCRIPTION:  Update # of rec/file in FDR
; CALLED BY:    WRITE
;---------------------------------------
UPRCFL MOV  @FDR^,2
       LVRA R2                (FDR ptr)
       MOVB @VDPRD,10     get first char of filename
       ORI  10,>8000          set "was modified" flag
       LVWA R2
       MOVB 10,@VDPWD     write flagged char back
       AI   2,>0012           point to # of recs/file in FDR
       INC  3                 one more
       LVWA R2
       SWPB 3                 update # of recs/file
       MOVB 3,@VDPWD
       SWPB 3
       MOVB 3,@VDPWD
       RT
;---------------------------------------
; FUNCTION:     GTRCOF
; DESCRIPTION:  get rec offset, compare sect with total
; CALLED BY:    LDSEC,WRITE
; CALLS:        LV$R8O,GT#SEC(jmp)
;---------------------------------------
GTRCOF MOV  @FDR^,8
       MOV  8,4               FDR ptr
       AI   4,>0100           point to data buffer area
       DECT 8                 point to var record offset
       LVRA R8
       MOVB @VDPRD,2      get var rec offset in current sector
       SRL  2,8               make it a word
       MOV  11,10             save return point
       BL   @LV$R8O           get 2 bytes from FDR (at R8-4) into R0
       DATA -4                current sector offset in file
       MOV  0,3               save it
       MOV  10,11             restore return point
       MOV  2,0               var record offset
       B    @GT#SEC           get # of sect/file from FDR, return
*------------------------------------
* Opcode 4: Rewind
* --------------
* PAB 0: >04
*     1: file type <--- error code
*   2-3:
*     4:
*     5:
*   6-7: record #  <--- >0000 if sequential
*     8:
*------------------------------------
REWND  CALL FNFDR4            find FDR in VDP buffers, read status from PAB
       VPSH $R0               save R0 (status from PAB)
       ANDI 0,>0600           keep only access mode
       JEQ  $5696             "update" is ok
       CI   0,>0400           is it "input"?
       JEQ  $5696             yes: ok
       B    @ERR60            "output" or "append": return with "illegal opcode"
$5696  CALL UPBFR             update data buffer if needed
       VPOP $R0               retrieve R0 (status from PAB)
       ANDI 0,>0100           sequential or reloc?
       JNE  $56CA             reloc: don't do anything, return
RWND2  CLR  2                 rewind file: record offset = 0
       SETO 3                 -----------  current record = -1 (none)
       BL   @UPSCRC           update file control block
       CLR  0                 record 0
       MOV  @PAB^,8      get PAB ptr
       AI   8,>0006           point to record #
       LVWA R8
       MOVB 0,@VDPWD      write record #
       NOP
       MOVB 0,@VDPWD
$56CA  B    @VRETN            return to caller
*

*------------------------------------
* Opcode 5: Load
* --------------
* PAB 0: >05
*     1: file type  <--- error code
*   2-3: data buffer address in VDP mem
*     4:
*     5:
*   6-7: maximum # of bytes (size of buffer)
*     8:
*------------------------------------
LOAD   CALL FNFDR2            find FDR on disk
       MOV  4,4               found?
       JEQ  $56DE             yes
$56D8  BL   @UPDERR           no: return with error
       DATA >E000             "file error"
$56DE  BL   @LV$FDR           get 2 bytes from FDR into R0
       DATA >000C             file status byte
       ANDI 0,>0100           is it "program"?
       JEQ  $56D8             no: file error
       INCT 8                 point to # of sect/file in FDR
       LVRA R8
       MOVB @VDPRD,1      get # of sectors in file
       SWPB 1
       MOVB @VDPRD,1
       SRC  1,8
       JEQ  $56D8             0=empty file: return with "file error"
       BL   @GTBFAD           get data buffer address in R7, # of sectors in R2
       INCT 8                 point to eof offset in FDR
       CLR  4
       LVRA R8
       MOVB @VDPRD,4      get # of bytes in last sector
       C    1,2               compare # of sect with max in PAB
       JH   $56D8             file is too big: return with "file error"
       JNE  $571C             file is smaller
       C    0,4               same # of sect: check bytes in last sector
       JL   $56D8             file is too big: "file error"
$571C  CLR  3                 sector offset in file
       SWPB 4
$5720  DEC  1                 next sector
       JEQ  $573E             done
       VPSH $R0+$R1+$R3+$R4+$R7
       CALL RDSEC$OFS            read a sector from offset in R3
       VPOP $R0+$R1+$R3+$R4+$R7
       INC  R3                next sector
       AI   7,>0100           256 bytes further in PAB buffer
       JMP  $5720             keep going
$573E  MOV  4,4
       JNE  $574A
       CALL RDSEC$OFS            read a sector from offset in R3
       JMP  $576C             done
$574A  MOV  7,5               save PAB data buffer ptr
       MOV  @FDR^,7      FDR ptr
       AI   7,>0100           point to FDR data area
       VPSH $R4+$R5+$R7
       CALL RDSEC$OFS            read a sector from offset in R3
       VPOP $R0+$R1+$R2       restore R4 in R0, R5 in R1, R7 in R2
       CALL DTACP             write bytes from FDR data buffer to PAB data buf
$576C  B    @UPFDRD           update FDR, data buffer, VIB and return to caller
*
*------------------------------------
* Opcode 6: Save
* --------------
* PAB 0: >06
*     1: file type  <--- error code
*   2-3: data buffer address in VDP mem
*     4:
*     5:
*   6-7: # of bytes to save
*     8:
*------------------------------------
SAVE   CALL CREATE            create file
       BL   @GTBFAD           get PAB buffer ptr + # of bytes
       CLR  3                 sector offset 0
$577C  VPSH $R0+$R2+$R3+$R7
       CALL WRSEC$OFS            write sector to offset in R3
       VPOP $R0+$R2+$R3+$R7   retrieve R0, R2, R3, R7
       INC  3                 next sector
       AI   7,>0100           256 bytes further in PAB data buffer
       DEC  2                 next sector
       JNE  $577C             more to do
       MOV  @FDR^,1      FDR ptr
       AI   1,>000C           point to file status byte
       LI   2,>0100           value for "program" file
       LVWA R1
       MOVB 2,@VDPWD      write file status byte in FDR
       AI   1,>0004           point to eof offset byte in FDR
       LVWA R1
       MOVB 0,@VDPWD      # of bytes in last sector
       B    @UPFDRD           update FDR, data buffer, VIB then return to caller
;--------------------------------------------------
; FUNCTION:     GTBFAD
; DESCRIPTION:  get buffer address + # of bytes
; CALLED BY:    LOAD,SAVE
;--------------------------------------------------
GTBFAD MOV  @PAB^,0
       INCT 0                 data buffer in PAB
       LVRA R0
       MOVB @VDPRD,7      get data buffer address
       SWPB 7
       MOVB @VDPRD,7
       SWPB 7
       AI   0,>0004           point to # of bytes to transfer
       LVRA R0
       MOVB @VDPRD,2      get # of bytes to be transfered
       SRL  2,8               make it # of sectors (256 bytes each)
       CLR  0
       MOVB @VDPRD,0      see if one more is needed
       JEQ  $57F2             no
       INC  2                 yes: one more sector
$57F2  RT
*
*-------------------------------------
* Opcode 9: Status
* --------------
* PAB 0: >09
*     1:
*   2-3:
*     4:
*     5:
*   6-7: record #
*     8:           <--- file status
*
* Status bits, returned in PAB byte 8:
* >80: file not found
* >40: file is protected
* >20:
* >10: internal (else display or program)
* >08: program file
* >04: variable (else fixed or program)
* >02: memory full
* >01: end-of-file reached
*-------------------------------------
STATUS CALL FNDFIL            save filename in comp buf, then find FDR in VDP
$57FA  MOV  4,4               found?
       JEQ  $581E             yes
       CALL FNFDR3            no: find FDR on disk
       LI   0,>8000           value for "file not found"
       MOV  4,4               found?
       JNE  $589E             no: return with that value
       MOV  @FDR^,1      yes: ptr to FDR
       CLR  2
       LVWA R1
       MOVB 2,@VDPWD      invalidate that FDR (file not open)
       JMP  $588A             transfer FDR status to PAB status byte
$581E  CALL ADJFDR            file is open: adjust FDR ptr, get PAB file type into R4
       BL   @GTFDRS           get status byte from FDR
       JLT  $582C             var
       JMP  $5838             fix
$582C  CALL LDSEC             var: load wanted sector, point to wanted rec in buffer
       JMP  $585A             out of range
       CLR  2                 ok: clear flag
       JMP  $588A             copy status byte from FDR into PAB, return
$5838  MOVB @VDPRD,5      fix: get rec/sect byte
       SRL  5,8               make it a word
       JNE  $5844
       LI   5,>0100           00 (program files) means 256
$5844  BL   @LV$PAB           get 2 bytes from PAB into R0
       DATA >0006             # of wanted record
       MOV  0,3               save it
       JLT  $5886             too big: set memory full bit in PAB status byte
       BL   @GT#SEC           get # recs/file into R2, comp with R3
       CLR  2
       JL   $588A             in file: copy file type bits, return
       DIV  5,2               how many sectors do we need?
       MOV  2,3               save result
$585A  BL   @LV$FDR           get 2 bytes from FDR into R0
       DATA >000E             # of sectors/file
       INC  3                 plus 1 sector for FDR
       LI   2,>0100           value for "eof reached" in PAB status
       S    0,3               are there enough sectors in file for these recs?
       JGT  $586C
       JMP  $588A             yes: we reached the eof
$586C  CALL RDVIB$R6            load VIB (sector 0)
       MOV  3,4               number of sectors that will be needed
       MOV  5,8               VIB ptr
       AI   8,>000A           skip 10 bytes (required by CNTSEC)
       BL   @CNTSEC           count free sectors in bitmap, into R3
       LI   2,>0100           value for "eof reached"
       C    3,4               are there that many free sectors?
       JHE  $588A             yes
$5886  LI   2,>0200           value for "memory full"
$588A  BL   @LV$FDR           get 2 bytes from FDR into R0
       DATA >000C             file status byte
       ANDI 0,>8F00           mask irrelevant bits
       JGT  $589A
       ORI  0,>0080           var: put var bit in PAB status style
$589A  SLA  0,3               get rid of var bit in FDR style
       SOCB 2,0               add "eof" and "mem full" bits
$589E  MOV  @PAB^,1      PAB ptr
       AI   1,>0008           point to bias/status return byte
       LVWA R1
       MOVB 0,@VDPWD      write file status to PAB
       B    @VRETN            return to caller
;-----------------------------------------------------------------------------
; DISK DIRECTORY ACCESS
; ---------------------
; THE DIRECTORY IS ACCESSED BY OMITING THE FILENAME IN THE DSR NAME: "DSK1."
; IT MUST BE OPENED FOR INPUT ONLY, AS AN INT/FIX 38 FILE.
; IT CONSISTS IN UPTO 128 RECORDS, THE FIRST ONE CONTAINS THE DISK INFORMATIONS,
; THE OTHERS THE INFORMATIONS FOR UPTO 127 FILES (IN ALPHABETICAL ORDER).
; EACH RECORD CONSISTS IN AN ASCII STRING AND THREE FLOATING POINT NUMBERS.
;
; RECORD 0 CONTAINS:
; - DISKNAME (AN ASCII STRING OF UPTO 10 CHARS).
; - THE NUMBER ZERO.
; - THE NUMBER OF SECTORS ON DISK.
; - THE NUMBER OF FREE SECTORS ON DISK.
;
; OTHER RECORDS CONTAIN:
; - FILENAME (AN ASCII STRING OF UPTO 10 CHARS).
; - FILETYPE: 1=D/F, 2=D/V, 3=I/F, 4=I/V, 5=PROG, 0=END OF DIRECTORY.
;   IF THE FILE IS PROTECTED, THIS NUMBER IS NEGATIVE (-1=D/F, ETC).
; - FILE SIZE IN SECTORS (INCLUDING THE FDR ITSELF).
; - FILE RECORD LENGTH (0 FOR PROGRAMS).
;-----------------------------------------------------------------------------

;-----------------------------------------------------------------------------
;
; -- O P N D I R --
;
;   THIS ROUTINE OPENS THE DISK DIRECTORY PSEUDO-FILE
;
;-----------------------------------------------------------------------------
OPNDIR     MOVB    @VDPRD,0                ;GET FILE TYPE FROM PAB
           ANDI    R0,>1E00                ;MASK IRRELAVANT BITS (REL/SEQ)
           CI      R0,>0C00                ;IS IT "INT/FIX" IN "OUTPUT" MODE?
           JEQ     $58C8                   ;YES
$58C2      BL      @SCRTCH                 ;RETURN WITH ERROR
           DATA    >4000                   ;"BAD ATTRIBUTES"
$58C8      BL      @LV$PAB                 ;GET 2 BYTES FROM PAB INTO R0
           DATA    >0004                   ;REC LENGTH
           SRL     R0,8                    ;MAKE IT A WORD
           JEQ     $58D8                   ;>00= DEFAULT: SET IT TO 38
           CI      R0,>0026                ;IS IT 38?
           JNE     $58C2                   ;NO: RETURN WITH "BAD ATTRIBUTES" ERROR
$58D8      LI      R0,>2600                ;SET REC LEN TO 38
           LVWA    R8                      ;(FROM LV$PAB)
           MOVB    R0,@VDPWD               ;WRITE REC LEN TO PAB
           CLR     R7
           BL      @FDVFCB                 ;FIND MATCHING DRIVE IN FILE CONTROL BLOCKS
           DATA    $58F4                   ;GO THERE IF NOT FOUND
ERRE0      BL      @UPDERR                 ;UPDATE DATA THEN RETURN WITH ERROR
           DATA    >E000                   ;"FILE ERROR"
$58F4      MOV     R7,R7                   ;DID WE FIND A FREE SLOT?
           JNE     $58FE                   ;YES
           BL      @SCRTCH                 ;NO: RETURN WITH ERROR
           DATA    >8000                   ;"MEMORY FULL"
$58FE      LVWA    R7
           MOVB    R3,@VDPWD               ;WRITE DRIVE #
           SWPB    R3
           MOVB    R3,@VDPWD               ;AND A SPACE AS FILENAME (ILLEGAL, INDICATES DIR)
           B       @VRETN                  ;RETURN TO CALLER
;--------------------------------------------------------------------------------------------
;
; -- C L S D I R --
;
; THIS ROUTINE CLOSES THE DISK DIRECTORY
;
;--------------------------------------------------------------------------------------------
CLSDIR     BL      @FDVFCB                 ;FIND MATCHING DRIVE IN FILE CONTROL BLOCKS
           DATA    ERRE0                   ;GO THERE IF NOT FOUND: RETURN WITH "FILE ERROR"
           CLR     R0
           LVWA    R8
           MOVB    R0,@VDPWD               ;CLEAR DRIVE #
           B       @VRETN
;--------------------------------------------------------------------------------------------
;
; -- R D D I R --
;
;   THIS ROUTINE READS A RECORD FROM DISK DIRECTORY
;
;--------------------------------------------------------------------------------------------
RDDIR      BL      @FDVFCB                 ;FIND MATCHING DRIVE IN FILE CONTROL BLOCKS
           DATA    ERRE0                   ;GO THER IT NOT FOUND: RETURN WITH "FILE ERROR"
           INC     R8
           MOV     R8,R5                   ;SAVE PTR TO FDR
           BL      @LV$PAB                 ;GET 2 BYTES FROM PAB INTO R0
           DATA    >0006                   ;RECORD #
           MOV     R0,R2                   ;SAVE IT
           INC     R2
           LVWA    R8
           MOVB    R2,@VDPWD               ;WRITE RECORD NUMBER IN FDR
           SWPB    R2                      ;AFTER FIRST CHAR OF FILENAME!
           MOVB    R2,@VDPWD
           SLA     R0,1                    ;SINCE TWO BYTE PER FILE PTR
           MOVB    R0,R0                   ;IS REC # GREATER THAN 128?
           JEQ     $5958                   ;NO
           BL      @UPDERR                 ;YES: UPDATE DATA THEN RETURN WITH ERROR
           DATA    >A000                   ;"PAST EOF"
$5958      SETO    R2                      ;CODE FOR READ
           MOV     R0,R4                   ;RECORD #
           JEQ     $59D4                   ;0=DISK PARAMETERS
           LI      R4,1                    ;SECTOR #1
           CALL    RWSEC$R7                   ;READ SECTOR INTO BUFFER IN R5
           MOV     R5,R8                   ;BUFFER PTR
           AI      R5,>00FF                ;POINT TO DATA BUFFER AREA IN THIS CTRL BLOCK
           DECT    R0                      ;DON'T COUNT RECORD 0
           A       R0,R8                   ;POINT TO DESIRED FILE PTR
           BL      @LV$R8                  ;GET TWO BYTE FROM VDP AT R8 INTO R0
           MOV     R0,R4                   ;SECTOR WHERE THAT FDR IS TO BE FOUND
           JEQ     $59CA                   ;NO MORE
           CALL    RWSEC$R7                   ;READ FDR SECTOR INTO DATA BUFFER AREA
           BL      @LV$FDR                 ;GET 2 BYTES FROM FDR INTO R0
           DATA    >010E                   ;# OF SECT/FILE
           MOV     R0,R6                   ;SAVE IT TO OUTPUT FILE SIZE
           INC     R6                      ;INCLUDE THE FDR ITSELF
           MOVB    @VDPRD,R3               ;IGNORE EOF OFFSET
           LI      R2,>0A00                ;10 CHARS PER FILENAME
           MOVB    @VDPRD,R3               ;GET REC LENGTH
           SRL     R3,8                    ;MAKE IT A WORD
           DECT    R8                      ;POINT TO STATUS BYTE IN FDR
           LVRA    R8
           MOVB    @VDPRD,R0               ;GET FILE STATUS BYTE
           MOV     R0,R7
           ANDI    R0,>0800                ;KEEP ONLY "WRITE PROTECTED" BIT
           SZCB    R0,R7                   ;CLEAR "WRITE PROTECTED" BIT (IF IT WAS SET)
           SRL     R7,8                    ;MAKE IT A WORD
           INC     R7                      ;TYPES ARE NUMBERED FROM 1
           CI      R7,2                    ;IS IT A "PROGRAM" FILE?
           JNE     $59BA                   ;NO
           AI      R7,3                    ;YES: MAKE IT TYPE 5
$59BA      CI      R7,8                    ;IS IT VAR?
           JL      $59C4                   ;NO
           AI      R7,>FF81                ;YES: ADD 1 AND CLEAR "VAR" BIT
$59C4      SLA     R0,4                    ;"WRITE PROTECT" BIT WILL BE >80
           SOC     R0,R7                   ;ADD IT TO FILE TYPE
           JMP     $59D2
$59CA      CLR     R2                      ;NO MORE FILES: FILENAME SIZE = 0
           CLR     R6                      ;FILE SIZE = 0
           CLR     R3                      ;REC LENGTH = 0
           CLR     R7                      ;TYPE = 0
$59D2      JMP     $59F2                   ;OUTPUT THAT
$59D4      AI      R5,>00FF                ;DISK INFO: POINT TO DATA BUFFER AREA IN CTRL BLOCK
           CALL    RWSEC$R7                   ;READ SECTOR 0
           BL      @LV$FDR                 ;GET 2 BYTES FROM FDR INTO R0
           DATA    >010A                   ;# OF SECTORS ON DISK
           MOV     R0,R6                   ;DUPLICATE IT
           DECT    R6                      ;MINUS DIRECTORY ITSELF (SECT 0 + 1)
           BL      @CNTSEC                 ;COUNT FREE SECTORS IN BITMAP, RESULT IN R3
           CLR     R7                      ;FILETYPE IS NOT USED
           LI      R2,>0A00                ;DISKNAME IS 10 CHARS
$59F2      BL      @LV$PAB                 ;GET 2 BYTES FROM PAB INTO R0
           BYTE    >00
H02        BYTE    >02                     ;DATA BUFFER ADDRESS
           MOV     R0,R8                   ;DUPLICATE IT
           INC     R8                      ;SKIP FIRST BYTE
           SRL     R2,8                    ;FILENAME LENGTH (OR DISKNAME)
           JEQ     $5A2C                   ;0: SKIP FILENAME COPYING
           CLR     R1
$5A02      LVRA    R5                      ;(FDR PTR)
           MOVB    @VDPRD,R1               ;GET 1 CHAR FROM FILENAME IN FDR
           CI      R1,>2000                ;IS IT A SPACE?
           JEQ     $5A24                   ;YES: END OF NAME
           LVWA    R8                      ;(PAB DATA BUFFER PTR)
           MOVB    R1,@VDPWD               ;COPY CHAR IN PAB DATA BUFFER
           INC     R5                      ;INCREMENT SOURCE PTR
           INC     R8                      ;INCREMENT DESTINATION PTR
           DEC     R2                      ;NEXT CHAR
           JNE     $5A02
$5A24      NEG     R2                      ;NUMBER OF TRAILING SPACES
           AI      R2,10                   ;NUMBER OF CHARS IN FILENAME
           SWPB    R2
$5A2C      LVWA    R0                      ;(BEG OF PAB DATA BUFFER)
           MOVB    R2,@VDPWD               ;WRITE STRING LENGTH BYTE
           LVWA    R8
           MOV     R7,R1                   ;FILE TYPE + PROTECTION
           BL      @INT2FL                 ;MAKE IT A FLOAT NUMBER
           MOV     R6,R1                   ;FILE SIZE IN SECTORS, INCLUDING FDR
           BL      @INT2FL                 ;MAKE IT A FLOAT NUMBER
           MOV     R3,R1                   ;RECORD LENGTH
           BL      @INT2FL                 ;MAKE IT A FLOAT NUMBER
           MOV     @PAB^,R8                ;GET PAB PTR
           AI      R8,5                    ;POINT TO CHARACTER COUNT
           LI      R0,>2600                ;ALWAYS 38 BYTES
           LVWA    R8
           MOVB    R0,@VDPWD               ;WRITE # OF CHARACTERS IN RECORD
           B       @VRETN                  ;RETURN TO CALLER
;--------------------------------------------------------------------------------------------
;
; -- C N T S E C --
;
;   THIS ROUTINE COUNTS FREE SECTORS IN VIB BITMAP
;
;--------------------------------------------------------------------------------------------
CNTSEC     AI      R8,46   
           LI      R2,200                  ;BITMAP SIZE
           CLR     R3                      ;FREE SECTORS COUNTER
           LVRA    R8
$5A78      MOVB    @VDPRD,1                ;GET A BYTE FROM BITMAP
           AI      R1,256
           SRL     R1,8
           JEQ     $5A9C                   ;WAS >FF: NO FREE SECTORS, NEXT BYTE
           DEC     R1                      ;WAS IT >00?
           JNE     $5A8E                   ;NO: COUNT BITS
           AI      R3,8                    ;YES: 8 MORE FREE SECTORS
           JMP     $5A9C                   ;NEXT BYTE
$5A8E      LI      R0,8                    ;8 BITS PER BYTE
$5A92      SRL     R1,1                    ;TEST A BIT
           JOC     $5A98                   ;WAS 1: SECTOR IS USED
           INC     R3                      ;WAS 0: ONE MORE FREE SECTOR
$5A98      DEC     R0                      ;NEXT BIT IN BYTE
           JNE     $5A92                   ;MORE TO COME
$5A9C      DEC     R2                      ;NEXT BITMAP BYTE
           JNE     $5A78                   ;MORE TO COME
           RT
;----------------------------------------------------
; FUNCTION:     FDVFCB
; DESCRIPTION:  find drive in file control blocks
; CALLED BY:    OPNDIR,CLSDIR,RDDIR
; CALLS:        LV$R8
;----------------------------------------------------
FDVFCB     MOV     *R11+,R10
           MOV     R11,R5                  ;SAVE 2 RETURNS
           MOV     @FDR^,R8                ;"TOP OF MEM" WORD IN VDP BUFFERS HEADER
           AI      R8,3                    ;POINT TO MAX # OF FILES
           LVRA    R8
           MOVB    @VDPRD,R2               ;GET # OF FILES
           SRA     R2,8                    ;MAKE IT A WORD
           AI      R8,6                    ;POINT TO DRIVE # IN FILE CTRL BLOCK
           LI      R3,>0020                ;FILENAME BEGIN WITH SPACE (ILLEGAL: FLAG FOR DIR)
           MOVB    R6,R3                   ;ADD DRIVE #
$5AC4      BL      @LV$R8                  ;READ 2 BYTES FROM VDP AT R8 INTO R0
           C       R3,R0                   ;MATCH WITH THAT CONTROL BLOCK?
           JEQ     $5ADE                   ;YES
           ANDI    R0,>00FF                ;KEEP ONLY FIRST CHAR OF FILENAME
           JNE     $5AD4                   ;VALID FILENAME: A FDR IS LOADED HERE
           MOV     R8,R7                   ;THIS SPACE IS FREE: SAVE PTR
$5AD4      AI      R8,>0206                ;POINT TO NEXT FILE CONTROL BLOCK
           DEC     R2                      ;NEXT FILE
           JNE     $5AC4                   ;MORE TO COME
           B       *R10                    ;NOT FOUND: RETURN TO ADDRESS PASSED IN DATA WORD
$5ADE      INC     R8                      ;DRIVE MATCHES: POINT TO FDR
           MOV     R8,@FDR^                ;SAVE PTR
           B       *R5                     ;RETURN TO CALLER AFTER DATA WORD
;------------------------------------------
; FUNCTION:     INT2FL
; DESCRPIPTION:     write an integer in floating point format
; CALLED BY:    RDDIR
;------------------------------------------
INT2FL     LI      R2,>0800
           MOVB    R2,@VDPWD               ;SIZE=8
           MOV     R1,R5                   ;INTEGER IS IN R1: SAVE IT FOR SIGN PROCESSING
           ANDI    R1,>7FFF                ;CLEAR SIGN BIT
           CI      R1,100                  ;IS IT LESS THAN 100?
           JL      $5B08                   ;YES
           CLR     R0                      ;100 OR OVER
           LI      R4,100
           DIV     R4,R0                   ;DIVIDE BY 100
           ORI     R0,>4100                ;ADD EXPONENT 2 TO HUNDRETHS
           JMP     $5B12
$5B08      MOV     R1,R0                   ;IS IT 0?
           JEQ     $5B10                   ;YES: EXPONENT IS 0
           ORI     R0,>4000                ;NO: ADD EXPONENT 1
$5B10      CLR     R1                      ;NEXT DIGITS WILL BE 0
$5B12      MOV     R5,R5                   ;TEST SIGN BIT
           JLT     $5B18                   ;NEGATIVE
           JMP     $5B1A                   ;POSITIVE OR ZERO
$5B18      NEG     R0                      ;NEGATE FIRST WORD
$5B1A      MOVB    R0,@VDPWD               ;WRITE EXPONENT TO VDP AT PRESET ADDRESS
           SWPB    R0
           MOVB    R0,@VDPWD               ;WRITE FIRST FIRST 2 DIGITS (OR HUNDRETHS)
           SWPB    R1
           MOVB    R1,@VDPWD               ;WRITE LAST 2 DIGITS (IF ANY)
           LI      R2,5                    ;THE REMAINING BYTES ARE ALL 0 WITH INTEGERS
$5B2E      MOVB    R2,@VDPWD               ;WRITE 0 TO VDP
           DEC     R2                      ;NEXT BYTE
           JNE     $5B2E                   ;MORE TO DO
           RT
;----------------------
; Floating point format
; ---------------------
; Float numbers are 8 bytes long: EE 12 34 56 78 9A BC
; EE is the exponent in radix 100 (not in radix 10 as usual!). It is biased
; by 64: >40=0, 41=1 (i.e *100), >42=2 (i.e * 10,000) >3F= -1 (i.e /100), etc
;
; 12 ... BC are the mantissa in binary coded decimal: each byte encodes two
; decimal digits from 00 to 99
;
; For negative numbers, the first word is negated
; For zero, the first word is >0000 the others are irrelevant
;
; Examples: 40 08 00 00 00 00 00 00 is 8.0
;           41 02 37 00 00 00 00 00 is 255.0 (>37 hex = 55 decimal)
;           BF F8 00 00 00 00 00 00 is -8.0
;           43 01 02 03 04 05 06 07 is 1020304.050607
;--------------------------------------
;---------------------------------------
; Subprogram >10: sector R/W
; --------------
; >834A: (n/a)      <--- sector #
; >834C: drive #
; >834D: R/W code (write if >00)
; >834E: VDP buffer
; >8350: sector #   <--- error code
;---------------------------------------
SECTIO     MOV     R11,R7
           BL      @INIT                   ;PREPARE DISK OPERATIONS
           MOV     @ERRFLG,@SEC#           ;COPY SECTOR #
;-------------------------------------------------------
; FUNCTION:     SEC$RW
; DESCRIPTION:  Sector read/write
; CALLED BY:    RWSECT,SECTIO
; CALLS:        LVWAR2,LVRAR2,CFCMD
;-------------------------------------------------------
SEC$RW     LI      R4,10                   ;SET RETRY COUNTER IN R4 (HIGH BYTE = 0 AND USED AS CONSTANT)
$RETRY     CB      @C.STAT,@H51
           JNE     SEC$RW10
           BL      @RESET
SEC$RW10   MOVB    R4,@ERRFLG              ;CLEAR ERROR 
           MOVB    @DRV#,R8                ;GET DRIVE #
           SRL     R8,8                    ;CONVERT TO WORD
           DEC     R8                      ;CONVERT TO ZERO-BASED
           SLA     R8,1
           A       @VIB^,R8
           AI      R8,DIBS+2               ;POINT TO VIRTUAL DRIVES
           BL      @LV$R8                  ;GET VIRTUAL DRIVE # INTO R0
           DEC     R0                      ;CONVERT FROM 1-BASED TO 0-BASED
           MOV     R0,R5                   ;R0 IS USED BY "BUSY?" SBR
           MPY     @D1600,R5               ;MPY BY VIRTUAL DRIVE OFFSET
           A       @SEC#,R6
           JNC     $4110
           INC     R5
$4110      BL      @BUSY?
           MOVB    @DRV#,R1                ;GET DRIVE #
           BL      @SETLBA!
           DATA    R5
           MOV     @BUF^,R2                ;DATA BUFFER ADDRESS
           MOVB    @IORQST,R0              ;READ OR WRITE ?
           JEQ     $425C                   ;WRITE
;
; -- R E A D --
;
           BL      @LVWAR2                 ;READ: SET VDP TO WRITE
           BL      @CFCMD                  ;SEND COMMAND
H20        DATA    >2000                   ;READ SECTOR
           BL      @DRQ?                   ;WAIT FOR DATA REQUEST
           LI      R6,256                  ;256 BYTES / SECTOR
$4214      MOVB    @C.DATAR,@VDPWD         ;READ BYTE
           DEC     R6
           JNE     $4214                   ;NEXT BYTE
           MOVB    @C.STAT,R0
           ANDI    R0,>0100                ;TEST STATUS BITS
           JEQ     $4270                   ;NO ERROR
           BL      @RETRY                  ;GRACEFUL ERROR 23
           DATA    >2300
;
; -- W R I T E --
;
$425C      BL      @LVRAR2                 ;WRITE: SET VDP TO READ
           BL      @CFCMD                  ;SEND COMMAND
H30        DATA    >3000                   ;WRITE SECTOR
           BL      @DRQ?
           LI      R6,256                  ;256 BYTES / SECTOR
$426C      MOVB    @VDPRD,@C.DATAW         ;GET A BYTE FROM DATA BUFFER
           DEC     R6
           JNE     $426C                   ;NEXT
           MOVB    @C.STAT,R0
           ANDI    R0,>0100                ;TEST STATUS BITS
           JEQ     $4270                   ;NO ERROR
           BL      @RETRY                  ;GRACEFUL ERROR 23
           DATA    >3300
$4270      B       @VRETN                  ;RETURN
      
;----------------------------
; Subprogram >11: format disk
; ---------------------------
; >834A: (n/a)      <--- # of sectors on disk
; >834C: DSR+drive # (DSR >0x old version >1x new, >2x unknown)
; >834D: # of tracks
; >834E: VDP buffer
; >8350: density    <--- error code
; >8351: # of sides <--- ditto
;---------------------------------------
FMTDSK     MOV     R11,R7
           BL      @INIT                   ;PREPARE DISK OPERATIONS
           CLR     @ERRFLG
           MOV     @D1600,@SEC#
           LI      11,32*256
           MOVB    11,@SECTRK              ;PASS # OF SECTORS PER TRACK
           B       @VRETN                  ;RETURN


;---------------------------------------
; Subprogram >12: file (un)protect
; --------------
; >834C: drive #
; >834D: protect code (>00 unprotect)
; >834E: ptr to filename
; >8350: (n/a)      <--- error code
;---------------------------------------
PROTCD     EQU     >834D
FNMPTR     EQU     >834E
PROT       MOV     R11,R7
           BL      @INIT                   ;PREPARE DISK OPERATIONS
           MOVB    @PROTCD,R0              ;GET PROTECTION CODE
           ANDI    R0,>0800                ;KEEP THE BIT THAT WILL BE NEEDED
           VPSH    $R0                     ;SAVE R0 ON STACK
           MOV     @FNMPTR,R0              ;GET POINTER TO FILENAME
           CALL    FDR2BF                  ;LOAD FDR IN VDP BUFFER
           VPOP    $R2                     ;RETRIEVE OLD R0, IN R2
           BL      @LV$FDR                 ;READ TWO BYTES IN R0 FROM TOP OF FDR + OFFSET
           DATA    12                      ;FILE STATUS BYTE
           ANDI    R0,>F700                ;CLEAR PROTECTION FLAG
           SOCB    R2,R0                   ;SET IT IF NEEDED
           LVWA    R8
           MOVB    R0,@VDPWD               ;WRITE BACK FILE STATUS TO FDR
MODFDR     MOV     @FDR^,R8                ;FDR ADDRESS IN VDP MEM
           LVRA    R8
           MOVB    @VDPRD,R0               ;GET DRIVE # IN CTRL BLOCK
           ORI     R0,>8000                ;FLAG IT
           LVWA    R8
           MOVB    R0,@VDPWD               ;WRITE IT BACK
           B       @UPFDRD                 ;UPDATE FDR, LOAD VIB
;---------------------------------
; Subprogram >13: file rename
; --------------
; >834C: drive #
; >834E: ptr to new name
; >8350: ptr to old name <--- error code
;---------------------------------
NEWNAM     EQU     >834E
OLDNAM     EQU     >8350
RENAME     MOV     R11,R7
           BL      @INIT                   ;PREPARE DISK OPERATIONS
           MOV     @NEWNAM,R0              ;GET PTR TO NEW FILENAME
           VPSH    $R0                     ;SAVE R0 ON STACK
           MOV     @OLDNAM,R0              ;GET PTR TO OLD FILENAME
           CALL    FDR2BF                  ;PUT FDR IN VDP BUFFER
           BL      @RMFDR                  ;REMOVE FDR PTR FROM SECTOR 1
           BL      @LV$FDR                 ;GET 2 BYTES FROM FDR
           DATA    12                      ;FILE STATUS BYTE
           ANDI    R0,>0800                ;PROTECTED?
           JEQ     $5BDC                   ;NO
           BL      @UPDERR                 ;YES: RETURN WITH ERROR
           DATA    >2000                   ;"WRITE PROTECTED"
$5BDC      BL      @LV$FDR                 ;GET 2 BYTES FROM FDR
           DATA    >FFFC                   ;SECTOR # OF FDR
           MOV     R0,R1
           VPOP    $R0                     ;RETRIEVE R0 FROM STACK
           VPSH    $R1                     ;SAVE R1 ON STACK. SECTOR # OF FDR
           BL      @UPDCMP                 ;WRITE DRIVE # AND FILENAME IN COMPARE BUFFER
           CALL    FDRFFL                  ;FIND FDR FROM FILENAME
           MOV     R4,R4                   ;FOUND?
           JEQ     $5C6E                   ;YES: RETURN WITH "FILE ERROR" (NAME ALREADY EXIST)
           BL      @INFDR1                 ;INSERT A FDR IN SECTOR 1
           VPOP    $R4                     ;RETRIEVE SECT # OF FDR IN R4
           LVWA    R8
           MOVB    R4,@VDPWD               ;WRITE SECTOR # OF FDR
           SWPB    R4
           MOVB    R4,@VDPWD
           SWPB    R4                      ;SECTOR #
           SETO    R2                      ;CODE FOR READ
           CLR     R5                      ;BUFFER OFFSET: VDP AT >8356
           CALL    RWFDR$R5                ;READ FDR
           MOV     R5,R1
           MOV     @VIB^,R0
           AI      R0,>0101
           DEC     R1
           BL      @CPFNFD                 ;COPY FILENAME IN COMPARE BUFFER TO FDR
           CLR     R2                      ;CODE FOR WRITE
           CALL    RWSEC$R7                   ;WRITE FDR (WITH NEW NAME IN IT)
           CALL    RWBUF1                  ;WRITE SECTOR 1 (WITH NEW FDR PTR IN IT)
           MOV     @FDR^,R1                ;FDR PTR
           LVWA    R1
           MOVB    R4,@VDPWD               ;CLEAR FIRST CHAR OF FDR IN BUFFER
           B       @VRETN                  ;RETURN TO CALLER
;----------------------------------------------
; FUNCTION:     FDR2BF
; DESCRIPTION:  put FDR in VDP buffer
; CALLED BY:    PROT,RENAME
; CALLS:        UPDCMP,FNFDRV,FNFDRD,SCRTCH
;----------------------------------------------
FDR2BF     CLR     R6
           MOVB    @DRV#,R6                ;GET DRIVE #
           BL      @UPDCMP                 ;UPDATE FILENAME COMPARE BUFFER
           CALL    FNFDRV                  ;LOOK IF FDR ALREADY IN VDP BUFFER
           CALL    FNFDRD                  ;FILE FDR ON DISK
           MOV     R4,R4                   ;FOUND?
           JEQ     $5C74                   ;YES
$5C6E      BL      @SCRTCH                 ;NO: RETURN WITH ERROR CODE IN >8350
           DATA    >E000                   ;"FILE ERROR"
$5C74      B       @VRETN                  ;RETURN TO CALLER
;
;-------------------------------------
; Subprogram >14: file raw read
; --------------
; >834C: drive #                          <--- >00
; >834D: # of sectors (>00=get file info) <--- sectors read
; >834E: ptr to filename
; >8350: file info buffer (>83xx)         <--- error code
;                              |
; >83xx  : VDP buffer       <--'
; >83xx+2: first sector # (total # of sect when get file info)
; >83xx+4: status flag
; >83xx+5: recs/sector
; >83xx+6: eof offset
; >83xx+7: rec size
; >83xx+8: # of recs
;-------------------------------------
RAWRD      MOV     R11,R7
           BL      @INIT                   ;PREPARE DISK OPERATIONS
           BL      @LDDVFN                 ;LOAD FILENAME AND PTRS
           BL      @FNFDRF                 ;FIND FILE FDR, LOAD SOME INFO
           MOV     R2,R2                   ;# OF SECTORS TO READ
           JEQ     $5CC6                   ;>00: GET FILE INFO
           S       R3,R0                   ;SECTORS IN FILE -  FIRST SECTOR TO READ
           JGT     $5C92                   ;IN FILE
           CLR     R2                      ;PAST EOF
           JMP     $5CC0
$5C92      C       R2,R0                   ;SECTORS PAST FIRST ONE VS SECTORS TO LOAD
           JL      $5C98
           MOV     R0,R2                   ;LOAD WHAT'S LEFT
$5C98      VPSH    $R2
$5C9E      VPSH    $R2+$R3+$R7
           CALL    RDSEC$OFS                  ;READ SECTOR FROM OFFSET IN FILE
           VPOP    $R2+$R3+$R7             ;RETRIEVE R2, R3, R7
           INC     R3                      ;NEXT SECTOR
           AI      R7,>0100                ;INCREMENT VDP BUFFER PTR BY 256 BYTES
           DEC     R2                      ;MORE TO DO?
           JNE     $5C9E                   ;YES
$5CBA      VPOP    $R2                     ;RETRIEVE R2 (# OF SECTORS READ)
$5CC0      MOV     R2,@DRV#                ;UPDATE # OF SECTORS IN PARAMETERS
           JMP     $5CDA
$5CC6      MOV     R0,*R4+                 ;GET FILE INFO: SECTORS IN FILE
           DECT    R8
           INCT    R2                      ;COPY 2 BYTES (STATUS + RECS/SECTOR)
           BL      @V2SCR                  ;FROM VDP AT R8 TO FILE INFO STRUCTURE
           LI      R2,4                    ;COPY 4 BYTES
           A       R2,8
           BL      @V2SCR                  ;EOF OFFSET, REC LEN, # OF RECS (OR # OF SECT)
$5CDA      CALL    UPFDRD                  ;UPDATA FDR (+ DATA) IF NEEDED, READ VIB
           CLR     @ERRFLG                 ;CLEAR ERROR FLAG
           B       @VRETN                  ;RETURN TO CALLER
;--------------------------------------
; Subprogram >15: file raw write
; --------------
; >834C: drive #                                  <--- >00
; >834D: # of sectors (>00=create file from info) <--- # of sectors written
; >834E: ptr to filename
; >8350: file info buffer (>83xx)                 <--- error code
;                              |
; >83xx  : VDP buffer       <--'
; >83xx+2: first sector # (total # of sectors when creating file)
; >83xx+4: status flag
; >83xx+5: recs/sector
; >83xx+6: eof offset
; >83xx+7: rec size
; >83xx+8: # of recs
;--------------------------------------
RAWWR      MOV     R11,R7
           BL      @INIT                   ;PREPARE DISK OPERATIONS
           BL      @LDDVFN                 ;LOAD DRIVE + FILENAME, + A FEW INFO
           JEQ     $583A                   ;SECTORS TO WRITE=0: CREATE FILE
           BL      @FNFDRF                 ;FIND FILE FDR
           VPSH    $R2                     ;SAVE R2 (# OF SECTORS TO WRITE)
$5CFE      VPSH    $R2+$R3+$R7             
           CALL    WRSEC$OFS               ;WRITE SECTOR FROM OFFSET IN FILE
           VPOP    $R2+$R3+$R7             ;RETRIEVE R2, R3, R7
           INC     R3                      ;NEXT SECTOR
           AI      R7,256                  ;INCREMENT VDP PTR BY 256 BYTES
           DEC     R2                      ;MORE TO DO?
           JNE     $5CFE                   ;YES
           JMP     $5CBA                   ;UPDATE # OF SECTORS WRITTEN, IN PARAM. THEN RETURN
$583A      CALL    FNFDRV                  ;FIND FILE FDR IN VDP BUFFERS
           CALL    FNFDRD                  ;FIND FILE FDR ON DISK
           CALL    CREAT2                  ;CREATE FILE
           VPOP    $R4                     ;RETRIEVE R4 (FILE INFO PTR)
           MOV     @FDR^,R8                ;FDR PTR
           INCT    R4                      ;SKIP 2 BYTES
           MOV     *R4+,R3                 ;# OF SECTOR TO CREATE
           AI      R8,10                   ;SKIP FILENAME
           BL      @SCR2V                  ;WRITE 2 BYTES IN VDP AT R8+2
           DATA    2                       ;I.E. STATUS + RECS/SECTOR
           BL      @SCR2V                  ;WRITE 4 BYTES IN VDP AT NEW R8+4
           DATA    4                       ;I.E. EOF OFFSET, REC LEN, # OF RECS (OR # OF SECT)
           DEC     R3                      ;OFFSET = # OF SECTORS-1 (STARTS FROM 0)
           JLT     $5872                   ;CREATE AN EMPTY FILE, FDR ONLY
           CALL    APNSEC                  ;APPEND ENOUGH SECTORS TO REACH OFFSET
$5872      B       @MODFDR                 ;MODIFY FDR, WRITE IT, LOAD VIB, RETURN
;
; CALL FORMAT(<drive#>)
;
;   >9D "F" "O" "R" "M" "A" "T" >B7 >C8 >01 "2" >B6 ??
;
BASTKN^    EQU     >832C
CURTKN     EQU     >8342
;
FORMAT     MOV     R11,R7                  ;SAVE RETURN ADDRESS
           BL      @INIT                   ;PREPARE DISK OPERATION
           MOV     @BASTKN^,R8             ;PTR TO NEXT BASIC TOKEN
           AI      R8,8                    ;SKIP "FORMAT"
           LVRA    R8
           LI      R11,VDPRD
           CB      *R11,@UNQUOTED          ;>C8?
           JNE     FMT99                   ;NO --\
           MOVB    *R11,R0                 ;S/B LENGTH OF UNQUOTED TEXT
           JEQ     FMT99                   ;NO --\
           INC     R8
           SRL     R0,8
           CI      R0,4                    ;TOO LARGE?
           JH      FMT99                   ;YES --\
           CLR     R1                      ;START ASCII TO BINARY CONVERSION
FMT10      MPY     @D10,R1                 ;SHIFT DIGITS LEFT BY 1 POSITION (R1/R2)
           MOVB    *R11,R1                 ;GET DIGIT
           CB      R1,@H30                 ;TEST AGAINST "0"
           JL      FMT99                   ;INVALID DIGIT --\
           CB      R1,@H39                 ;TEST AGAINST "9"
           JH      FMT99                   ;INVALID DIGIT --\
           SLA     R1,4                    ;CONVERT TO BINARY
           SRL     R1,12
           A       R2,R1                   ;ADD TO VALUE
           DEC     R0                      ;LAST DIGIT?
           JNE     FMT10                   ;NO --/
           CB      *R11,@HB6               ;S/B RIGHT PARENS.
           JNE     FMT99                   ;IT'S NOT --\
           MOV     R1,R2
           DEC     R2                      ;CONVERT VOLUME NUMBER TO 0 BASED
           MPY     @D1600,R2               ;CALCULATE VOLUME STARTING SECTOR (R2/R3)
           BL      @BUSY?                  ;WAIT WHILE IDE IS BUSY.
           BL      @SETLBA!
           DATA    R2
           BL      @CFCMD                  ;SEND COMMAND
           DATA    >3000                   ;"WRITE SECTOR"
           BL      @DRQ?
           LI      R4,C.DATAW
           LI      R5,FMTDATA
           MOVB    *R5+,*R4                ;'V'
           MOVB    *R5+,*R4                ;'O'
           MOVB    *R5+,*R4                ;'L'
           LVRA    R8                      ;POINT TO VOLUME NUMBER
           LI      R11,VDPRD               ;VDP READ DATA PORT
           MOVB    *R11,R0                 ;LENGTH OF VOLUME NUMBER
           SRL     R0,8                    ;CONVERT TO WORD
           A       R0,R8                   ;POINT TO TOKEN FOLLOWING VOLUME #
           INCT    R8
           LI      R1,FMTDATAL-3           ;LENGTH OF VIB INFO LESS "VOL" TEXT
           S       R0,R1                   ;DECREMENT LENGTH OF VIB INFO BY LENGTH OF VOLUME NAME
           A       R0,R5                   ;POINT TO REMAINDER OF VIB INFO
FMT30      MOVB    *R11,*R4                ;COPY VOLUME #
           DEC     R0
           JNE     FMT30
FMT40      MOVB    *R5+,*R4                ;COPY REMAINDER OF VIB INFO
           DEC     R1
           JNE     FMT40
           BL      @ZFILL                  ;FILL WITH 36 ZEROS
           DATA    36
           MOVB    @H03,*R4                ;SET USED SECTORS
           BL      @ZFILL                  ;FILL REST OF BITMAP WITH ZEROS
           DATA    199
           INC     R3                      ;POINT TO DIRECTORY
           JNC     FMT50
           INC     R2
FMT50      BL      @BUSY?                  ;WAIT WHILE BUSY
           MOVB      @H0100,@C.SCCNTW
           MOVB      R3,@C.LBA8W
           SWPB      R3
           MOVB      R3,@C.LBA0W
           ORI       R2,>E000
           MOVB      R2,@C.LBA24W
           SWPB      R2
           MOVB      R2,@C.LBA16W
           BL      @CFCMD                  ;SEND COMMAND
           DATA    >3000                   ;"WRITE SECTOR"
           BL      @DRQ?                   ;WAIT FOR DRQ
           BL      @ZFILL                  ;CLEAR DIRECTORY
           DATA    256
           B       @$5DA0
FMT99      B       @$5DAA
FMTDATA    TEXT    'VOL       '
           BYTE    >06,>40,>20,>44,>53,>4B,>20,>28,01,01
FMTDATAL   EQU     $-FMTDATA
HB6        BYTE    >B6
           EVEN
;
;
;
ZFILL      MOV     *R11+,R0
           CLR     R1
ZFILL10    MOVB    R1,@C.DATAW
           DEC     R0
           JNE     ZFILL10
           RT
;---------------------------------------
; Subprogram FILES: number of files
;   THIS ROUTINE EXPANDS NUMBER OF FILES
;
;   >9D "F" "I" "L" "E" "S" >B7 >C8 >01 "n" >B6 ?? 
;---------------------------------------
FILES      MOV     R11,R7
           BL      @INIT                   ;PREPARE DISK OPERATION
           MOV     @BASTKN^,R8             ;PTR TO NEXT BASIC TOKEN
           AI      R8,7                    ;SKIP "FILES"
           BL      @LV$R8                  ;GET NEXT TWO BYTES IN R0
           CI      R0,>C801                ;>C8=UNQUOTED STRING, SIZE=1 CHAR
           JNE     FMT99                   ;RETURN (WITH ERROR) IF DIFFERENT --/
           INCT    R8                      ;INCREMENT POINTER
           BL      @LV$R8                  ;GET NEXT TWO BYTES
           SWPB    R0
           AI      R0,>49D0                ;SUBTRACT >B630: # OF FILES  >B6=CLOSED PARENTHESIS
           CI      R0,9                    ;ONLY 9 FILES ALLOWED IN BASIC !
           JH      $5DAA                   ;RETURN WITH ERROR IF MORE
           SWPB    R0
           MOVB    R0,@DRV#                ;PUT NEW # OF FILES IN SCRATCH-PAD MEMORY
           CALL    $FILE2                  ;SUBPROGRAM >16 (PRIVATE ENTRY POINT)
           MOVB    @ERRFLG,@ERRFLG         ;TEST RESULT
           JNE     $5DAA                   ;ERROR
           MOV     @BASTKN^,R8              ;OK: GET PTR TO BASIC TOKEN
           AI      R8,12                   ;SKIP THE WHOLE STATEMENT
           JMP     $5DA0
;
; CALL MOUNT(<drive#>,<disk#>,<virtual disk#>)
; 
;   Example: CALL MOUNT(2,127)
;
;   >9D "M" "O" "U" "N" "T" >B7 >C8 >01 "2" >B3 >C8 >01 "3" >B3 >C8 >03 "127" >B6 ?? ?? ??
; CALL MOUNT("<drive name>","<disk name>.<volume name>");
;   Example: CALL MOUNT("DSK2","HD3.VOL127")
;
UNQUOTED   BYTE    >C8
COMMA      BYTE    >B3
D10        DATA    10
H39        BYTE    >39
           EVEN

MOUNT      MOV     R11,R7
           BL      @INIT
           MOV     @BASTKN^,R8              ;PTR TO NEXT BASIC TOKEN
           AI      R8,7                    ;SKIP CALL TOKEN AND TEXT "MOUNT("
           LVRA    R8                      ;GET VIRTUAL DISK NBR
           LI      R11,VDPRD
           CB      *R11,@UNQUOTED          ;S/B UNQUOTED TOKEN
           JNE     $5DAA                   ;RETURN (WITH ERROR) IF DIFFERENT
           CB      *R11,@H01               ;S/B LENGTH OF 1
           JNE     $5DAA                   ;RETURN (WITH ERROR) IF DIFFERENT
           MOVB    *R11,R6                 ;S/B 1..3
           SLA     R6,4                    ;CONVERT TO BINARY
           SRL     R6,12
           CI      R6,3                    ;ONLY THREE DRIVES ALLOWED
           JH      $5DAA                   ;RETURN (WITH ERROR) IF DIFFERENT
           DEC     R6
           CB      *R11,@COMMA             ;S/B COMMA
           JNE     $5DAA         
           CB      *R11,@UNQUOTED          ;S/B UNQUOTED STRING
           JNE     $5DAA
           MOVB    *R11,R2
           SLA     R2,4
           SRL     R2,12                   ;S/B LENGTH (1..4)
           AI      R8,6                    ;UPDATE BASIC TOKEN POINTER
           A       R2,R8
           CLR     R0
MOUNT10    MPY     @D10,R0
           MOVB    *R11,R0
           SLA     R0,4
           SRL     R0,12
           A       R1,R0
           DEC     R2
           JNE     MOUNT10
           AI      R8,3
           JMP     BASEXIT
;
; CALL UNMOUNT(1)
;
UNMOUNT    MOV     R11,R7
           BL      @INIT
           MOV     @BASTKN^,R8              ;PTR TO NEXT BASIC TOKEN
           AI      R8,9                    ;SKIP TEXT "UNMOUNT"
           BL      @LV$R8                  ;GET NEXT TWO BYTES IN R0
           CI      R0,>C801                ;>C8=UNQUOTED STRING, SIZE=1 CHAR
           JNE     $5DAA                   ;RETURN (WITH ERROR) IF DIFFERENT
           INCT    R8                      ;INCREMENT POINTER
           BL      @LV$R8                  ;GET NEXT TWO BYTES
           SWPB    R0                       
           AI      R0,->B630               ;SUBTRACT >B630: # OF FILES  >B6=CLOSED PARENTHESIS
           CI      R0,3                    ;ONLY 9 FILES ALLOWED IN BASIC !
           JH      $5DAA                   ;RETURN WITH ERROR IF MORE
           MOV     R0,R6
           DEC     R6
           MOV     @BASTKN^,R8              ;OK: GET PTR TO BASIC TOKEN
           AI      R8,14                   ;SKIP THE WHOLE STATEMENT
BASEXIT    SLA     R6,1                    ;S/B R0 = VIRTUAL DISK NUMBER, R6 DRIVE #
           A       @VIB^,R6
           AI      R6,DIBS+2
           LVWA    R6
           MOVB    R0,@VDPWD
           SWPB    R0
           MOVB    R0,@VDPWD
           CLR     R6
           CALL    RDVIB$R6                ;FLUSH VIB IN MEMORY (IF ANY)
           CALL    RDDIB                   ;READ VIB WITH DIB
           CALL    LVDIB                   ;COPY DIB TO VIB
           CALL    WRDIB                   ;WRITE VIB WITH UPDATED VIB
$5DA0      MOV     R8,@BASTKN^              ;UPDATE PTR
           SZCB    @CURTKN,@CURTKN         ;CLEAR CURRENT TOKEN
$5DAA      B       @VRETN
;---------------------------------------
; Subprogram >16: number of files
; --------------
; >834C: # of files
; >8350: (n/a)      <--- error code
; CALLS:        INIT,LV$R8
;---------------------------------------
NUMFIL     EQU     >834C
$FILES     MOV     R11,R7                  ;ENTRY POINT FROM ASSEMBLY
           BL      @INIT                   ;PREPARE DISK OPERATIONS
$FILE2     CLR     R0                      ;ENTRY POINT FROM "CALL FILES"
           MOVB    @NUMFIL,R0              ;GET # OF FILES
           JEQ     $5E94                   ;RETURN WITH ERROR
           MOV     @FDR^,R8                ;PTR TO "END OF BUFFER" WORD
           AI      R8,3                    ;POINT TO "# OF FILES" BYTE
           CLR     R3
           LVRA    R8
           MOVB    @VDPRD,R3               ;GET CURRENT # OF FILES
           LI      R5,>0206                ;SIZE OF 1 FILE CONTROL BLOCK
           CB      R0,R3                   ;COMPARE REQUIRED WITH CURRENT
           JEQ     $5E8E                   ;SAME: RETURN WITH NO ERROR
           JLE     $5E30                   ;LESS
           MOV     R0,R6                   ;MORE FILES NEEDED
H1000      EQU     $+2
           CI      R0,>1000                ;MAXIMUM IS 16
           JH      $5E94                   ;RETURN WITH ERROR
           S       R3,R0                   ;HOW MANY TO ADD
           SRL     R0,8                    ;MAKE IT A WORD
           MPY     R5,R0                   ;# OF BYTES TO ADD
           MOV     R1,R4                   ;RESULT IN R0:R1
           NEG     R4
           MOV     @VHIMEM,R2              ;HIGHEST FREE ADDRESS IN VDP MEM
           MOV     R2,R0
           S       R1,R0                   ;WHAT IT WOULD BECOME
           CI      R0,>0800                ;IS THERE ROOM ENOUGH FOR VDP?
           JLT     $5E94                   ;NO: RETURN WITH ERROR
           MOV     R0,R1                   ;OK: NEW BASE
$5DFC      INC     R2                      ;INCREMENT SOURCE PTR
           INC     R0                      ;INCREMENT DESTINATION PTR
           LVRA    R2
           MOVB    @VDPRD,R3               ;GET A BYTE
           LVWA    R0
           MOVB    R3,@VDPWD               ;COPY A BYTE
           C       R2,R8                   ;DID WE COPY THE WHOLE HEADER?
           JNE     $5DFC                   ;NOT YET
           LVWA    R0
           MOVB    R6,@VDPWD               ;NEW # OF FILES
           CLR     R6
           S       R0,R2
$5E26      MOVB    R6,@VDPWD               ;CLEAR BYTE
           DEC     R2
           JNE     $5E26
           JMP     $5E64
;LESS FILES NEEDED
$5E30      LVWA    R8
           MOVB    R0,@VDPWD               ;NEW # OF FILES IN BUFFER HEADER
           S       R0,R3                   ;HOW MANY TO REMOVE
           SRL     R3,8                    ;MAKE IT A WORD
           MPY     R5,R3                   ;# OF BYTES TO REMOVE
           MOV     R4,R1                   ;RESULT IN R3:R4
           A       R8,R1                   ;NEW ADDRESS FOR BUFFER HEADER
           MOV     @VHIMEM,R2              ;HIGHEST FREE ADDRESS IN VDP MEM
$5E48      LVRA    R8
           MOVB    @VDPRD,R0               ;READ A BYTE
           LVWA    R1
           MOVB    R0,@VDPWD               ;WRITE BYTE BACK
           DEC     R1                      ;DECREMENT DESTINATION PTR
           DEC     R8                      ;DECREMENT SOURCE PTR
           C       R8,R2                   ;DID WE COPY THE WHOLE HEADER?
           JNE     $5E48                   ;NOT YET
$5E64      MOV     R1,@VHIMEM              ;NEW FIRST FREE ADDRESS
           MOV     R1,R8
$5E6A      INCT    R8                      ;POINT TO "END OF BUFFER" WORD
           BL      @LV$R8                  ;READ IT IN R0
           MOVB    @VDPRD,R1               ;GET CRU BASE BYTE
           CB      R12,R1                  ;SAME AS CURRENT CONTROLLER?
           JEQ     $5E8E                   ;YES: RETURN WITH NO ERROR
           A       R4,R0                   ;NO: COIN ADDRESS OF NEXT BUFFER
           LVWA    R8
           MOVB    R0,@VDPWD               ;WRITE NEW "END OF BUFFER" WORD
           MOV     R0,R8                   ;AND MAKE IT NEW ADDRESS
           SWPB    R0
           MOVB    R0,@VDPWD
           JMP     $5E6A                   ;NOW, TRY AGAIN
$5E8E      CLR     @ERRFLG                 ;CLEAR ERROR FLAG
           JMP     $5E98
$5E94      SETO    @ERRFLG                 ;SET ERROR FLAG
$5E98      B       @VRETN                  ;RETURN TO CALLER
;-----------------------------------------------------------
; FUNCTION:     UPDCMP
; DESCRIPTION:  write drive # and filename in compare buffer
; CALLED BY:    LDDVFN,RENAME
; CALLS:        CPFNFD(jmp)
;-----------------------------------------------------------
UPDCMP     CLR     @PAB^
           MOV     @VIB^,R1
           AI      R1,256                  ;PTR TO FILENAME COMPARE BUFFER
           LVWA    R1
           MOVB    R6,@VDPWD               ;WRITE DRIVE #
;---------------------------------------
; FUNCTION:     CPFNFD
; DESCRIPTION:  copy filename in compare buffer to FDR
; CALLED BY:    RENAME,UPDCMP(jmp)
;---------------------------------------
CPFNFD     LI      R2,10                   ;FILENAME MUST BE EXACTLY 10 CHARS
$5EB6      INC     R1                      ;NEXT CHAR IN COMPARE BUFFER
           LVRA    R0                      
           MOVB    @VDPRD,R3               ;GET 1 CHAR FROM FILENAME
           INC     R0                      ;NEXT CHAR IN PROVIDED FILENAME
           LVWA    R1
           MOVB    R3,@VDPWD               ;WRITE 1 CHAR TO COMPARE BUFFER
           DEC     R2
           JNE     $5EB6                   ;NEXT CHAR
           RT
;-----------------------------------------------
; FUNCTION:     LDDVFN
; DESCRIPTION:  load compare buffer and ptrs
; CALLED BY:    RAWRD,RAWWR
; CALLS:        UPDCMP
;-----------------------------------------------
LDDVFN     MOV     R11,R10
           CLR     R6
           MOVB    @DRV#,R6                ;DRIVE #
           MOV     @BUF^,R0                ;PTR TO FILENAME
           BL      @UPDCMP                 ;WRITE THEM IN COMPARE BUFFER
           MOVB    @ERRFLG,R4              ;FILE INFO STRUCTURE PTR
           SRL     R4,8
;           A       R9,R4                   ;MAKE IT A PAB ADDRESS
           AI      R4,WRKSP
           MOVB    @SECCNT,R0              ;# OF SECTORS (>00=GET FILE INFO)
           VPSH    $R4                     ;SAVE R4
           SRL     R0,8
           B       *R10                    ;EQ SET FOR GET FILE INFO
;-----------------------------------------------------------
; FUNCTION:     FNFDRF
; DESCRIPTION:  find file FDR
; CALLED BY:    RAWRD,RAWWR
; CALLS:        FNFDRV,FNFDRD,UPDERR,LV$FDR
;-----------------------------------------------------------
FNFDRF     VPSH    $R0+$R11                ;FIND FILE FDR
           CALL    FNFDRV                  ;FIND FILE FDR IN VDP BUFFERS
           CALL    FNFDRD                  ;FIND FDR ON DISK
           MOV     R4,R4                   ;FOUND ?
           JEQ     $5F16                   ;YES
           BL      @UPDERR                 ;NO: RETURN WITH ERROR
           DATA    >E000                   ;"FILE ERROR"
$5F16      BL      @LV$FDR                 ;GET TWO BYTES FROM FDR INTO R0
           DATA    >000E                   ;# OF SECTORS IN FILE
           VPOP    $R2+$R11                ;RETRIEVE R0 IN R2 (# OF SECT TO READ), AND R11
           VPOP    $R4                     ;RETRIEVE R4 (PTR TO FILE INFO STRUCTURE)
           MOV     *R4+,R7                 ;VDP BUFFER
           MOV     *R4,R3                  ;FIRST SECTOR
           RT
;-----------------------------------------------------------
; FUNCTION:     V2SCR
; DESCRIPTION:  copy VDP bytes to scratch-pad
; CALLED BY:    RAWRD
;-----------------------------------------------------------
V2SCR      LVRA    R8
$5F34      MOVB    @VDPRD,*R4+             ;READ BYTES INTO SCRATCH-PAD AT R4
           DEC     R2                      ;# OF BYTE IN R2
           JNE     $5F34                   ;NEXT BYTE
           RT
;-----------------------------------------------------------
; FUNCTION:     SCR2V
; DESCRIPTION:  copy scratch-pad bytes to VDP
; CALLED BY:    RAWWR
;
;          LI      R8,<VRAM>
;          LI      R4,<CPU RAM>
;          BL      @SCR2V
;          DATA    <OFFSET>
;-----------------------------------------------------------
SCR2V      MOV     *R11+,R2
           A       R2,R8
           LVWA    R8                      ;+ OFFSET IN DATA WORD
$5F48      MOVB    *R4+,@VDPWD             ;WRITE BYTE FROM SCRATCH-PAD AT R4
           DEC     R2                      ;# OF BYTES IN R2, WAS IN DATA WORD
           JNE     $5F48                   ;NEXT BYTE
           RT

;-----------------------------------------------------------
;
; -- S E T L B A ! --
;
;   THIS ROUTINE SETS UP THE IDE'S LBA REGISTER BASED OF
;   DWORD IN REGISTER PAIR.
;
;-----------------------------------------------------------
SETLBA!    MOVB    @H01,@C.SCCNTW          ;SET ONE SECTOR
           STWP    R0                      ;GET WORKSPACE
           A       *R11,R0                 ;MAKE POINTER TO 1ST REGISTER OF PAIR
           A       *R11+,R0
SETLBA10   SOCB    @HE000,*R0              ;SET LBA MODE BITS
           MOVB    *R0,@C.LBA24W
           SZCB    @HF000,*R0+             ;CLEAR LBA MODE BITS
           MOVB    *R0+,@C.LBA16W          ;SET REMAINDER OF LBA 
           MOVB    *R0+,@C.LBA8W
           MOVB    *R0+,@C.LBA0W
           RT

;----------------------------
; FUNCTION:     RESET
; DESCRIPTION:  RESET IDE
; CALLED BY:    SEC$RW,FMT
; CALLS:
;-------------------------------------------------------------
RESET      MOVB    @H04,@C.DCTLW           ;START IDE RESET
           NOP
           MOVB    @H00,@C.DCTLW           ;END IDE RESET
           RT

H0100      DATA    >0100
H01        EQU     H0100
H0800      DATA    >0800
HE5        BYTE    >E5
H51        BYTE    >51
           EVEN

;----------------------------
; FUNCTION:     RETRY
; DESCRIPTION:  CHECK RETRY COUNT; IF NOT EXHAUSTED THEN RETRY, ELSE ERROR
; CALLED BY:    SEC$RW,FMT
; CALLS:
;-------------------------------------------------------------
RETRY      DEC     R4                      ;GRACEFUL ERROR
           JEQ     EEXIT                   ;NO MORE TRIES: ERROR
           BL      @RESET
           MOVB    @H00,@ERRFLG            ;NO ERROR
$45A8      B       @$RETRY                 ;BACK TO SECTOR R/W ROUTINE
;-------------------------------------------------------------
; FUNCTION:     EEXIT
; DESCRIPTION:  exit with error
; CALLED BY:    SEC$RW,RETRY,FMT
; CALLS:        CFCMD
;-------------------------------------------------------------
EEXIT      MOV     *R11,R0
           MOVB    R0,@ERRFLG              ;PLACE ERR CODE IN >8350
           CI      R0,>0600                ;CHECK IF "DEVICE ERROR"
           JNE     $45C6                   ;NO: EXIT
           SETO    R0
           BL      @RESET
$45C6      B       @VRETN                  ;RETURN TO CALLER (ADDRESS FROM STACK)
BUSY?      MOVB    @C.STAT,R0
           JLT     BUSY?
           CZC     @H0100,R0
           JEQ     $4480                   ;BIT 0 = ERROR (GOTO ERR06 IF SET)
ERR06      BL      @EEXIT                  ;EXIT WITH ERROR CODE 6, RESETING ALL DRIVES
           DATA    >0600

DRQ?       MOVB    @C.STAT,R0
           JLT     DRQ?
           CZC     @H0800,R0
           JEQ     DRQ?
           CZC     @H0100,R0
           JNE     ERR06
$4480      RT

;-----------------------------------
; FUNCTION:     CFCMD
; DESCRIPTION:  send command to FDC from data word
; CALLED BY:    SEC$RW,FMT,EEXIT
;-----------------------------------
CFCMD      MOV     *R11+,R0
           MOVB    R0,@C.CMD
           RT
SLAST      EQU     $
           END