; ===========================================================================
; FAT.ASM - Chris Evans 2008 
; ===========================================================================

TEST_BUFFER_BLOCKS equ 16
BLOCK_SIZE         equ 1
HARD_SECTOR_SIZE   equ 512
SECTORS_PER_BLOCK  equ (BLOCK_SIZE / HARD_SECTOR_SIZE)
ATTR_VOLUME        equ 8
FAT_EOF            equ 0ffffff8b
FAT_BAD            equ 0ffffff7b
MSDOS_EXT_SIGN     equ 29h
BOOTSIGN           equ 0aa55h
FAT12_THRESHOLD    equ 4085
BOOTCODE_SIZE      equ 448
BOOTCODE_SIZEFAT32 equ 420

bpbBytesPerSector   EQU 512
sectorsperfat       EQU 9
RETRY               EQU 5
FATSIZE             DW ?
bpbHeadsPerCylinder dw ? 
bpbSectorsPerTrack  dw ?
bpbHiddenSectors    dd ?
bpbReservedSectors  dw ?
drive               db 0
FATBUF_SEG          dw 0
FATBUF_OFS          dw 0
ProgramName         db 'COMMAND COM',0

FS_OTHER       db 'Unknown',0
FS_FAT12       db 'FAT12',0
FS_XENIXROOT   db 'Xenix root',0
FS_XENIXUSR    db 'Xenix /usr',0
FS_FAT16       db 'FAT16 (Old School)',0
FS_DOSEXT      db 'DOS Extended',0
FS_FAT16B      db 'FAT16',0
FS_NTFS        db 'NTFS',0
FS_OS2AIX      db 'OS/2 or AIX',0
FS_QNX         db 'QNX',0
FS_OS2_BOOTM   db 'OS/2 Boot Manager',0
FS_FAT32       db 'FAT32',0
FS_FAT32LBA    db 'FAT32 LBA',0
FS_FAT16LBA    db 'FAT16 LBA',0
FS_EXTLBA      db 'Extended LBA',0
FS_CPAQDIAG    db 'Compaq Diagnostic',0
FS_AST_S2D     db 'AST Suspend-to-Disk',0
FS_NEC_DOS     db 'NEC DOS',0
FS_OS2_WARP    db 'OS/2 Warp',0
FS_THEOS       db 'THEOS',0
FS_PLAN9       db 'Plan 9',0
FS_PMAGIC_R    db 'PartitionMagic Recovery',0
FS_VENIX       db 'Venix',0
FS_DRDOS_CRUFT db 'DRDOS Cruft',0
FS_GOBACK      db 'GoBack',0
FS_BOOT_US     db 'Boot-US',0 
FS_EUMEL_ELAN  db 'EUMEL/ELAN',0
FS_ADAOS       db 'AdaOs',0
FS_OBERON      db 'Oberon',0
FS_ONTRACK_DM  db 'OnTrack Disk Manager',0
FS_NOVELL      db 'Novell',0
FS_CP_M        db 'CP/M',0
FS_DISKMANAGER db 'Disk Manager',0
FS_DISKMGR_DO  db 'Disk Manager Drive Overlay',0
FS_EZ_DRIVE    db 'EZ-Drive',0
FS_GOLDENBOW   db 'Golden Bow DOS',0
FS_DRIVEPRO    db 'DrivePro',0
FS_SPEEDSTOR   db 'SpeedStor',0
FS_UNIXVMGNU   db 'Unix System V / Mach / GNU Hurd',0
FS_PC_ARMOUR   db 'PC-ARMOUR',0
FS_NOVELL_NW   db 'Novell Netware',0
;
bad_media    db 'Media not inserted o ready.',0ffh,0

partition    dw  01h, FS_FAT12
             dw  02h, FS_XENIXROOT  
             dw  03h, FS_XENIXUSR
             dw  04h, FS_FAT16
             dw  05h, FS_DOSEXT
             dw  06h, FS_FAT16B
             dw  07h, FS_NTFS
             dw  08h, FS_OS2AIX
             dw  09h, FS_QNX
             dw  11h, FS_FAT12
             dw  14h, FS_FAT16
             dw  16h, FS_FAT16
             dw  17h, FS_NTFS
             dw  0ah, FS_OS2_BOOTM
             dw  0bh, FS_FAT32
             dw  0ch, FS_FAT32LBA
             dw  0eh, FS_FAT16LBA
             dw  0fh, FS_EXTLBA
             dw  12h, FS_CPAQDIAG
             dw  18h, FS_AST_S2D
             dw  1bh, FS_FAT32
             dw  1ch, FS_FAT32LBA
             dw  1eh, FS_FAT16LBA
             dw  24h, FS_NEC_DOS
             dw  35h, FS_OS2_WARP
             dw  38h, FS_THEOS
             dw  39h, FS_PLAN9
             dw  3ah, FS_THEOS
             dw  3ch, FS_PMAGIC_R
             dw  40h, FS_VENIX
             dw  41h, FS_DRDOS_CRUFT
             dw  42h, FS_DRDOS_CRUFT
             dw  43h, FS_DRDOS_CRUFT
             dw  44h, FS_GOBACK
             dw  45h, FS_BOOT_US
             dw  46h, FS_EUMEL_ELAN
             dw  47h, FS_EUMEL_ELAN
             dw  48h, FS_EUMEL_ELAN
             dw  4ah, FS_ADAOS
             dw  4ch, FS_OBERON
             dw  4dh, FS_QNX
             dw  4eh, FS_QNX
             dw  4fh, FS_OBERON
             dw  50h, FS_ONTRACK_DM
	     dw  51h, FS_NOVELL
	     dw  52h, FS_CP_M
	     dw  53h, FS_DISKMANAGER
	     dw  54h, FS_DISKMGR_DO
             dw  55h, FS_EZ_DRIVE
             dw  56h, FS_GOLDENBOW
             dw  57h, FS_DRIVEPRO
             dw  61h, FS_SPEEDSTOR
             dw  63h, FS_UNIXVMGNU
             dw  64h, FS_PC_ARMOUR
             dw  65h, FS_NOVELL_NW
             dw  67h, FS_NOVELL_NW
             dw  68h, FS_NOVELL_NW
             dw  69h, FS_NOVELL_NW
             dw  0, 0
partitions   dw 59
;
fatdrive     db ?
mbrbuf       db 512 dup(0)
bootbuf      db 512 dup(0)

; =======================================================================
load_FAT16 proc far
; Reserve some memory for a FAT16 image (128KB max) and load it whole
; ES:BX = FAT Buffer
; =======================================================================
     mov  ax, es
     mov  word ptr cs:FATBUF_SEG, ax
     mov  word ptr cs:FATBUF_OFS, bx
     ;
     mov  ax, bpbBytesPerSector
     shr  ax, 4                 ; 4096   128K 
     mov  cx, sectorsperfat 
     mul  cx                    ; AX * CX   FATSIZE*FATSECTORS
     mov  word ptr cs:FATSIZE, ax
     ;
     ; ES:BX = FAT BUFFER
     ;
     mov  ax, word ptr cs:[bpbHiddenSectors]
     mov  dx, word ptr cs:[bpbHiddenSectors+2]
     add  ax, word ptr cs:[bpbReservedSectors]
     adc  dx, bx         
     ; dx:ax = LBA
     call cs:readsector     
     ret
load_FAT16 endp

; =======================================================================
load_ROOT16 proc far
; Reserve some memory for a root directory and load it whole (16KB max)
;      ES = ROOT segment
; Returns :
;      AX:DX = LBA 
; =======================================================================
     mov     ax, 32
     mov     si, [bpbRootEntries]
     mul     si
     div     word ptr [bpbBytesPerSector]
     mov     cx, ax                  ; cx = root directory size in sectors

     mov     al, [bpbNumberOfFATs]
     cbw
     mul     word ptr [bpbSectorsPerFAT]
     add     ax, bx
     adc     dx, di                  ; dx:ax = LBA

     ; ES:0
     xor  bx, bx      ; es:bx -> buffer for root directory

     call    cs:ReadSector
     add     ax, cx
     adc     dx, bx                  ; adjust LBA for cluster data
     ; push    dx
     ; push    ax                      ; push LBA for data (1st parameter)
     ret
load_ROOT16 endp

; =============================================================
FindName proc far
; Input:  DS:SI -> file name (11 chars) 
;         ES:DI -> root directory array 
;         DX = number of root entries   
; Output: SI = cluster number           
; =============================================================
        mov     cx, 11
FindNameCycle:
        cmp     byte [es:di], ch
        je      FindNameFailed          ; end of root directory
        pusha
        repe    cmpsb
        popa
        je      FindNameFound
        add     di, 32
        dec     dx
        jnz     FindNameCycle           ; next root entry
FindNameFailed:
        stc
        jmp  short FFNX
FindNameFound:
        clc
        mov     si, [es:di+1Ah]         ; si = cluster no.
FFNX:
    ret 
FindName endp

; =======================================================================
ReadSector proc far
; Reads a sector using BIOS Int 13h fn 2
; Input:  DX:AX = LBA                    
;         CX    = sector count           
;         ES:BX -> buffer address        
; Output: CF = 1 if error                
; =======================================================================
        push    es
        pusha
ReadSectorNext:
        mov     di, RETRY                   ; attempts to read
ReadSectorRetry:
        pusha

        div     word ptr [bpbSectorsPerTrack]
                ; ax = LBA / SPT
                ; dx = LBA % SPT         = sector - 1

        mov     cx, dx
        inc     cx                      ; cx = sector no.

        xor     dx, dx
        div     word ptr [bpbHeadsPerCylinder]
                ; ax = (LBA / SPT) / HPC = cylinder
                ; dx = (LBA / SPT) % HPC = head

        mov     ch, al                  ; ch = LSB 0...7 of cylinder no.
        shl     ah, 6
        or      cl, ah                  ; cl = MSB 8...9 of cylinder no. + sector no.
        mov     dh, dl                  ; dh = head no.
        mov     dl, byte ptr [Drive]    ; dl = drive no.
        mov     ax, 201h
                                        ; al = sector count = 1
                                        ; ah = 2 = read function no.

        int     13h                     ; read sectors
        jnc     ReadSectorDone          ; CF = 0 if no error
        xor     ah, ah                  ; ah = 0 = reset function
        int     13h                     ; reset drive
        popa
        dec     di
        jnz     ReadSectorRetry         ; extra attempt

        ; .. fall though to error

ReadSectorErr:
        stc 
        jmp     ReadSectorDone2  

ReadSectorDone:
        popa
        dec     cx
        jz      ReadSectorDone2         ; last sector

        mov     di, [bpbBytesPerSector]
        shr     di, 4                   ; paragraphs per sector
        mov     si, es
        add     si, di
        mov     es, si                  ; adjust segment for next sector

        add     ax, 1
        adc     dx, 0                   ; adjust LBA for next sector
        jmp     short ReadSectorNext

ReadSectorDone2:
        popa
        pop     es
        ret
ReadSector endp

cdiv macro cdiva, cdivb
; return (a+b-1)/b
           mov  ax, cdiva
           mov  bx, cdivb
           add  ax, bx
           dec  ax
           xor  dx, dx
           mov  bx, cdivb
           div  bx
     endm

dummy_boot_jump macro
     db  0ebh
     db  03ch
     db  90h
     endm

; ===========================================================================
getptype proc far
; DL = partition type
; ===========================================================================
      lea  si, partition
      mov  cx, partitions
gpsp:
      mov  ax, word ptr ds:[si]           
      xchg ah, al  
      inc  si 
      inc  si 
      cmp  al, 0
      je   gptx 
      cmp  al, dl
      jne  not_thisonep   
           mov  bx, word ptr ds:[si]
           xchg bh, bl
           mov  di, bx
gpdp:
           mov  al, byte ptr ds:[di] 
           inc  di
           cmp  al, 0
           je   gptx  
           call teletype
           jmp  gpdp
not_thisonep:
      inc  si
      inc  si 
      jmp  gpsp
      print FS_OTHER, 1 
gptx:
      ret
getptype endp

; ===========================================================================
DriveCheck proc far
; DL = drive
; ===========================================================================
   mov  ah, 10h
;   mov  dl, byte ptr ds:[fatdrive]
   int  13h
   ret
DriveCheck endp

dospart db ?
; ===========================================================================
ReadMbr proc far
; ===========================================================================
; Reading MBR at Sector: 0
; File System Type: 06
; Start Sector: 32
; Filesystem: FAT16
   lea  si, mbrbuf
   mov  cx, 512
   ; readdisk
   ; if mbrbuf[1feh] != 55h
   ;    mbrbuf[1ffh] != AAh
   ;    return error
   ; if sectorsize>512 return error
   ; dospart = mbrbuf[1BEh];
   ; taboffset = 0;
   mov  dl, dospart
   call getptype

   ret
ReadMbr endp

; ===========================================================================
ReadBootRecord proc far
; ===========================================================================
; Reading from Sector 32
; Bytes per sector: 512
; Sectors per cluster: 2
; Sectors per fat: 244
; Reserved sectors: 1
; Copies of fat: 2
; Max root entries: 512

   ret
ReadBootRecord endp

; ===========================================================================
Driveinit proc far
; ===========================================================================
   ; Tells the FAT driver to read the Master Boot Recotd (MBR), 
   ; partition table and boot record, in that order. This data is 
   ; required to locate the various areas of the disk like the 
   ; cluster maps, directory entries, data area, etc.

   call readmbr
   call readbootrecord

   ret
Driveinit endp

; ===========================================================================
FindLastUsedSector proc far
; ===========================================================================
; Finding Last Used Cluster starting at: 521
; Setting last used cluster to: 2

   ret
FindLastUsedSector endp

; ===========================================================================
fatinit proc far
; DL = Drive
; ===========================================================================
   push cs
   pop  ds
   mov  byte ptr ds:[fatdrive], dl
   Call drivecheck
   jc   drive_not_ready
        call driveinit
        call FindLastUsedSector

        ; set ES:BX
        call load_FAT16
        mov  bx, ax
        mov  di, dx  ; save LBA to di:bx      
        call load_ROOT16
        push dx
        push ax
        ; Look for a COM/EXE program to be load and run
        mov  di, bx                  ; es:di -> root entries array
        mov  dx, si                  ; dx = number of root entries
        mov  si, ProgramName         ; ds:si -> program name
        call FindName

        ; createfile "NEWFILE.TXT"
        ; writefile  "The quick brow fox jumped over the lazy dog!"
        ; Listfile "NEWFILE.TXT"
        clc
   ret
drive_not_ready:
   print bad_media, 1
   stc
   ret
fatinit endp
