FILESYS EQU 1

; 1 . need to take a path string and split it into path and file
;
; 2 . A function to browse through a directory starting at a specific cluster
; and match the string passed in with an entry in the directory table, and
; then returns the start cluster of this.
;
; 3 . A function to remove trailing spaces from filenames/paths.
;
; 4 . A function to use the match and search functions to traverse a path
; using start clusters found to find the next sublevel of directory in a
; directory string.
;
; 5 . A function to return the number of directories in a path string.
;
; 6 . A function to return a directory name from a path string from
; a sublevel value passed in.
;
; 7. Need to implement file system buffering, basic file I/O and file
; pointers, file access modes. Maintain and open file table, and include
; each file's access mode and pointer indicating next location to read
; or write
;
; Grants for giving 2005
; www.starbucks.com www.ktvu.com
;
; Lowlevel FAT12/16 Maintenance and Manipulation Routines.
; By: Christopher R. Evans in 2005, 2006

MAX_DRIVES     EQU 'Z' - 'A' + 1
SECTOR_SIZE    EQU 512
EMPTYENTRY     EQU 0e5h  ; empty directory
FIRST_DIR      EQU 5
FIRST_DATA     EQU 12
EOF            EQU -1    ; EOF indicator
ERROR          EQU -2    ; report error in file
READ           EQU 0     ; file opened for read
WRITE          EQU 1     ; file opened for write
FAT32_WRITE    EQU 0     ; WRITING_TO_FAT32_ENABLED
FAT12_FREE     EQU 0000h
FAT12_UNUSED   EQU 0001h
FAT12_RESERVED EQU 0ff0h  ; start of reserved values
FAT12_DEFECT   EQU 0ff7h  ; cluster w/ defective block
FAT12_EOF      EQU 0ff8h  ; Start of end of file range
FAT12_END      EQU 0fffh  ; end of reserved range
;
LONG_LAST_CLUSTER_32 EQU 0FFFFFFFh
LONG_BAD_32          EQU 0FFFFFF7h
LONG_LAST_CLUSTER    EQU 0FFFFh
LONG_BAD             EQU 0FFF7h
MASK16               EQU 0FFF8h
BAD16                EQU 0FFF7h
MASK12               EQU 0FF8h
BAD12                EQU 0FF7h

; magic constants: even though FF7 is BAD so FF6 could be a valid cluster,
; MS docs specify that FF5 is the maximal possible cluster number for FAT12
; similar for 16 and 32

FAT_MAGIC   EQU 4085
FAT_MAGIC16 EQU 65525
FAT_MAGIC32 EQU 268435455

; FAT-12 files of 0 size have starting cluster set to 0 or chkdsk get error
MAXFILES     EQU 255

; Test for 16 bit or 12 bit FAT
SIZEOF_CLUSTER16 EQU 2
SIZEOF_CLUSTER32 EQU 4

; FAT file system attribute bits
normal     EQU 0
readonly   EQU 01h
hidden     EQU 02h
system     EQU 04h
vol_id     EQU 08h
dir        EQU 10h
archive    EQU 20h
fatdevice  EQU 40h 
compressed EQU 60h
encrypted  EQU 80h
aBserial  EQU 0b243603h
aBSerial2 EQU 68eba700h

; ===========================================================================
; FAT DATASEG - FAT12 Data Area
; ===========================================================================nomedia   db 'Media not inserted.',0ffh,0

aBmsdos   db 'MSDOS5.0',0
aBfrdos   db 'FRDOS4.1',0
aBnxdos   db 'NXDOS8.0',0 ; 8
aBFsType  db 'FAT12   '
aBFs2Type db 'FAT16   '
aBVolume  db 'NO NAME    '

BOOTSECTOR:         ; BOOTSECTOR
_bsjump             db 3 dup(?)
_bsoemname          db 8 dup(?)
_bsbytespersector   dw ?
_bssectorpercluster db ?
_bsressectors       dw ?
_bsnumcopiesFAT     db ?
_bsmaxallocrootdir  dw ?
_bsmaxsectors       dw ?
_bsmediadescriptor  db ?
_bssectorsperfat    dw ?
_bssectorspertrack  dw ?
_bsheads            dw ?
_bshiddensectors    dd ?
_bshugesectors      dd ?
_bsdrivenumber      db ?
                    db ?
_bsbootsignature    db ?
_bsvolumeid         db 11 dup(?)
_bsfilesystemtype   db 8 dup(?)
BOOTSECTOR_SIZE EQU $ - BOOTSECTOR

BOOTBPB:
        BOOT_abOem db 'NXDOS8.0' ; 8
BPB:
        P_BPB_BYTESPERSECTOR    EQU 0
        P_BPB_SECTORSPERCLUSTER EQU 2
        P_BPB_RESERVEDSECTORS   EQU 3
        P_BPB_NUMBEROFFATS      EQU 5
        P_BPB_ROOTENTRIES       EQU 6
        P_BPB_TOTALSECTORS      EQU 8
        P_BPB_MEDIADESCRIPTOR   EQU 0ah
        P_BPB_SECTORSPERFAT     EQU 0bh
        P_BPB_SECTORSPERTRACK   EQU 0dh
        P_BPB_HEADS             EQU 0fh
        P_BPB_HIDDENSECTORS     EQU 11h
        P_BPB_BIGTOTALSECTORS   EQU 15h
        P_BPB_RESERVED          EQU 19h
        BPB_BYTESPERSECTOR    dw 512
        BPB_SECTORSPERCLUSTER db 1
        BPB_RESERVEDSECTORS   dw 0
        BPB_NUMBEROFFATS      db 2
        BPB_ROOTENTRIES       dw 512
        BPB_TOTALSECTORS      dw ?
        BPB_MEDIADESCRIPTOR   db 0f8h
        BPB_SECTORSPERFAT     dw ?
        BPB_SECTORSPERTRACK   dw 18
        BPB_HEADS             dw 2
        BPB_HIDDENSECTORS     dd 0
        BPB_BIGTOTALSECTORS   dd 0
BPB_SIZE EQU $ - BPB
        BPB_bDrvNo            db 0
        BPB_res1              db 0
        BPB_bExtBootSig       db 29h
        BPB_lSerNo            dd aBserial
        BPB_abVolLabel        db 11 dup(' ')
        BPB_abFileSysID       db 8 dup(' ')
BPB_DOS4_SIZE EQU $ - BPB
BPB_FULL_SIZE EQU $ - BOOTBPB

bpbjump   db 3 dup(0)
; FEB-24-2006 - Design note, This BPB structure will be used for the fat16/32 code as well.
include \nxdos\src\boot\bpb.asm
bpbfiller_size EQU $ - bpbjump
bpbfiller db (512-bpbfiller_size-2) dup(0)
          dw 0aa55H
bpbjump_size EQU $ - bpbjump

DPB:
        DPB_ABUSY       EQU 1
        DPB_AWANT       EQU 2
        DPB_FBUSY       EQU 4
        DPB_FWANT       EQU 8
        ; -----------------------------
        P_DPB_DRIVE         EQU 0
        P_DPB_UNIT          EQU 1
        P_DPB_SECTOR_SIZE   EQU 2
        P_DPB_CLUSTER_MASK  EQU 4
        P_DPB_CLUSTER_SHIFT EQU 5
        P_DPB_FIRST_FAT     EQU 6
        P_DPB_FAT_COUNT     EQU 8
        P_DPB_ROOT_ENTRIES  EQU 9
        P_DPB_FIRST_SECTOR  EQU 0bh
        P_DPB_MAX_CLUSTER   EQU 0dh
        P_DPB_FAT_SIZE      EQU 0fh
        P_DPB_DIR_SECTOR    EQU 10h
        P_DPB_DRIVER_ADDR   EQU 12h
        P_DPB_MEDIA         EQU 16h
        P_DPB_FIRST_ACCESS  EQU 17h
        P_DPB_NEXT_DPB      EQU 18h
        P_DPB_NEXT_FREE     EQU 1ch
        P_DPB_FREE_CNT      EQU 1eh
        ; -----------------------------
        DPB_drive     db 0             ; physical drive no
        DPB_unit      db 1             ; subunit
        DPB_cbsector  dw 512           ; bps
        DPB_mask      db 0             ; sectors/alloc unit - 1
        DPB_shift     db 0             ;
        DPB_secFAT    dw 0             ; sector no of first FAT
        DPB_cFAT      db 2             ;
        DPB_cDirEnt   dw 512           ;
        DPB_secData   dw 2             ; 1st data sector
        DPB_cCluster  dw 0ffffh        ; max no of clusters on drive = DSKSIZE
        DPB_csecFAT   dw 9             ; sectors in each fat - DOS 5
        DPB_secDir    dw 2             ; 1st sector of root directory
        DPB_pDevice   dd ?             ; pointer to device header
        DPB_media     db ?             ; last media in drive
        DPB_fFirst    db -1            ; TRUE => force media check
        DPB_nextDPB   dd ?             ; pointer to next block
        DPB_clusFree  dw ?             ; cluster number of last alloc
        DPB_FreeCnt   dw -1            ; count of free clusters, -1 for unk
DPB_SIZE EQU $ - DPB  
        DPB_SyncFlg   db 0             ; sync flags
DPB2_SIZE EQU $ - DPB  

DirEntry:
        DirNAME db 11 dup(0)
        DirATTR db 0
        DirRESR db 10 dup(0)
        DirTIME dw ?
        DirDATE dw ?
        DirCLST dw ?  ; first cluster no.
        DirSIZE dd ?  ; file size
DIRENTRY_SIZE EQU $ - DirEntry ; 32

iFCB:   ; Internal File Control Block 
        fATTR  db 0
        fSECT  db 0
        fDPTR  dd ?
        fDIRS  dw ?
        fFIRST dw ?
        fLAST  dw ?
        fNEXT  dw ?
        fOFSRW dw ?
        fSIZE  dd ?
        fBUFF  db SECTOR_SIZE dup(0)
iFCB_SIZE EQU $ - iFCB


mountdrv  db -1         ; open disk drive number, also current work drive
dirsec    dw FIRST_DIR  ; first sector of directory
datasec   dw FIRST_DATA ; first sector of data area
worksec   dw -1
workchg   db 0          ; work sector changed
workbuf   db SECTOR_SIZE dup(0)
sectbuf   db SECTOR_SIZE dup(0)
;
FAT12_drive          db 0
FAT12_mounted        dw 0
FAT12_disk_modified  dw 0
FAT12_fat_start      dw 1
FAT12_fat2_start     dw 10
FAT12_fat_sectors    dw 9
FAT12_data_start     dw 33
FAT12_RootDirSecs    dw 0
FAT12_root_dir_start dw 0
;

; ===========================================================================
; FAT FS primitives
; ===========================================================================

; ===========================================================================
cluster2lsn proc far
; Input : DX    = cluster
;         CX    = sectors per cluster
;         ES:BX = ptr to word of dpbfirstdatasector[low:high]
; Output: AX:DX = Logical Cluster Number 
; ===========================================================================
       mov  ax, dx
       dec  ax
       dec  ax
       xor  dx, dx
       mul  cx
       mov  bx, word ptr es:[bx]
       add  ax, bx
       mov  bx, word ptr es:[bx+2] ; high
       adc  ax, bx
       ret
cluster2lsn endp

; ===========================================================================
updatework proc far
; ===========================================================================
      mov  al, cs:workchg
      or   al, al
      jz   no_update
           mov  al, 1
           mov  dl, cs:[mountdrv]
           mov  cx, cs:[worksec]
           push cs
           pop  es
           lea  bx, cs:[workbuf]
           call cs:writesectors   
           xor  al, al
           mov  cs:[workchg], al
no_update:
      ret
updatework endp

; ===========================================================================
readwork proc far
;    DL = Drive
;    CL = Sector
; ES:BX = BUFFER
; ===========================================================================
      mov  bl, cs:[mountdrv]
      cmp  bl, dl
      je   no_rw
      mov  bx, cs:[worksec]
      cmp  bx, cx
      je   no_rw
           call cs:updatework
           mov  cs:[worksec], cx
           mov  cs:[mountdrv], dl
           push cs
           pop  es
           lea  bx, cs:[workbuf]
           mov  al, 1
           call cs:readsectors
no_rw:
      ret
readwork endp

; ===========================================================================
readbss proc far
; Input : DL    = drive
; Output: ES:SI = bootbpb
;         CX    = size
; ===========================================================================
       mov  cl, 0
       call cs:readwork       ; load boot sector
       cld
       lea  si, cs:workbuf+3
       lea  di, cs:bootbpb
       mov  cx, BPB_DOS4_SIZE
       rep  movsb
       ; lets keep a copy for referance later on
       lea  si, cs:[workbuf]
       lea  di, cs:bpbjump
       mov  cx, sector_size
       rep  movsb
       mov  cx, BPB_DOS4_SIZE 
       lea  si, bootbpb 
       push cs
       pop  es
       ret
readbss endp

; ===========================================================================
writebss proc far
; ===========================================================================     
       lea  si, cs:[bpbjump]
       lea  di, cs:[workbuf]
       mov  cx, sector_size
       rep  movsb
       mov  al, 1
       mov  cs:workchg, al      
       call updatework 
       ret
writebss endp

; ===========================================================================
calc_fatparams proc far
; ===========================================================================
      ; dirsec = (numfat * secfat) + ressec
      xor  ax, ax
      xor  dx, dx
      mov  al, cs:BPB_NUMBEROFFATS
      mov  bx, cs:BPB_SECTORSPERFAT
      mul  bx
      mov  bx, cs:BPB_RESERVEDSECTORS
      add  ax, bx
      mov  cs:dirsec, ax
      ; datasec = ((((dirent * sizeof(dentry)))+bytsec)-1)/bytsec)+dirsec
      xor  ax, ax
      xor  dx, dx
      mov  ax, cs:BPB_ROOTENTRIES
      mov  bx, DIRENTRY_SIZE
      mul  bx
      mov  bx, cs:BPB_BYTESPERSECTOR
      add  ax, bx
      dec  ax
      xor  dx, dx
      mov  bx, cs:BPB_BYTESPERSECTOR
      div  bx
      mov  bx, cs:dirsec
      add  ax, bx
      mov  cs:datasec, ax
      ret
calc_fatparams endp

; ===========================================================================
mount proc far
; THIS ROUTINE MUST BE CALLED BEFORE ACCESSING ANY FILES, AND WHEN YOU SWITCH
; TO ANOTHER DRIVE!
; Input : DL = Drive 
; Output: CF = CY on error
; ===========================================================================
  mov  al, cs:[mountdrv]
  cmp  al, -1
  jne  mnt_error
  call cs:resetdisk
  jc   mnt_error
       mov  cs:[mountdrv], dl
       call readbss
       ; Check for valid extended Boot Signature
       mov  al, cs:BPB_bExtBootSig
       cmp  al, 29h
       jne  mnt_error
       ; 
       cmp  byte ptr abFsType[4], '2'  ; check for FAT-12 system
       jne  mnt_error                  ; we dont support FAT16 yet
       call calc_fatparams
       ; call load_fat
       ; call load_fat2
       ; call load_root
       IF (DEBUG)
          push es
          lea  si, cs:[BOOTBPB]
          mov  cx, BPB_FULL_SIZE
          call cs:debugdump
          ; call cs:show_bpb
          pop  es
       ENDIF
mnt_okay:
  clc
  mov  dl, cs:mountdrv
  jmp  short mnt_x
mnt_error:
  mov  al, -1
  mov  cs:[mountdrv], al
  stc
mnt_x:
  mov  al, cs:mountdrv
  ret
;if (mounted) return;
;drive=DL;
;reset_drive(drive);
;if readbss(drive,&bpbjump) {
;    if bpb.extsig = 29h {
;       if bpb.sysid[4]='2' {
;           calcparams();
;           loadroot(drive);
;           loadfat(drive);
;           loadfat2(drive);
;           mounted=true;
;           return;
;          }
;       }
;    mounted = false;
;   }
;return;
;
; scale 1.5 to 2    1.5 bytes * 2 == 3
; dirtymapsize = roundup((nclust*3)/2,secz)/secz
; dirtymap=malloc(dirtymapsize)
; fillzero(dirtymap,dirtysize)
; malloc(fatlen*secsz)
; readin fatsectors
mount endp 

; ===========================================================================
unmount proc far
; Input : DL = Drive 
; ===========================================================================
      call updatework
      mov  al, -1
      mov  cs:mountdrv, al
      ret
unmount endp 

umount proc far
      call unmount
      ret
umount endp

; ===========================================================================
IsEOF proc far
; Input : ES:BP = -> DPB
;         BX    = FAT 
; Output: JAE EOF
; ===========================================================================
      cmp  es:[bp+P_DPB_MAX_CLUSTER], 0ff6h ; 4096 - 10   16bit?
      jae  EOF16
      cmp  bx, 0ff0h  ; other media like fat12
      je   EOFOther
      cmp  bx, 0ff8h  ; 12 bit
      ret
EOF16:
      cmp  bx, 0fff8h
EOFOther:
      ret
IsEOF endp

diskinfo proc far
; Input : AL = BIOS DISK 0=A:
; Output: ES:SI = bootdescriptor
;         CX    = size
      push dx
      push bx
      ;
      mov  dl, al
      mov  al, 1
      ; packcylsect 0, 1
      mov  ch, 0
      mov  cl, 1
      xor  dx, dx
      mov  bx, offset cs:[sectbuf]
      call cs:readsectors
      jc   di_error
      ;
      mov  ax, word ptr cs:[sectbuf+bpb_bSectsperclust]
      mov  word ptr cs:[bSECTSPERCLUST], ax
      mov  al, byte ptr cs:[sectbuf+bpb_bFats]
      mov  byte ptr cs:[bNO_FATS], al
      mov  ax, word ptr cs:[sectbuf+bpb_wRootentries]
      mov  word ptr cs:[bRootentries], ax
      mov  ax, word ptr cs:[sectbuf+bpb_wSectsperfat]
      mov  word ptr cs:[bSectsperFAT], ax
      mov  ax, word ptr cs:[sectbuf+bpb_lHiddenSects+2]
      mov  word ptr cs:[bHiddenSects], ax
      ;
      ; calculate logical sectors
      ;
      push cs
      pop  es
      lea  si, cs:[bootdesc]
      mov  cx, bootdesc_size
      clc
      jmp  short di_okay
di_error:
      stc
di_okay:
      pop  bx
      pop  dx
      ret
bootdesc:
      bSECTPERCLUST  dw ?
      bNO_FATS       db ?
      bRootentries   dw ?
      blogicalsect   dw ?
      bSectsperFAT   dw ?
      bHiddenSects   dw ?
bootdesc_size EQU $ - bootdesc
diskinfo endp

; ===========================================================================
SHOW_BPB proc far
; ===========================================================================
        _output  aboem, 9, 1
        _echo ' '
        _print SectSize_msg, 1
        mov  ax, wsectsize
        call putnum
        _echo ' '
        _print SPC______msg, 1
        xor  ax, ax
        mov  al, bSectsperclust
        call putnum
        _newline

        _print Reserved_msg, 1
        xor  ax, ax
        mov  ax, wReservedsects
        call putnum
        _echo ' '
        _print FATS_____msg, 1
        xor  ax, ax
        mov  al, bFats
        call putnum
        _echo ' '
        _print RootEnt__msg, 1
        xor  ax, ax
        mov  ax, wRootentries
        call putnum
        _newline

        _print SmallSec_msg, 1
        xor  ax, ax
        mov  ax, wSmallsectors
        call putnum
        _echo ' '
        _print MediaID__msg, 1
        xor  ax, ax
        mov  al, bMedia
        call put_hex
        _echo 'H'
        _echo ' '
        _print SPF______msg, 1
        xor  ax, ax
        mov  ax, wSectsperfat 
        call putnum
        _newline

        _print SPT______msg, 1
        xor  ax, ax
        mov  ax, wSectspertrack 
        call putnum
        _echo ' '
        _print Heads____msg, 1
        xor  ax, ax
        mov  ax, wHeads 
        call putnum
        _echo ' '
        _print Hiddensc_msg, 1
        xor  ax, ax
        mov  ax, word ptr lHiddensects+2
        call putnum
        mov  ax, word ptr lHiddensects
        call putnum
        _echo ' '
        _print Largesec_msg, 1
        xor  ax, ax
        mov  ax, word ptr lLargesects+2
        call putnum
        mov  ax, word ptr lLargesects
        call putnum
        _newline

        _print Driveno__msg, 1
        xor  ax, ax
        mov  al, bPhysicaldrvno
        call putnum
        _echo ' '
        _print CurHead__msg, 1
        xor  ax, ax
        mov  al, bCurrenthead
        call putnum
        _echo ' '
        _print bpb_sig__msg, 1
        xor  ax, ax
        mov  al, bSig
        call put_hex
        _echo 'H'
        _echo ' '
        _print Serialno_msg, 1
        xor  ax, ax
        mov  ax, word ptr lSerialNo+2
        call put_hex
        mov  ax, word ptr lSerialNo
        call put_hex
        _echo 'H'
        _newline
        _print Vollabel_msg, 1
        _output Vollabel, 11, 1
        _echo ' '
        _print SystemID_msg, 1
        _output SystemID, 07, 1
        _newline
         ret
SectSize_msg db 'Sector    : ',0
SPC______msg db 'S.P.C.    : ',0
Reserved_msg db 'Reserved  : ',0
Fats_____msg db 'FAT(s)    : ',0
RootEnt__msg db 'Root block: ',0
SmallSec_msg db 'Small Sect: ',0
MediaID__msg db 'Media Type: ',0
SPF______msg db 'S. Per FAT: ',0
SPT______msg db 'S.P.T.    : ',0
Heads____msg db 'Heads     : ',0
Hiddensc_msg db 'Hidden    : ',0
Largesec_msg db 'Large Sect: ',0
Driveno__msg db 'Drive     : ',0
Curhead__msg db 'Head      : ',0
bpb_sig__msg db 'BPB Flag  : ',0
Serialno_msg db 'Serial  # : ',0
vollabel_msg db 'Volume    : ',0
SystemID_msg db 'System ID : ',0
SHOW_BPB endp

debug_FAT12ROOT proc far
IF (DEBUG)
      push cs
      pop  es
      mov  si, offset [FAT12_ROOT]
      mov  cx, 64
      call debugdump
ENDIF
      ret
debug_FAT12ROOT endp

VALID_FAT12 proc far
      push cs
      pop  es
      mov  si, offset [FAT12_FAT]
      mov  al, byte ptr es:[si+0]
      mov  bl, byte ptr es:[si+1]
      mov  dl, byte ptr es:[si+2]
      cmp  al, 0F0h
      je   chk2nd
      jmp  badfat
chk2nd:
      cmp  bl, 0FFh
      je   chk3rd
      jmp  badfat
chk3rd:
      cmp  dl, 0FFh
      je   validfat
      jmp  badfat
validfat:
      clc
      ret
badfat:
      stc
      ret
VALID_FAT12  endp

; ===========================================================================
FAT12_Mount proc far
; DL = Driveno
; Output : CF = CY on error
; ===========================================================================
      mov  cs:FAT12_drive, dl
      mov  cs:[diskdrive], dl
      call resetdisk
      push cs
      pop  es
      mov  ah, 2                   
      mov  al, 1       
      mov  cx, 1                            
      mov  dh, 0
      mov  dl, cs:[diskdrive]
      mov  bx, offset cs:[bpbjump]
      int  13h
      jc   FAT12_Mount_error
      ;
      mov  al, bSig
      cmp  al, 29h
      jne  FAT12_Mount_error
      cmp  byte ptr SystemID[4], '6'  ; check for FAT-16 system
      je   FAT12_Mount_error
IF (DEBUG)
      call cs:show_bpb
ENDIF
      call cs:FAT12_CALCPARAMS
      call cs:FAT12_loadroot
      jc   FAT12_Mount_error
      call debug_FAT12ROOT
      call cs:FAT12_loadfat
      jc   FAT12_Mount_error
      call VALID_FAT12
      jc   FAT12_Mount_error
      call cs:FAT12_loadfat2
      jc   FAT12_Mount_error
      mov  ax, 1
      mov  FAT12_mounted, ax
      clc
      jmp  FAT12_Mount_Okay
FAT12_Mount_error:
      xor  ax, ax
      stc
FAT12_Mount_Okay:
      ret
FAT12_Mount endp

FAT12_unmount proc far
            mov  ax, FAT12_disk_modified
            or   ax, ax
            jz   FAT12_NO_COMMIT

            ; write FATS and root dir to disk
            call cs:FAT12_saveroot
            call cs:FAT12_savefat
            ; commit read cache to disk.

FAT12_NO_COMMIT:
            lea  di, FAT12_root
            mov  cx, FAT12_root_size
            mov  bl, 0
            call fillarray

            lea  di, FAT12_FAT
            mov  cx, FAT12_FAT1_size
            mov  bl, 0
            call fillarray

            lea  di, FAT12_FAT2
            mov  cx, FAT12_FAT2_size
            mov  bl, 0
            call fillarray
            xor  ax, ax
            mov  FAT12_mounted, ax
            ret
FAT12_unmount endp

FAT12_CALCPARAMS proc far
                mov  bx, word ptr [wReservedsects]
                xor  ax, ax
                xor  dx, dx
                add  ax, bx
                mov  bx, word ptr [lHiddensects]
                add  ax, bx
                mov  bx, word ptr [lHiddensects+2]
                adc  bx, 0 
                mov  word ptr FAT12_fat_start, ax
                ;
                mov  bx, wSectsperfat
                add  ax, bx
                mov  word ptr FAT12_fat2_start, ax
                ;
                mov  ax, wSectsperfat
                mov  word ptr FAT12_fat_sectors, ax

                xor  ax, ax
                mov  al, bFATs
                mov  bx, wSectsperfat
                xor  dx, dx
                mul  bx

                inc  ax
                mov  word ptr FAT12_root_dir_start, ax

                ;    18
                mov  ax, wRootentries
                mov  bx, 32
                xor  dx, dx
                mul  bx
                mov  bx, wSectsize
                div  bx
                mov  FAT12_RootDirSecs, ax  
                ; 14
                mov  ax, word ptr FAT12_fat_sectors
                mov  bx, 2
                xor  dx, dx
                mul  bx
                mov  bx, word ptr FAT12_RootDirSecs
                add  ax, bx
                inc  ax
                mov  FAT12_data_start, ax  
                ; 33

            IF (DEBUG)
            mov  ax, FAT12_fat_start
            call putnum
            _echo ' '
            mov  ax, FAT12_root_dir_start
            call putnum
            _echo ' '
            mov  ax, FAT12_RootDirSecs
            call putnum
            _echo ' '
            mov  ax, FAT12_data_start
            call putnum
            _newline
            ENDIF

                ret
FAT12_CALCPARAMS endp

; the first sector of cluster 2 (data region) is computed as follows:
; FAT12_rootdirsectors = ((BPB_RootEntries*32)+(BPB_Bytespersec-1))/BPB_Bytespersec;
; rootdirectory size = BPB_reserved+(BPB_numFats*FATSz);
; 1st data sector = BPB_reserved+(BPB_numFats*FATSz)+RootDirSect;

FAT12_LoadRoot proc
            mov  dl, FAT12_drive
            mov  diskdrive, dl
            xor  dx, dx
            mov  ax, FAT12_root_dir_start    ; root directory location
            mov  di, FAT12_RootDirSecs     ; root dir size
            push cs
            pop  es
            mov  bx, offset [FAT12_ROOT]
            call readdisk
            jc   lraf_me
            push cs
            pop  es
            mov  bx, offset [FAT12_ROOT]
            clc
            ret
lraf_me:    stc
            ret
FAT12_LoadRoot endp

FAT12_SaveRoot proc
            mov  dl, FAT12_drive
            mov  diskdrive, dl
            xor  dx, dx
            mov  ax, FAT12_root_dir_start    ; root directory location
            mov  di, FAT12_RootDirSecs     ; root dir size
            push cs
            pop  es
            mov  bx, offset [FAT12_ROOT]
            call writedisk
            jc   sraf_me
            push cs
            pop  es
            mov  bx, offset [FAT12_ROOT]
            clc
            ret
sraf_me:    stc
            ret
FAT12_SaveRoot endp

FAT12_loadFAT proc far
            mov  dl, FAT12_drive
            mov  diskdrive, dl
            xor  dx, dx
            mov  ax, FAT12_fat_start
            mov  di, FAT12_fat_sectors
            push cs
            pop  es
            mov  bx, offset [FAT12_FAT]
            call readdisk
            jc   lraf_me2
            push cs
            pop  es
            mov  di, offset [FAT12_FAT]
            clc
            ret
lraf_me2:   stc
            ret
FAT12_loadFAT endp

FAT12_loadFAT2 proc far
            mov  dl, FAT12_drive
            mov  diskdrive, dl
            xor  dx, dx
            mov  ax, FAT12_fat2_start
            mov  di, FAT12_fat_sectors
            push cs
            pop  es
            mov  bx, offset [FAT12_FAT2]
            call readdisk
            jc   lraf2_me2
            push cs
            pop  es
            mov  di, offset [FAT12_FAT2]
            clc
            ret
lraf2_me2:   stc
            ret
FAT12_loadFAT2 endp

FAT12_saveFAT proc far
            mov  dl, FAT12_drive
            mov  diskdrive, dl
            xor  dx, dx
            mov  ax, FAT12_fat_start
            mov  di, FAT12_fat_sectors
            push cs
            pop  es
            mov  bx, offset [FAT12_FAT]
            call writedisk
            jc   sraf_me2
            push cs
            pop  es
            mov  di, offset [FAT12_FAT]
            clc
            ret
sraf_me2:   stc
            ret
FAT12_saveFAT endp

FAT12_saveFAT2 proc far
            mov  dl, FAT12_drive
            mov  diskdrive, dl
            xor  dx, dx
            mov  ax, FAT12_fat2_start
            mov  di, FAT12_fat_sectors
            push cs
            pop  es
            mov  bx, offset [FAT12_FAT2]
            call writedisk
            jc   sraf2_me2
            push cs
            pop  es
            mov  di, offset [FAT12_FAT2]
            clc
            ret
sraf2_me2:   stc
            ret
FAT12_saveFAT2 endp

FAT12_findfile proc far
; ES:SI = ROOT DIRECTORY
; DS:DI = Filename
; Output: AX = FIRST CLUSTER OF FILE
;         CF = CY on error
                mov     bx, wRootentries
FAT12_FF1:
                cmp  byte ptr es:[si], 0  ; null entry?
                je   FAT12_FNF   
                mov  cx, 0bh
                push si
                push di
                repe cmpsb   ; repeat if ZF = 1 CX > 0
                pop  di
                pop  si
                je   FAT12_FF
                dec  bx
                jz   FAT12_FNF
                add  si, 32      ; next entry
                jmp  FAT12_FF1
FAT12_FNF:    
                stc
                jmp  F12X
FAT12_FF:
                mov  ax, word ptr [SI+1aH]  ; first cluster
                cmp  ax, 2                  ; start cluster
                jb   FAT12_FNF   
                ; save first cluster for using by kernel
                mov  word ptr cs:[DirCLST], ax
                mov  bl, byte ptr [SI+0bh]        ; move attribute byte to bl
                mov  byte ptr cs:[DirATTR], bl
                mov  bx, word ptr [SI+16h]       
                mov  word ptr cs:[DirTIME], bx
                mov  bx, word ptr [SI+18h]       
                mov  word ptr cs:[DirDATE], bx
                mov  bx, word ptr [SI+1eh]       
                mov  word ptr cs:[DirSIZE], bx
                mov  bx, word ptr [SI+1ch]       
                mov  word ptr cs:[DirSIZE+2], bx
               ; and  bl, 0018h              ; notvalidfilemask
               ; ja   FAT12_FNF   
                clc
F12X:           ret
; OFFSET    LENGTH
; 00        8       -  Filename
; 08h       3       -  extension
; 0bh       1       -  attribute
; 0ch       10      -  reserved  unused in fat12/16
; 16h       2       -  time  BCD
; 18h       2       -  date  BCD
; 1ah       2       -  Starting cluster or 0
; 1ch       4       -  File Size in bytes
; 
FAT12_findfile endp

; loadfile (cluster, address) : boolean
; convert cluster to LBA
; read LBA sector for spc count into address
; add address + 512 bytes
; look up next cluster entry get new cluster value
; if cluster is 0ff0 or above then its eof
; if cluster is 0ff7 then it be a bad sector, abort.
; if cluster is 0ff8 then it end of file 
; loop to loadfile top

; ===========================================================================
_loadimage proc far
; AX    = cluster
; ES:BX = buffer
; ===========================================================================
          push di
          push si
          push ds
          push es
          push ax
          push bx
          push dx
          mov  FAT12_cluster, ax
load_image:
          mov  ax, FAT12_cluster
          ; cluster to LBA
          dec  ax
          dec  ax      ; sub  ax, 2
          xor  cx, cx
          mov  cl, byte ptr [bSectsperclust]
          mul  cx
          add  ax, word ptr [FAT12_data_start]
          ;
;          xor  cx, cx
;          mov  cl, byte ptr [bSectsperclust]
;          call readsectors
         ; jc   li_failure

          mov  dl, FAT12_drive
          xor  dx, dx
          xor  cx, cx
          mov  cl, byte ptr [bSectsperclust]
          mov  di, cx
          xor  cx, cx
          call readdisk
          ; jc   li_failure


; ---------------------------------------------
          ; compute next cluster
          mov  ax, FAT12_cluster
          mov  cx, ax
          mov  dx, ax
          shr  dx, 1            ; divide by 2
          add  cx, dx
          mov  bx, offset FAT12_FAT
          add  bx, cx
          mov  dx, word ptr [bx]   ; read two bytes from FAT table
          test ax, 1
          jnz  li_odd_cluster
li_even_cluster:
          and  dx, 0FFFh           ; take twelve bits
          jmp  li_done
li_odd_cluster:
          shr  dx, 4               ; take high twelve bits
li_done:
          mov  FAT12_cluster, dx
          mov  cx, fat12_filesize
          add  cx, 512
          mov  fat12_filesize, cx
;          cmp  dx, 0ff7h
;          je   li_failure     
;          cmp  dx, 0ff8h
;          je   li_doneloading
          cmp  dx, 0ff0h
          jb   load_image
li_doneloading:
          clc
          mov  cx, fat12_filesize
          jmp  li_ret
li_failure:
          stc
li_ret:
          pop  dx
          pop  bx
          pop  ax
          pop  es
          pop  ds
          pop  si
          pop  di
          ret
FAT12_cluster dw 0
fat12_filesize dw 0
_loadimage endp

;word nextcluster number filepointer, prevcluster
;{
;        offsetfromfat = prevcluster * 1.5
;        fatentrynum   = read contents from offsetfromfat+fatstartinglocation
;        if (nextcluster is even)
;                return fatentry & 0fffh
;           else
;                fatentrynum = fatentrynum >> 4  ; shr 4
;                fatentrynum = fatentrynum & 0fffh
;                return fatentrynum
;}

;int SearchAndPrintFile(filePointer, offset,fileName,startingClusterNumber,choice)
;{
;         if(current directory!= root directory)
;                 if(first Entry!='.'  || Second Entry!='..')
;                         return 0;
;
;        while(1)
;        {
;                if( current directory!=root directory)
;                            if( 16 entries are already read) 
;                                     get next cluster number from FAT;
;                 if(deleted entry)
;                            continue;
;                  if(first byte=0x00)
;                            return(numberOfMatchingFiles);
;                 
;                  if(current entry name = fileName)
;                            print file information;
;                             numberOfMatchingFiles++;
;                   
;                 if(current entry=sub directory)
;                       SearchAndPrintFile(filePointer, offset,fileName,startingClusterNumber,choice);
;                      
;            }
;               
;}

pSysN db 000h,'Empty',00h
      db 001h,'MSDOS FAT12',00h
      db 002h,'XENIX root',00h
      db 003h,'XENIX usr',00h
      db 004h,'MSDOS Fat16',00h
      db 005h,'MSDOS Extended',00h
      db 006h,'MSDOS FAT16 LFS',00h
      db 007h,'QNX/OS2 HPFS/WINNT NTFS/Unix',00h
      db 008h,'OS2/AIX boot/COMMODORE/DELL',00h
      db 009h,'AIX data',00h
      db 00Ah,'OS2 Boot Manager/OPUS',00h
      db 00Bh,'WINDOWS FAT32',00h
      db 00Ch,'WINDOWS FAT32 (LBA Mode)',00h
      db 00Eh,'WINDOWS FAT32 (LBA Mode)',00h
      db 00Fh,'WINDOWS FAT32',00h
      db 010h,'OPUS',00h
      db 011h,'OS2 Boot Manager FAT12',00h
      db 012h,'COMPAQ',00h
      db 014h,'OS2 Boot Manager FAT16',00h
      db 016h,'OS2 Boot Manager FAT16',00h
      db 017h,'OS2 HPFS',00h
      db 018h,'AST swap',00h
      db 024h,'NEC MSDOS',00h
      db 03Ch,'PowerQuest',00h
      db 040h,'VENIX 80286',00h
      db 041h,'RISC Boot',00h
      db 042h,'SFS',00h
      db 050h,'OnTrack Disk Manager RO',00h
      db 051h,'NOVEL/OnTrack Disk Manager RW',00h
      db 052h,'CPM/Microport System V386',00h
      db 054h,'OnTrack Disk Manager',00h
      db 056h,'GoldenBow VFeature',00h
      db 061h,'SpeedStor',00h
      db 063h,'Unix SysV386/BSD/GNU HURD',00h
      db 064h,'NOVELL NETWARE 286',00h
      db 065h,'NOVELL NETWARE 3.11',00h
      db 067h,'NOVELL',00h
      db 068h,'NOVELL',00h
      db 069h,'NOVELL',00h
      db 070h,'DiskSecure Multi-Boot',00h
      db 075h,'PCIX',00h
      db 080h,'MINIX',00h
      db 081h,'LINUX/MINIX',00h
      db 082h,'LINUX SWAP',00h
      db 083h,'LINUX ext2fs-xiafs',00h
      db 084h,'OS2',00h
      db 085h,'LINUX EXT',00h
      db 087h,'HPFS',00h
      db 093h,'AMOEBA',00h
      db 094h,'AMOEBA BBT',00h
      db 0A5h,'FreeBSD-386',00h
      db 0B7h,'BSDI',00h
      db 0B8h,'BSDI SWAP',00h
      db 0C1h,'DR DOS FAT12',00h
      db 0C4h,'DR DOS FAT16',00h
      db 0C6h,'DR DOS',00h
      db 0C7h,'SYRINX',00h
      db 0D5h,'NTDIOS',00h
      db 0D8h,'CPM-86',00h
      db 0DBh,'Compatible DOS-CPM/CTOS',00h
      db 0E1h,'SpeedStor FAT12',00h
      db 0E3h,'DOS RO',00h
      db 0E4h,'SpeedStor FAT16',00h
      db 0EBh,'BEOS',00h
      db 0F1h,'Storage Dimensions',00h
      db 0F2h,'DOS',00h
      db 0F4h,'Storage Dimensions',00h
      db 0FEh,'LANstep/IBM PS2 IML',00h

FAT12_ROOTSIZE EQU ((224*32)+(512-1)/512)
FAT12_ROOT  db  FAT12_ROOTSIZE dup(0)
FAT12_ROOT_SIZE EQU $ - FAT12_ROOT
FAT12_FAT   db  9*512 dup(0) ; 0feh
FAT12_FAT1_SIZE EQU $ - FAT12_FAT
FAT12_FAT2  db  9*512 dup(0)
FAT12_FAT2_SIZE EQU $ - FAT12_FAT2

; FILESYS.ASM- v 1.19 2005/03/25 12:16:06 Chris Evans

; ===========================================================================
FS_OPEN proc far
; Input : ES:SI = *filename,0
;         AX    = file mode
; Output: CF    = NZ successful 
; ===========================================================================
           clc

           ret
FS_OPEN endp

; ===========================================================================
FS_CLOSE proc far
; Input : AX = FileHandleIndex
; Output: CF = NZ successful
; ===========================================================================
           clc
           ; release filehandle, return 1 for success, 0 failure
           ret
FS_CLOSE endp

; ===========================================================================
FS_READ proc far
; Input : ES:DI = *buffer
;         AX    = filehandleindex
;         CX    = buffer size
; Output: AX    = lower AX contains byte read, if CF = NZ
; ===========================================================================
           clc
           xor ax, ax
           ; read length of bytes from file with handle and store in buffer
           ; null terminate the buffer.
           ; advance the filepointer, as your read.
           ; return number of bytes successfully read in CX or -1 for fail
           ; if already at eof then return CX = 0
           ret
FS_READ endp

; ===========================================================================
FS_WRITE proc far
; Input : AX    = Filehandle
;         ES:SI = *buffer
;         CX    = size
; ===========================================================================
           clc
           xor ax, ax
           ; write len of bytes from buffer to filehandle in overwrite mode.
           ; return bytes successfully written
           ; always write forward from current file position
           ; if number of bytes written exceeds diskspacefree then dont write
           ; to disk just return error, -1
           ret
FS_WRITE endp

; ===========================================================================
FS_SEEK proc far
; Input : AX = filehandle
;         CX = location
; ===========================================================================
           clc
           ; advance filepointer to new location from beginning of file
           ; if new location is beyond the end of file, and not in read mode
           ; then enlarge the file to new size
           ; remember to fill in any new bytes allocated to file (20h is good
           ; choice)
           ; if seeking will go beyond available disk then don't move the
           ; pointer al all, return error code
           ret
FS_SEEK endp

FS_FLUSH proc far
; Flush file buffer to disk

         ret
FS_FLUSH endp

FS_CHSIZE proc far
; extend or truncate a file 

         ret
FS_CHSIZE endp

FS_TRUNCATE proc far
; truncate a file

         ret
FS_TRUNCATE endp

; File management
FS_FSTAT proc far
; Obtain statistics on open file

         ret
FS_FSTAT endp

FS_GETATTRIB proc far
; get file attributes

         ret
FS_GETATTRIB endp

FS_MV proc far
; rename a file or directory

         ret
FS_MV endp

FS_SETATTRIB proc far
; Set file attributes

         ret
FS_SETATTRIB endp

FS_UNLINK proc far
; Delete file

         ret
FS_UNLINK endp

; Directory Management

FS_DELTREE proc far
; truncate directory tree 
     ret
FS_DELTREE endp

FS_GET_CWD proc far
; get current working directory
   ret
FS_GET_CWD endp

FS_GDONE proc far
; free gnext and gfirst resources
        _save
        lea  di, FCB_bDrvId
        mov  cx, FCB_SIZE2
        mov  bl, 20h
        call fillarray
        _restore
    ret
FS_GDONE endp

; ===========================================================================
FS_SEARCH  proc far
; DI = FILENAME.EXT
; return first entry in a directory matching specification
; ===========================================================================
        call FS_GDONE
        mov  si, di
        call fix_filename
        mov  di, si
        ;
      ;  mov  ax, FAT12_mounted
      ;  or   ax, ax
      ;  jz   FS_GF_TF16
        push cs
        pop  es
        ; push di
        mov  si, offset FAT12_ROOT
        call cs:FAT12_findfile
        jc   FS_GF_XX
        clc
        mov  FCB_Cluster, ax
        mov  bl, byte ptr cs:[DirATTR]
        mov  FCB_Attrib, bl
        mov  bx, word ptr cs:[DirTIME]
        mov  FCB_rTime, bx
        mov  bx, word ptr cs:[DirDATE]
        mov  FCB_rDate, bx
        mov  bx, word ptr cs:[DirSIZE]
        mov  word ptr FCB_lFilesize+2, bx
        mov  bx, word ptr cs:[DirSIZE+2]
        mov  word ptr FCB_lFilesize, bx
        mov  al, diskdrive
        mov  FCB_bDrvId, al
        ; pop  si
        lea  di, FCB_abName
        mov  cx, 8
        rep  movsb
        lea  di, FCB_abExt 
        mov  cx, 3
        rep  movsb
        lea  si, FCB_bDrvID
        mov  cx, 40
        jmp  FS_GF_X
FS_GF_TF16:
      ;  mov  ax, 0
    ; ;  mov  ax, FAT16_mounted
      ;  or   ax, ax
      ;  jz   FS_GF_X
        ; search fat16 root
FS_GF_XX:
        stc
FS_GF_X:
        ret
FS_SEARCH endp

; ===========================================================================
FS_CHK  proc far
; ES:SI = Buffer
;    CX = Image file size
; ===========================================================================
        ; 0090:0100h
        mov  bx, es
        mov  ds, bx
        mov  bx, si
        call crc16
        mov  ax, dx
IF (DEBUG)
        _echo '-'
        _echo '-'
        _echo '>'
        call put_hex
        _newline
        mov  cx, 128
        call cs:debugdump
        _newline
ENDIF

        ret
FS_CHK  endp

; ===========================================================================
FS_RUN   proc far
; INPUT : DI = asciiz filename/path 
; ===========================================================================
         call FS_SEARCH
         jnc  alloc_image
              _echo '6'
              stc
              ret
alloc_image:
         ;
         ; Allocate and clear PSP memory locations
         ;
         mov  ax, FCB_Cluster
         mov  cx, (0ffffh / 16)
         call AllocMemBlock
         jnc  load_alloc
              _echo '7'
              jmp  FS_ERR
load_alloc:
         push es
         pop  bx
         mov  cs:[FS_SEG], bx
         xor  bx, bx
         mov  bx, 0100h
         ; cs+0010h:0100h = 0090h:0100h
         call cs:_loadimage
         jnc  run_alloc
              _echo '8'
              jmp  FS_RUNFREE
run_alloc:
         ;mov  bx, cs:[FS_SEG]
         ;push bx
         ;pop  es
         ;xor  si, si
;IF (DEBUG)
;         mov  cx, 128
;         call cs:FS_CHK
;ENDIF
         mov  bx, cs:[FS_SEG]
         mov  es, bx
         mov  bx, 0100h
       ;  mov  cx, 64
       ;  call debugdump
         ;
         ; Run the beastie!
         ;
         mov  bx, cs:[FS_SEG]
         cli
         push ss
         push sp
         sti
         push ds
         push es
         push bx
         mov  bx, 0100h
         push bx
         retf
         pop  es
         pop  ds
         cli
         pop  sp
         pop  ss
         sti
         jmp  FS_RET
FS_RUNFREE:
         mov  ax, cs:[FS_SEG]
         mov  es, ax
         call Free_Block
         jnc  FS_RET
              _echo '9'
              jmp  FS_ERR
FS_RET:
         clc
         jmp  short FS_EXIT
FS_ERR:
         stc
FS_EXIT: ret
FS_SEG dw ?
FS_RUN   endp

FS_ISDIR proc far
; test if path is directory
        ret
FS_ISDIR endp

FS_MKDIR proc far
        ret
FS_MKDIR endp

FS_PWD   proc far
; return to current working directory
        ret
FS_PWD   endp

FS_RMDIR proc far
        ret
FS_RMDIR endp

FS_SET_CWD proc far
; set current working directory
        ret
FS_SET_CWD endp

; Disk management

FS_CLUSTER_SIZE proc far
; disks cluster size
        ret
FS_CLUSTER_SIZE endp

FS_DISKABORT proc far
; Abort all operations with out modifying disk
        ret
FS_DISKABORT endp

FS_DISKFLUSH proc far
; Flush FAT and all files to disk
        ret
FS_DISKFLUSH endp

FS_FORMAT    proc far
        ret
FS_FORMAT    endp

FS_FREE      proc far
        ret
FS_FREE      endp

FS_GETDEFDRVNO proc far
        mov  al, FS_Drive
        ret
FS_GETDEFDRVNO endp

FS_ISVOL       proc far
ret
FS_ISVOL       endp

FS_SET_DEFAULTDRIVE proc far
ret
FS_SET_DEFAULTDRIVE endp

FS_STAT proc far
ret
FS_STAT endp     

FS_WRITEPROT   proc far
; Write protected disk? 
ret
FS_WRITEPROT   endp

; Contiguous I/O functions

FS_GET_FILE_EXTENTS proc far
; get the list of clusters that make up a file

           ret
FS_GET_FILE_EXTENTS endp

FS_GET_FREE_LIST proc far
; LIST OF FREE CLUSTERS ON DRIVE

         ret
FS_GET_FREE_LIST endp

FS_RAW_READ proc far

          ret
FS_RAW_READ endp

FS_RAW_WRITE proc far

          ret
FS_RAW_WRITE endp

FS_EXTEND_FILE proc far

              ret
FS_EXTEND_FILE endp

; ===========================================================================
truename proc far
; Return the canonical filename of a file in the file system.
; c:\nxdos\src\..\archives\..\..\config.sys = c:\config.sys
; ===========================================================================

      ret
truename endp

IOCtrl proc far
; AX = Command
; BX = Parameter

          ret
IOCtrl endp

sectorwrite proc far
; ES:BX = pRAMAddr
;    AX = StartSector
;    CX = How Many Sectors
        mov  di, cx
        call writeDisk
           ret
sectorwrite endp

sectorread proc far
; ES:BX = pRAMAddr
;    AX = StartSector
;    CX = How Many Sectors
        mov  di, cx
        call readDisk
           ret
sectorread endp

diskclose proc far
         mov  ax, FAT12_mounted
         or   ax, ax
         jz   dc_try_FAT16
         mov  dl, FS_Drive
         call FAT12_unmount
         mov  ax, 12
         clc
         jmp  diskclose1
dc_try_FAT16:
         mov  ax, 0
      ;  mov  ax, FAT16_mounted
         or   ax, ax
         jz   diskclose_Err
         mov  dl, FS_Drive
      ;  call FAT16_unmount
         mov  ax, 16
         clc
         jmp  diskclose1
diskclose_Err:
         stc
diskclose1:
         ret
           ret
diskclose endp

diskopen proc far
         push dx
         call FAT12_Mount
         pop  dx
         jc   do_try_FAT16
         mov  FS_Drive, dl
         mov  ax, 12
         clc
         jmp  diskopen1
do_try_FAT16:
       ;  call FAT16_Mount
         jc   diskopen_Err
         mov  FS_Drive, dl
         clc
         mov  ax, 16
         jmp  diskopen1
diskopen_Err:
         stc
diskopen1:
         ret
diskopen endp

driverrelease proc far

           ret
driverrelease endp

; ===========================================================================
driverinit proc far                                          ; INITIALIZATION
; ===========================================================================
        call cs:diskinit
        jc   mounterror
IFS:
        ; load filesystem handlers and system device drivers
        mov  dl, cs:bootdrive
        call diskopen
        jnc  mounted
mounterror:
        _print cs:ErrMsg0003, 1
        stc
        jmp  short fsinitx
mounted:
        ; call cs:driverrelease
        clc
fsinitx:
        ret
driverinit endp

; DIRECTORY MANAGEMENT
; HELPER FUNCTIONS
; BLOCK BUFFERING
; STRING PROCESSING

; ===========================================================================
; Filesys.asm Data Area
; ===========================================================================

filename_buf db 127 dup(0)
FS_wtemp     dw ?
FS_Drive     db 0
driveheads   db 2
driveSPT     db 18

FAT_CACHE_SIZE dw 0
NBLKBUFFS      dw 0  ; blocks in buffer pool
NUM_USERS      dw 4  ; maximum number of tasks that my access the file system
NUSERFILES     dw 10 ; maximum number of open files

; Maximum cluster count
FAT_TYPE_FAT12 dw 0ff0h      ; Minimum for 12 and 16 just divide max by 2
FAT_TYPE_FAT16 dw 0fff0h
FAT_TYPE_FAT32 dd 0ffffff0h  ; Minimum is FAT16 maximum

D_LFN      db readonly+hidden+system+vol_id
D_ALL      db readonly+hidden+system+dir+archive

; FAT file name constants
FNAME_SIZE db 8
FEXT_SIZE  db 3

; FAT deleted flag
DELETED     db 5                  ; if first char, delete file 
EXT_DELETED db 0e5h               ; external deleted flag

; FAT cluster special flags
FAT_FREE   db 0,0,0

FS_SEARCH_FCB:                           ; FAT file system directory entry
          FCB_bDrvID     db ?            ; sysdirentry
          FCB_abName     db 8 dup(0)     ; Filename + extension in FCB format
          FCB_abExt      db 3 dup(0)
          FCB_wCurBlock  dw 0            
          FCB_wRecSize   dw 0
          FCB_lFileSize  dd 0
          FCB_rDate      dw ?
          FCB_rTime      dw ?
          FCB_res        db 8 dup(0)
          FCB_wCurRecNo  dw ?
          FCB_lRndmRecNo dd ?
          FCB_SIZE       EQU 37
          FCB_Cluster    dw ?            ; Starting cluster 
          FCB_Attrib     db 0            ; File Attribute
          FCB_SIZE2      EQU 40

; Who at Microsoft came up with this mad structure?!?!
lfnentry    db 0 
lfnid       db 0                    ; Sequence number for this LFN entry
lfnname5    db 5 dup(0)             ; First 5 characters of LFN
lfnattrib   db 0                    ; LFN attribute, should be D_LFN == 0x0f
lfnreservd1 db 0
lfnchecksum db 0                    ;Checksum for the corresponding 8.3 name
lfnname_n6  db 6 dup(0)             ; Next 6 characters of LFN
lfnreservd2 dw 0
lfnname_l2  dw 2 dup(0)             ; Last 2 characters of LFN
; You know, I like 4-DOS method of long filenames, the descript.ion file
; filename.ext description blah blah blah

; DOUBLESPACE/DRIVESPACE STACKER drives are just DOS FORMATTED FATS with
; a CVS index and volume file stored in root directory (with is host drive).

; Windows 95/98 and Windows NT/2000 store long filenames on FAT in special
; directory entries with set attributes ReadOnly, Hidden, System and Volume,
; so if you access FAT volume from DOS you don't see these "files". These
; special entries have this mad structure:

vfat_slotseq      db ? ; byte              sequence number for slot
lfn_first5fn      db 10 dup(0) ; string(10)        first 5 characters in name
lfn_attrib        db ? ; byte              attribute byte
lfn_reserved      db 0 ; byte              always 0
lfn83aliaschksum  db ? ; byte              checksum for 8.3 alias
lfn_next6fn       db 12 dup(0) ; string(12)        6 more characters in name
lfn_startcluster  dw 0 ; word           starting cluster number, 0 in long slots
lfn_last2fn       db 4 dup(0); string(4)         last 2 characters in name

; Problem occur when you delete or modify file with long name from system
; without VFAT support, because only DOS 8+3 entry will be deleted or
; modified. Scandisk from Windows 95/98 can repair this problem.

UMSDOS_LFN        db '--linux.-.---',0
; Linux UMSDOS stores longfile names in the --linux-.--- file in each
; directory. I like this way better, instead of the hack MS did.
os2extattrib      db '\ea data. sf',0
os2extattrib2     db '\wp root. sf',0

_fsreadonly  db 0 ; if not exist, fail. ptr = 0
_fswriteonly db 1 ; if exist, erase it or create new. ptr = eof
_fsappend    db 2 ; if exist, open or create new. Initilize ptr to eof.
_fsreadwrite db 3 ; open/create file, initialize ptr to ofs 0

;General File System Functions
;f_getversion    Get File System Version Number
;f_initvolume    Initialize a Volume
;f_delvolume     Removes a Volume from the System
;f_get_volume_count      Gets Number of Active Volumes
;f_get_volume_list       List the Active Volumes

;Drive Functions
;f_mountdrive    Mount a Drive
;f_format        Format a Drive
;f_hardformat    Hard Format a Drive
;f_getfreespace  Get Usage Information From a Drive
;f_getdrive      Get Current Drive Number
;f_chdrive       Change Current Drive
;f_setlabel      Set the Label on a Volume
;f_getlabel      Get the Label from a Volume

;Directory Functions
;f_getcwd        Get Current Working Directory
;f_getdcwd       Get a Drives' Current Working Directory
;f_mkdir Make a Directory
;f_chdir Change Directory
;f_rmdir Remove a Directory

;File Access Functions
;f_open  Open a File
;f_close Close a File
;f_write Write ro a File
;f_read  Read from a File
;f_seek  Set Position in a File
;f_tell  Get Current File Position
;f_eof   Check if at End of File
;f_rewind        Move to Start of File
;f_putc  Put a Character to a File
;f_getc  Get a Character from a File
;f_truncate      Truncates a File

;File Control Functions
;f_rename        Rename a File of Directory
;f_delete        Delete a File or Directory
;f_filelength    get the Current Length of a File
;f_findfirst     Find a File or Directory in the Specified Path
;f_findnext      Find Next File or Directory in the Specified Path
;f_settimedate   Set the Time and Date on a File or Directory
;f_gettimedate   Get the Time and Date from a File or Directory
;f_setattr       Set the Attributes on a File or Directory
;f_getattr       Get the Attributes from a File or Directory

        _print msg1, 1
        call drivecheck
        jc no_media
           call driveinit
           ; 
           call readmbr
           call init
           ; show debug info
           ; create a file on the root directory
           xor  bx, bx
           lea  si, newfiletxt
           call mkfile
           mov  filehandle, ax
           lea  si, datatext
           call wrfile
           mov  faterrcode, ax
           mov  ax, filehandle
           call lsfile
        ret
no_media:
        _print msg2, 1
        ret

.386

initdevice proc far
; static function (only called once), needs BIOS Parameter Block
; writes "First_Data_Sector" into Drive_Buffer
; (BPB_NumFATs * FATSz)

Get_FATSz:
; static function (only called once), needs BIOS Parameter Block
; returns the FATSz
; If(BPB_FATSz16 != 0)
;   FATSz = BPB_FATSz16;
; Else
;   FATSz = BPB_FATSz32;
movzx eax,word [API_Buffer+22]    ; eax = BPB_FATSz16
or eax,eax                        ; eax = 0 ?
jnz Get_FATSz_Exit                ; if not zero, it's FATSz16
; else it's FATSz32
mov eax,[API_Buffer+36]						; eax = BPB_FATSz32
Get_FATSz_Exit:
ret
call Get_FATSz
movzx ebx,byte [API_Buffer+16]
mul ebx
; BPB_ResvdSecCnt + (BPB_NumFATs * FATSz)
movzx ecx,word [API_Buffer+14]
add ecx,eax
; BPB_ResvdSecCnt + (BPB_NumFATs * FATSz) + RootDirSectors
call Get_Root_Dir_Sectors
add eax,ecx
mov [Drive_Buffer+12h],eax					; set "first data sector"
ret
initdevice endp

fatinit proc far
    push ds
    push es
    pusha
    mov  bx, fat_code_sel
    call initdevice
    mov  ax, cs
    mov  ds, ax
    mov  es, ax
    mov  si, offset fa12_name
    mov  di, offset fa12_ctrl
    call registerfilesystem
    mov  si, offset fa16_name
    mov  di, offset fa16_ctrl
    call registerfilesystem
    mov  si, offset fa32_name
    mov  di, offset fa32_ctrl
    call registerfilesystem
    popa
    pop  es
    pop  ds  
    ret
fatinit endp

FAT_TYPE proc far
; IN : DL = Drive to determine the FAT type
; OUT: Drive_buffer updated
        ; read BPB readdisk i, 1, open_file_destroy
        ; copy values of the BIOS Parameter Block
        ; set sectors per cluster
        movzx bx, byte ptr [api_buffer+13]
        mov   [Drive_Buffer+0Bh], bl
        movzx ax, byte ptr [api_buffer+11]
        shr   ax, 2
        mov   [Drive_Buffer+0ch], ax
        ; set "dwords per Cluster"
        mul bx                 ; Sectors per Cluster * words per Sector
        mov [Drive_Buffer+0Eh], ax
        ; set "File Allocation Table" (= "Reserved Sector Count")
        mov ax, word [API_Buffer+14; FAT Offset = Reserved Sector Count
        mov [Drive_Buffer+1Ch],ax
        ; set the value "First_Data_Sector"
        call Get_First_Data_Sector
        ; ---- OBSOLETE ----
        ; set the value "entries per cluster"
        ;call Get_Entry_Count
        ; ---- OBSOLETE ----
        ; set the value "FAT Type"
        call Get_Count_of_Clusters
        ; set the values "first root directory sector" and "last root directory sector"
        call Get_FirstRootDirSecNum
         ret
FAT_TYPE endp

Get_Count_of_Clusters:

; static function (only called once), needs BIOS Parameter Block
; writes "FAT Type" into Drive_Buffer

; CountofClusters = DataSec / BPB_SecPerClus
call Get_DataSec

xor edx,edx
movzx ebx,byte [Drive_Buffer+0Bh]
div ebx


; C++

; If(CountofClusters < 4085)
;   /* Volume is FAT12 */
; else
;   if(CountofClusters < 65525)
;     /* Volume is FAT16 */
;   else
;     /* Volume is FAT32 */


; Assembler

mov [Drive_Buffer+0Ah],byte 12					; suppose FAT Type is FAT12
cmp eax,4085
jl Determine_FAT_Type_Exit					; if CountofClusters < 4085 exit

mov [Drive_Buffer+0Ah],byte 16					; suppose FAT Type is FAT16
cmp eax,65525
jl Determine_FAT_Type_Exit					; if CountofClusters < 65525 exit

; else FAT Type is FAT32
mov [Drive_Buffer+0Ah],byte 32

Determine_FAT_Type_Exit:

ret

Get_DataSec:

; static function (only called once), needs BIOS Parameter Block and returns static value
; return = DataSec

; DataSec = TotSec - FirstDataSector
call Get_TotSec
sub eax,[Drive_Buffer+12h]

ret

Get_TotSec:

; static function (only called once), needs BIOS Parameter Block and returns static value
; return = TotSec

; C++

; If(BPB_TotSec16 != 0)
;   TotSec = BPB_TotSec16;
; Else
;   TotSec = BPB_TotSec32;


; Assembler

movzx eax,word [API_Buffer+19]					; eax = BPB_TotSec16

or eax,eax							; eax = 0 ?
jnz Get_TotSec_Exit						; if not zero, it's TotSec16

; else it's TotSec32
mov eax,[API_Buffer+32]						; eax = BPB_TotSec32

Get_TotSec_Exit:

ret

Get_Root_Dir_Sectors:

; static function (only called once), needs BIOS Parameter Block
; return = Root Directory Sectors

; (BPB_RootEntCnt * 32)
movzx eax,word [API_Buffer+17]
imul eax,32

; eax = zero ? (only on FAT32 drives)
or eax,eax
jz Get_Root_Dir_Sectors_Exit

; (BPB_RootEntCnt * 32) + (BPB_BytsPerSec - 1)
movzx edx,word [API_Buffer+11]
dec edx
add eax,edx

; ((BPB_RootEntCnt * 32) + (BPB_BytsPerSec - 1)) / BPB_BytsPerSec
xor edx,edx
movzx ebx,word [API_Buffer+11]
div ebx

Get_Root_Dir_Sectors_Exit:

ret






Get_FirstRootDirSecNum:

; static function (only called once), needs BIOS Parameter Block
; writes "first root directory sector" and "last root directory sector" into Drive_Buffer


; share function into FAT12/16 and FAT32
cmp [Drive_Buffer+0Ah],byte 32
je Get_FirstRootDirSecNum_FAT32


; FirstRootDirSecNum = BPB_ResvdSecCnt + (BPB_NumFATs * BPB_FATSz);
call Get_FATSz
movzx ebx,byte [API_Buffer+16]
mul ebx								; BPB_FATSz * BPB_NumFATs

movzx ebx,word [API_Buffer+14]
add eax,ebx							; BPB_ResvdSecCnt + (BPB_NumFATs * BPB_FATSz16)

mov [Drive_Buffer+16h],eax					; set "first root directory sector"

call Get_Root_Dir_Sectors					; calculate last root directory sector (add values)
add ax,[Drive_Buffer+16h]
mov [Drive_Buffer+1Ah],ax					; set "last root directory sector"

ret 


Get_FirstRootDirSecNum_FAT32:
; FirstRootDirSecNum = BPB_RootClus * Sectors per Cluster
mov eax,[API_Buffer+44]
imul eax,[API_Buffer+13]

mov [Drive_Buffer+16h],eax					; set "first root directory sector"
mov [Drive_Buffer+1Ah],word 00h					; set "last root directory sector" (on FAT32 not avl)

ret





%if 0 = 1

Get_Entry_Count:

; static function (only called once), needs BIOS Parameter Block
; writes "entries per cluster" into Drive_Buffer

; calculate the count of entrys per sector (BPB_BytsPerSec / Entry_Size)
movzx eax,word [API_Buffer+11]
xor edx,edx
mov ebx,32
div ebx

; calculate the count of entries per cluster
movzx ebx,byte [API_Buffer+13]
mul ebx								; * sectors per cluster

mov [Drive_Buffer+1Ch],eax					; set "entries per cluster"

ret

%endif







; non static functions, which are called more than once and returns non static values



First_Sector_of_Cluster:

; normal FAT32 ?
cmp [Drive_Buffer+0Ah],byte 32
je First_Sector_of_Cluster_normal

; normal cluster type ?
cmp [cluster_type],byte 18h
jne First_Sector_of_Cluster_normal

; else the cluster is a sector in the root directory area
; (do nothing)

ret


; eax = cluster n
; return = first sector of cluster n

; n - 2
First_Sector_of_Cluster_normal:
dec eax
dec eax

; (n - 2) * BPB_SecPerClus
movzx ebx,byte [Drive_Buffer+0Bh]
mul ebx

; ((n - 2) * BPB_SecPerClus) + FirstDataSector
add eax,[Drive_Buffer+12h]

ret







Get_FATSecNum_Offset:

; eax = cluster number n

; return = sector to read (element of the File Allocation Table)
; reutrn(edx) = Offset in the EFFECTIVE sector


; if FAT12 is set, and offset streches over boundarys, the carry flag is set
; in all other cases (non FAT12 or in boundarys) the carry flag is cleared



; If(FATType == FAT16)
;   FATOffset = N * 2;
; Else
;   if (FATType == FAT32)
;     FATOffset = N * 4;
;
; ThisFATSecNum = BPB_ResvdSecCnt + (FATOffset / BPB_BytsPerSec);
; ThisFATEntOffset = REM(FATOffset / BPB_BytsPerSec);


; special FAT12?
cmp [Drive_Buffer+0Ah],byte 12
je Get_FATSecNum_Offset_FAT12

; calculate the FATOffset (N * 2 or 4)
shl eax,1							; suppose FAT Type is 16
cmp [Drive_Buffer+0Ah],byte 16
je Calculate_ThisFAT						; if FAT 16, exit

shl eax,1							; else mul one more with 2 (in effect eax * 4)

Calculate_ThisFAT:
; FATOffset / BPB_BytsPerSec
xor edx,edx
sub ebx,ebx
imul bx, word [Drive_Buffer+0Ch], 4				; bx = bytes per sector
div ebx								; return(edx) = Offset in sector

; BPB_ResvdSecCnt + (FATOffset / BPB_BytsPerSec)
movzx ebx,word [Drive_Buffer+1Ch]
add eax,ebx							; return = sector

clc
ret

Get_FATSecNum_Offset_FAT12:

; FATOffset = N + (N / 2);
; /* Multiply by 1.5 without using floating point, the divide by 2 rounds DOWN */
; ThisFATSecNum = BPB_ResvdSecCnt + (FATOffset / BPB_BytsPerSec);
; ThisFATEntOffset = REM(FATOffset / BPB_BytsPerSec);
;
; We now have to check for the sector boundary case:
;
; If(ThisFATEntOffset == (BPB_BytsPerSec – 1)) {
; /* This cluster access spans a sector boundary in the FAT */
; /* There are a number of strategies to handling this. The */
; /* easiest is to always load FAT sectors into memory */
; /* in pairs if the volume is FAT12 (if you want to load */
; /* FAT sector N, you also load FAT sector N+1 immediately */
; /* following it in memory unless sector N is the last FAT */
; /* sector). It is assumed that this is the strategy used here */
; /* which makes this if test for a sector boundary span */
; /* unnecessary. */
; }

; the same code as by "Calculate_ThisFAT"
; FATOffset / BPB_BytsPerSec
xor edx,edx
sub ebx,ebx
imul bx, word [Drive_Buffer+0Ch], 4				; bx = bytes per sector
div ebx								; return(edx) = Offset in sector

; BPB_ResvdSecCnt + (FATOffset / BPB_BytsPerSec)
movzx ebx,word [Drive_Buffer+1Ch]
add eax,ebx							; return = sector

; If(ThisFATEntOffset == (BPB_BytsPerSec – 1)) then boundary strech
dec ebx
cmp edx,ebx
je Get_FATSecNum_Offset_FAT12_boundary

clc
ret

; if here, the entry is over boundarys, so set cf to indicate it
Get_FATSecNum_Offset_FAT12_boundary:
stc
ret

Read_FAT_entry:

; eax = Cluster

mov [Temp],eax						; store the Cluster temporary (for FAT12)

; special FAT12?
cmp [Drive_Buffer+0Ah],byte 12
je Read_FAT12_entry


call Get_FATSecNum_Offset				; get the Sector and the Offset of the entry

; read the SST
Read eax, 1, Read_FAT_Entry_Exit


; now share the function into FAT16 and FAT32
cmp [Drive_Buffer+0Ah],byte 32
je Read_FAT32_entry

lea edi,[API_Buffer+edx]				; edi = Offset in the Buffer
movzx eax,word [edi]					; eax = searched entry
jmp Read_FAT_Entry_succ


Read_FAT32_entry:
lea edi,[API_Buffer+edx]				; edi = Offset in the Buffer
mov eax,[edi]						; eax = searched entry
;jmp Read_FAT_Entry_succ


Read_FAT_Entry_succ:
clc

Read_FAT_Entry_Exit:
ret



Read_FAT12_entry:					; now: same code as by Read_FAT16/32_entry

call Get_FATSecNum_Offset				; get the Sector and the Offset of the entry
jc Read_FAT12_entry_boundary				; if the entry streches about sector boundarys, handle special

; read the SST
Read eax, 1, Read_FAT_Entry_Exit

lea edi,[API_Buffer+edx*2]				; edi = Offset in the Buffer
movzx eax,word [edi]					; eax = searched (word) entry

; odd?
test [Temp],dword 01b
je Read_FAT12_entry_odd

; if here, we want the low 12 bits of the word, so clear the other (and exit)
or eax,0F000h
xor eax,0F000h
jmp Read_FAT_Entry_succ

Read_FAT12_entry_odd:
; if here, we want only the high 12 bits of the word, so shift right about 4 bits (and exit)
shr eax,4
jmp Read_FAT_Entry_succ


Read_FAT12_entry_boundary:				; if here, the entry streches over sector boundarys

mov [Temp],eax						; save the sector

; read the SST
Read eax, 1, Read_FAT_Entry_Exit

lea edi,[API_Buffer+edx*2]				; edi = Offset in the Buffer

; restore the needed sector (+ 1 !)
mov eax,[Temp]
inc eax

; read the LEAST 4 bits of the ENTRY, which are - in fact - the HIGH NIBBLE bits of the read byte
mov bl,[edi]						; just use bl instead of al; bl wouldn't change by the ToasterOS function definition

; read the next Sector of the SST
Read eax, 1, Read_FAT_Entry_Exit

; now merge the 4 bits in bl and the byte at API_Buffer + 0
movzx eax,byte [API_Buffer+0]
shl eax,4						; shl the byte (by 4 bits)

shr bl,4						; the high nibble of the byte is the low nibble we need

or al,bl						; add the entry (end exit)
clc

ret

Write_FAT_entry:

; eax = Cluster
; ebx = value; or implicit used bx/b12 for using FAT12, FAT16
;   the caller shouldn't determine the size of using, because all FAT functions return 32 bit (probably zero extended) values

; maintaily the code (explicit the initialising process) is the same as by Read_FAT_Entry

mov [Temp],eax						; store the Cluster temporary (for FAT12)

; special FAT12?
cmp [Drive_Buffer+0Ah],byte 12
je Write_FAT12_entry

push ebx						; store ebx, it will be used in Get_FATSecNum_Offset
call Get_FATSecNum_Offset				; get the Sector and the Offset of the entry
pop ebx							; restore ebx

add eax,[Drive_Buffer+1Ch]				; eax = sector to modify
mov ecx,eax						; store to-change sector

; read the SST
Read eax, 1, Write_FAT_Entry_Exit


; now share the function into FAT16 and FAT32
cmp [Drive_Buffer+0Ah],byte 32
je Write_FAT32_entry

lea edi,[API_Buffer+edx*2]				; edi = Offset in the Buffer
mov [edi],bx						; store new data into the entry
jmp Write_entry

Write_FAT32_entry:
lea edi,[API_Buffer+edx*4]				; edi = Offset in the Buffer
mov [edi],ebx						; store new data (now 32 bit) into the entry


Write_entry:						; write the to-change sector
Write_into ecx, 1


Write_FAT_Entry_Exit:

ret



Write_FAT12_entry:					; now: same code as by Read/Write_FAT16/32_entry

push ebx						; store ebx, it will be used in Get_FATSecNum_Offset
call Get_FATSecNum_Offset				; get the Sector and the Offset of the entry
pop ebx							; restore ebx
jc Write_FAT12_entry_boundary				; if the entry streches about sector boundarys, handle special

add eax,[Drive_Buffer+1Ch]				; eax = sector to modify
mov ecx,eax

; read the SST
Read eax, 1, Write_FAT_Entry_Exit

lea edi,[API_Buffer+edx*2]				; edi = Offset in the Buffer
mov ax,[edi]						; eax = searched (word) entry


; odd?
test [Temp],dword 01b
je Write_FAT12_entry_odd

; if here, we want to change the low 12 bits of the word, so clear the other
or ebx,0F000h
xor ebx,0F000h						; clear high 4 bits of the value to store (also: or)
or ax,bx
mov ax,[edi]
jmp Write_entry

Write_FAT12_entry_odd:
; if here, we want to change only the high 12 bits of the word, so shift right about 4 bits
or ebx,0F000h
xor ebx,0F000h						; clear high 4 bits of the value to store (also: or)
shr ebx,4						; shift ebx, the value
or ax,bx
mov ax,[edi]
jmp Write_entry


Write_FAT12_entry_boundary:				; if here, the entry streches over sector boundarys

add eax,[Drive_Buffer+1Ch]				; eax = sector to modify
mov ecx,eax

; read the SST
Read eax, 1, Write_FAT_Entry_Exit

lea edi,[API_Buffer+edx*2]				; edi = Offset in the Buffer

; modify the top 4 bits of the byte, which are the least 4 bits of the entry
mov al,[edi]
mov dl,bl
or dl,00001111b
xor dl,00001111b
or al,bl
mov [edi],al

; write the first to-change sector
Write_into ecx, 1, Write_FAT_Entry_Exit


; restore the needed sector (+ 1 !)
inc ecx

; read the next Sector of the SST
Read ecx, 1, Write_FAT_Entry_data_losing


; store the remaining byte of the entry
shr ebx,4
mov [API_Buffer+0],bl

; write the second to-change sector
Write_into ecx, 1, Write_FAT_Entry_data_losing

jmp Write_FAT_Entry_Exit



Write_FAT_Entry_data_losing:
; comes here if there is an error while accessing (reading or writting) a FAT12 entry streches over sector boundarys

; contact security guard/change to security mode?
; ?

ret

Get_Next_Cluster:

; eax = Cluster
;   even on FAT12/16 root directorys it's interpreted as cluster size

; this function gets the next CLUSTER of any directory (specified by the given cluster), included the Root Directory
; cf is set if EOF is reached, then return = entry


; share into FAT12/16 and FAT32
cmp [Drive_Buffer+0Ah],byte 32
je Get_Next_Cluster_FAT32


; check if cluster is a sector of the FAT12/16 root directory
cmp [cluster_type],byte 18h
jne Get_Next_Cluster_no_root_directory


; if here, the cluster is a sector of the root directory
; if (Sectors per Cluster + Current Cluster) > Last_Root_Dir_Sector then EOC is reached
push bx
movzx bx,byte [Drive_Buffer+0Bh]
add ax,bx							; add Sectors per Cluster
pop bx

cmp [Drive_Buffer+1Ah],ax					; eax > last sector of the root directory?
jnl Get_Next_Cluster_RD_inc

; if here, the last sector of the root directory is reached, so there is no more cluster
mov eax,0FFFFFFFFh						; EOC is reached
stc
ret

Get_Next_Cluster_RD_inc:					; normal exit (already incremented)
; eax is already up to date (because of the add Sectors per Cluster)
clc
ret



Get_Next_Cluster_no_root_directory:				; share into FAT12 and FAT16
cmp [Drive_Buffer+0Ah],byte 16
je Get_Next_Cluster_FAT16


Get_Next_Cluster_FAT12:
call Read_FAT_entry

; EOF reached?
cmp eax,0FF8h
jl Get_Next_Cluster_succ					; if (eax < 0FF8h) then it's not the end

; else set cf (and exit)
stc
ret


Get_Next_Cluster_FAT16:
call Read_FAT_entry

; EOF reached?
cmp eax,0FFF8h
jl Get_Next_Cluster_succ					; if (eax < 0FFF8h) then it's not the end

; else set cf (and exit)
stc
ret


Get_Next_Cluster_FAT32:
call Read_FAT_entry

; the function returns a (32 bit) pointer, but on FAT32 entries there are only 28 bits used, so clear the top 4 bits
or eax,0F0000000h
xor eax,0F0000000h

; EOF reached?
cmp eax,0FFFFFF8h
jl Get_Next_Cluster_succ					; if (eax < 0FFFFFF8h) then it's not the end

; else set cf (and exit)
stc
ret


Get_Next_Cluster_succ:

clc
ret

.286c
