;
;*****************************************************************
;*****************************************************************
;**                                                             **
;**   B a s i c    D i s k   O p e r a t i n g   S y s t e m    **
;**                                                             **
;*****************************************************************
;*****************************************************************
;
selerror:
                                        ;report select error
        LEA     BX,seler                ;
;
;
goerr:
                                        ;BX = .errorhandler,call subr.
        JMP     w[bx]                   ;to subroutine
;
;
;
;       local subroutines for bios interface
;
move:
        ;move data length of length CL from source DX to
        ;destination given by BX
        push    cx
        mov     ch,0
        mov     si,dx
        mov     di,bx
rep     movs    al,al
        pop     cx
        ret
;
;
selectdisk:
        ;select the disk drive given by curdsk, and fill
        ;the base addresses curtrka - alloca, then fill
        ;the values of the disk parameter block
;
        MOV     cl,curdsk               ;current disk# to CL
                                        ;lsb of DL = 0 if not yet
                                        ;logged in
        CALL    seldskf                 ;BX filled by call
                                        ;BX = 0000 if error,
                                        ;otherwise disk headers
        cmp     bx,0
        jz      ret4                    ;rz
        MOV     DX,[bx]
        add     bx,2
        MOV     cdrmaxa,BX
        add     bx,2
        MOV     curtrka,BX
        add     bx,2
        MOV     curreca,BX
        add     bx,2
                                        ;DX still contains .tran
        xchg    bx,dx
        MOV     tranv,bx                ;.tran vector
        LEA     BX,buffa                ;DX= source for move, BX=dest
        MOV     CL,addlist
        CALL    move                    ;addlist filled
                                        ;now fill the disk
                                        ;parameter block
        MOV     dx,dpbaddr
        LEA     BX,sectpt               ;BX is destination
        MOV     CL,dpblist
        CALL    move                    ;data filled
                                        ;set single/double map mode
        MOV     al,byte ptr maxall+1    ;largest allocation number
        LEA     BX,single
        MOV     b[bx],true                      ;assume a=00
        OR      AL,AL
        JZ      retselect
                                        ;high order of maxall not
                                        ;zero, use double dm
        MOV     b[bx],false
retselect:
        MOV     AL,true
        OR      AL,AL
ret4:   RET                             ;select disk function ok
;
home:
        ;move to home position, then offset to start of dir
        CALL    homef
                                        ;first directory pos. selected
        XOR     AX,AX                   ;constant zero to accumulator
        MOV     BX,curtrka
        MOV     [bx],ax                         ;curtrk=0000
                                        ;curtrk, currec both set to 0
        RET
;
rdbuff:
        ;read buffer and check if ok
        CALL    readf                   ;current drive, track,....
        jmps    diocomp                 ;check for i/o errors
;
wrbuff:
        ;write buffer and check condition
        ;write type (wrtype) is in register CL
        ;wrtype = 0 => normal write operation
        ;wrtype = 1 => directory write operation
        ;wrtype = 2 => start of new block
        CALL    writef                  ;current drive, track, ...
diocomp:                                ;check for disk errors
        OR      AL,AL
        jz      ret4                    ;rz
        LEA     BX,pererr
        JMP     goerr
;
seekdir:
        ;seek the record containing the current dir entry
        MOV     BX,dcnt                 ;directory counter to BX
        MOV     CL,dskshf
        shr     bx,cl                   ;value to BX
        MOV     arecord,BX
        MOV     drec,BX                 ;ready for seek
                                        ; jmp seek
                                        ;ret
;
seek:
        ;seek the track given by arecord (actual record)
;
;
        mov     ax,arecord              ;compute track/sector
        mov     dx,0
        div     sectpt                  ;dx=sector, ax=track
        push    dx
        mov     cx,ax                   ;update curtrk
        mov     si,curtrka
        mov     [si],cx
        add     cx,offsetv              ;set BIOS track
        call    settrkf
        pop     cx                      ;set BIOS sector
        mov     dx,tranv
        call    sectran
        mov     cx,bx
        jmp     setsecf
        ;ret
;
;
;       utility functions for file access
;
dmposition:
        ;compute disk map position for vrecord to BX
        lea     bx,blkshf
        MOV     CL,[bx]                 ;shift count to CL
        MOV     AL,lvrecord             ;current virtual record to A
        shr     al,cl
        ;A = shr(vrecord,blkshf) = vrecord/2**(sect/block)
        MOV     CH,AL                   ;save it for later addition
        MOV     cl,7
        SUB     cl,[bx]
        MOV     AL,extval               ;extent value ani extmsk
;
                                        ;blkshf = 3,4,5,6,7
                                        ;CL=4,3,2,1,0
                                        ;shift is 4,3,2,1,0
        shl     al,cl
;arrive here with A = shl(ext and extmsk,7-blkshf)
        ADD     AL,CH                   ;add the previous
                                        ;shr(vrecord,blkshf) value
                                        ;AL is one of the following
                                        ;values, depending upon alloc
                                        ;bks blkshf
                                        ;1k   3     v/8 + extval * 16
                                        ;2k   4     v/16+ extval * 8
                                        ;4k   5     v/32+ extval * 4
                                        ;8k   6     v/64+ extval * 2
                                        ;16k  7     v/128+extval * 1
        RET                             ;with dm$position in A
;
getdm:
        ;return disk map value from position given by CX
        MOV     BX,info                 ;base address of file
                                        ;file control block
        ADD     BX,dskmap               ;bx=.diskmap
        ADD     BX,CX                   ;index by asingle byte value
        cmp     single,0                ;single byte/map entry?
        JZ      getdmd                  ;get disk map single byte
        MOV     BL,[bx]
        MOV     BH,0
        RET                             ;with BX=00bb
getdmd:
        ADD     BX,CX                   ;bx=.fcb(dm+1*2)
                                        ;return double precision value
        MOV     bx,[bx]
        RET
;
index:
        ;compute disk block number from current FCB
        CALL    dmposition              ;0...15 in register AL
        MOV     CL,AL
        MOV     CH,0
        CALL    getdm                   ;value to BX
        MOV     arecord,BX
        RET
;
alloct:
        ;called following index to see if block allocated
        MOV     BX,arecord
        or      bx,bx
        RET
;
atran:
        ;compute actual record address, assuming index called
        MOV     cl,blkshf               ;shift count to reg AL
        MOV     BX,arecord
        shl     bx,cl
        MOV     ablock,BX               ;save shifted block #  
        MOV     AL,lvrecord
        AND     AL,blkmsk               ;masked value in AL
        OR      bl,al
        MOV     arecord,BX              ;arecord=BX or 
                                        ;(vrecord and blkmsk)
        RET
;
getexta:
        ;get current extent field address to AL
        MOV     BX,info
        add     bx,extnum               ;bx=.fcb(extnum)
        mov     al,[bx]
        RET
;
gtfcba:
        ;compute reccnt and nxtrec addresses for get/setfcb
        MOV     DX,reccnt
        add     dx,info                 ;DX=.fcb(reccnt)
        MOV     BX,(nxtrec-reccnt)
        ADD     BX,DX                   ;bx=.fcb(nxtrec)
        RET
;
getfcb:
        ;set variables from currently addressed FCB
        CALL    gtfcba                  ;addresses in DX, BX
        MOV     AL,[bx]
        MOV     lvrecord,al             ;vrecord=fcb(nxtrec)
        XCHG    BX,DX
        MOV     AL,[bx]
        MOV     rcount,AL               ;rcount=fcb(reccnt)
        CALL    getexta                 ;BX=.fcb(extnum)
        MOV     AL,extmsk               ;extent mask to a
        AND     AL,[bx]                 ;fcb(extnum) and extmsk
        MOV     extval,AL
        RET
;
setfcb:
        ;place values back into current fcb
        CALL    gtfcba                  ;addresses to DX, BX
        MOV     AL,seqio
        CMP     AL,02
        JNZ     setfc1
        XOR     AL,AL                   ;check ranfill  
setfc1:
                                        ;=1 if sequential i/o
        add     AL,lvrecord              
        MOV     [bx],al                 ;fcb(nxtrec)=vrecord+seqio
        XCHG    BX,DX
        MOV     AL,rcount
        MOV     [bx],al                 ;fcb(reccnt)=rcount
        RET
;
;
cmpecs:
        ;compute checksum for current directory buffer
        MOV     cx,recsiz               ;size of directory buffer
        MOV     BX,buffa                ;current directory buffer
        XOR     AL,AL                   ;clear checksum value
cmpec0:
        ADD     AL,[bx]
        INC     BX
        loop    cmpec0
        RET                             ;with checksum in A
;
setcdisk:
        ;set a "1" value in curdsk position of CX
        PUSH    CX                      ;save input parameter
        MOV     cl,curdsk
        MOV     BX,1                    ;number to shift
        shl     bx,cl                   ;BX = mask to integrate
        POP     CX                      ;original mask
        or      bx,cx                   ;BX = mask or rol(1,curdsk)
        RET
;
nowrite:
        ;return true if dir checksum difference occurred
        MOV     ax,rodsk
        MOV     cl,curdsk
        shr     ax,cl
        AND     AL,1
        RET                             ;non zero if nowrite
;
setro:
                                        ;set current disk to read only
        mov     cx,rodsk
        CALL    setcdisk                ;sets bit to 1
        MOV     rodsk,BX
                                        ;high water mark in directory
                                        ;goes to max
        MOV     dx,dirmax
        INC     dx
        MOV     BX,cdrmaxa              ;BX = .cdrmax
        MOV     [bx],dx                 ;cdrmax = dirmax
        RET
;
ckrodir:
        ;check current directory element for read/only status
        CALL    getdptra                ;address of element
;                                               
ckrofile:
        ;check current buff(dptr) or fcb(0) for r/o status
        MOV     DX,rofile
        ADD     BX,DX                   ;offset to r/o bit
        MOV     AL,[bx]
        RCL     AL,1
        jae     ret5                    ;rnc
        LEA     BX,rofer
        JMP     goerr                   ;
;       jmp roferror ;exit to read only disk message
;
;
checkwrite:
        ;check for write protected disk
        CALL    nowrite
        jz      ret5                    ;rz
        LEA     BX,roderr
        JMP     goerr
;       jmp roderror ;read only disk error
;
getdptra:
        ;compute the address of a directory element at
        ;positon dptr in the buffer
        MOV     BX,buffa
        MOV     AL,dptr
addh:
        ;BX = BX + AL
        mov     ah,0
        add     bx,ax
ret5:   ret
;
;
getmodnum:
        ;compute the address of the module number
        ;bring module number to accumulator
        ;(high order bit is fwf (file write flag)
        MOV     BX,info
        add     bx,modnum
        MOV     AL,[bx]
        RET                             ;AL=fcb(modnum)
;
clrmodnum:
        ;clear the module number field for user open/make
        CALL    getmodnum
        MOV     b[bx],0                 ;fcb(modnum)=0
        RET
;
setfwf:
        CALL    getmodnum               ;BX=.fcb(modnum),
                                        ;AL=fcb(modnum)
                                        ;set fwf(file write flag) to 1
        OR      AL,fwfmsk
        MOV     [bx],al                 ;fcb(modnum)=fcb(modnum) + 80h
                                        ;also returns non zero
                                        ;in accumulator
        RET
;
;
compcdr:
        ;return cy if cdrmax > dcnt
        MOV     dx,dcnt                 ;DX = directory counter
        MOV     BX,cdrmaxa              ;BX=.cdrmax
        cmp     dx,[bx]
                                        ;condition dcnt - cdrmax
                                        ;produces cy if cdrmax>dcnt
ret6:   RET
;
setcdr:
        ;if not (cdrmax > dcnt) then cdrmax = dcnt+1
        CALL    compcdr
        jb      ret6                    ;return if cdrmax > dcnt
                                        ;otherwise, BX = .cdrmax+1,
                                        ;DX = dcnt
        INC     DX
        MOV     [bx],dx
        RET
;
subdh:
        ;compute BX = DX - BX
        push    dx
        sub     dx,bx
        mov     bx,dx
        pop     dx
        RET
;
newchecksum:
        ;drop through to compute new checksum
        MOV     CL,true
checksum:
        ;compute current checksum record and update the
        ;directory element if CL=true, or check for = if not
        ;drec < chksiz?
        MOV     DX,drec
        MOV     BX,chksiz
        CALL    subdh                   ;DX-BX
        jae     ret6                    ;skip checksum if past
                                        ;checksum vector size
                                        ;drec < chksiz, so continue
        PUSH    CX                      ;save init flag
        CALL    cmpecs                  ;check sum value to AL
        MOV     BX,drec                 ;value of drec
        ADD     BX,checka               ;bx=.check(drec)
        POP     CX                      ;recall true or false to CL
        INC     CL                      ;true produces zero flag
        JZ      initcs
                                        ;not initializing, compare
        CMP     AL,[bx]                 ;compute$cs=check(drec)?
        jz      ret7                    ;no message if ok
                                        ;checksum error, are we beyond
                                        ;the end of the disk?
        CALL    compcdr
        jae     ret7                    ;no message if so
        CALL    setro                   ;read/only disk set
        RET
initcs:
                                        ;initializing the checksum
        MOV     [bx],al
ret7:   RET
;
;
wrdir:
        ;write the current directory entry, set checksum
        CALL    newchecksum             ;initialize entry
        CALL    setdir                  ;directory dma
        MOV     CL,1                    ;indicates a write directory
        CALL    wrbuff                  ;write the buffer
        JMP     setdata                 ;to data dma address
                                        ;ret
;
rddirbuf:
        ;read a directory entry into the directory buffer
        CALL    setdir                  ;directory dma
        CALL    rdbuff                  ;directory record loaded
                                        ; jmp setdata
                                        ; ret
;
setdata:
        ;set data dma address
        mov     cx,dmabase
        call    setdmbf                 ;set BIOS disk I/O base
        LEA     BX,dmaad
        jmps    setdma                  ;to complete the call
;
setdir:
        ;set directory dma address
        mov     cx,ds
        call    setdmbf                 ;set BIOS disk I/O base
        LEA     BX,buffa                ;jmp setdma to complete call     
;
setdma:
        ;BX=.dma address to set (i.e., buffa or dmaad)
        MOV     CX,[bx]                 ;parameter ready
        JMP     setdmf
;
;
dirtouser:
        ;copy the directory entry to the user buffer
        ;after call to search or searchn by user code
        MOV     DX,buffa                ;source is directory buffer
        MOV     BX,dmaad                ;destination is user dma addr.
        MOV     CL,recsiz               ;copy entire record
        push    es                      ;move to user segment
        mov     es,parametersegment
        call    move
        pop     es
        ret
                                        ;ret
;
endofdir:
        ;return zero flag if at end of director, non zero
        ;if not at end (end of dir if dcnt = 0ffffh)
        LEA     BX,dcnt
        MOV     AL,[bx]                 ;may be 0ffh
        INC     BX
        CMP     AL,[bx]                 ;low(dcnt) = high(dcnt)?
        jnz     ret8                    ;return non zero if different
                                        ;high and low the same,= 0ffh?
        INC     AL                      ;0ffh becomes 00 if so
ret8:   RET
;
setenddir:
        ;set dcnt to the end of the directory
        MOV     BX,enddir
        MOV     dcnt,bx
        RET
;
rddir:
        ;read next directory entry, with CL=true if initializing
        MOV     DX,dirmax               ;in preparation for subtract
        MOV     BX,dcnt
        INC     BX
        MOV     dcnt,bx                 ;dcnt=dcnt+1
                                        ;continue while dirmax >= dcnt
                                        ;(dirmax-dcnt no cy)
        CALL    subdh                   ;DX-BX
        JAE     rddir0
                                        ;yes, set dcnt to end
                                        ;of directory
        jmps    setenddir
;               ret
rddir0:
        ;not at end of directory, seek next element
                                        ;CL=initialization flag
        MOV     AL,ldcnt
        AND     AL,dskmsk               ;low(dcnt) and dskmsk
        push    cx
        MOV     cl,fcbshf               ;to multiply by fcb size
        shl     al,cl
        pop     cx
                                        ;A = (low(dcnt) and dskmsk)
                                        ;shl fcbshf
        MOV     dptr,AL                 ;ready for next dir operation
        OR      AL,AL
        jnz     ret71                   ;return if not a new record
        PUSH    CX                      ;save initialization flag CL
        CALL    seekdir                 ;seek proper record
        CALL    rddirbuf                ;read the directory record
        POP     CX                      ;recall initialization flag
        JMP     checksum                ;checksum the directory elt
                                        ;ret
;
;
getallocbit:
        ;given allocation vector
        ;position on CX, return byte
        ;containing CX shifted so that the least significant
        ;bit is in the low order accumulator position. BX is
        ;the address of the byte for
        ;possible replacement in
        ;memory upon return, and DH contains the number of shifts
        ;required to place the returned value back into position
;
        MOV     dl,CL
        AND     dl,111b
        INC     dl
        MOV     DH,dl
                                        ;dh and dl both contain the
                                        ;number of bit positions to
                                        ;shift
        MOV     AL,CL
        mov     cl,3
        ror     al,cl
        AND     AL,11111b
        MOV     CL,AL                   ;CL shr 3 to CL
        push    cx
        mov     cl,5
        shl     al,cl
        pop     cx
        or      CL,AL                   ;bbbccccc to CL
        MOV     AL,CH
        ROR     AL,1
        ROR     AL,1
        ROR     AL,1
        AND     AL,11111b
        MOV     CH,AL                   ;CX shr 3 to CX
        MOV     BX,alloca               ;base addr. of alloc. vector
        ADD     BX,CX
                                        ;byte to A, hl =
        MOV     AL,[bx]                 ;.alloc(CX shr 3)
                                        ;now move the bit to the
                                        ;low order position of AL
        push    cx
        mov     cl,dl
        rol     al,cl
        pop     cx
ret71:  RET
;
;
setallocbit:
        ;CX is the bit position of ALLOC to set or reset. The
        ;value of the bit is in register DL.
        PUSH    DX
        CALL    getallocbit             ;shifted val AL,count in DL
        AND     AL,11111110b            ;mask low bit to zero
                                        ;(may be set)
        POP     CX
        OR      AL,CL                   ;low bit of CL masked into AL
                                        ; jmp rotr
                                        ; ret
rotr:
        ;byte value from ALLOC is in register AL, with shift count
        ;in register CH (to place bit back into position), and
        ;target ALLOC position in registers BX, rotate and replace
;
        push    cx
        mov     cl,ch
        ror     al,cl
        mov     [bx],al
        pop     cx
        ret
;
;************* end BDOS filesystem part 1 **************
end
