;------------------------------------------------------------------------------
;*
;*   Copyright (c) 2006 - 2007, Intel Corporation. All rights reserved.<BR>
;*   This program and the accompanying materials                          
;*   are licensed and made available under the terms and conditions of the BSD License         
;*   which accompanies this distribution.  The full text of the license may be found at        
;*   http://opensource.org/licenses/bsd-license.php                                            
;*                                                                                             
;*   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
;*   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             
;*   
;*    bs16.asm
;*  
;*   Abstract:
;*
;------------------------------------------------------------------------------

        .model  small
        .stack
        .486p
        .code

FAT_DIRECTORY_ENTRY_SIZE  EQU     020h
FAT_DIRECTORY_ENTRY_SHIFT EQU     5
BLOCK_SIZE                EQU     0200h
BLOCK_MASK                EQU     01ffh
BLOCK_SHIFT               EQU     9
                                               ; "EFILDR_____"
LOADER_FILENAME_PART1     EQU     04c494645h   ; "EFIL"
LOADER_FILENAME_PART2     EQU     036315244h   ; "DR16"
LOADER_FILENAME_PART3     EQU     020202036h   ; "6___"

        org 0h
Ia32Jump:
  jmp   BootSectorEntryPoint  ; JMP inst                  - 3 bytes
  nop

OemId             db  "INTEL   "    ; OemId               - 8 bytes
; BPB data below will be fixed by tool
SectorSize        dw  0             ; Sector Size         - 16 bits
SectorsPerCluster db  0             ; Sector Per Cluster  - 8 bits
ReservedSectors   dw  0             ; Reserved Sectors    - 16 bits
NoFats            db  0             ; Number of FATs      - 8 bits
RootEntries       dw  0             ; Root Entries        - 16 bits
Sectors           dw  0             ; Number of Sectors   - 16 bits
Media             db  0             ; Media               - 8 bits  - ignored
SectorsPerFat     dw  0             ; Sectors Per FAT     - 16 bits
SectorsPerTrack   dw  0             ; Sectors Per Track   - 16 bits - ignored
Heads             dw  0             ; Heads               - 16 bits - ignored
HiddenSectors     dd  0             ; Hidden Sectors      - 32 bits - ignored
LargeSectors      dd  0             ; Large Sectors       - 32 bits 
PhysicalDrive     db  0             ; PhysicalDriveNumber - 8 bits  - ignored
CurrentHead       db  0             ; Current Head        - 8 bits
Signature         db  0             ; Signature           - 8 bits  - ignored
Id                db  "    "        ; Id                  - 4 bytes
FatLabel          db  "           " ; Label               - 11 bytes
SystemId          db  "FAT16   "    ; SystemId            - 8 bytes

BootSectorEntryPoint:
        ASSUME  ds:@code
        ASSUME  ss:@code

; ****************************************************************************
; Start Print
; ****************************************************************************
  lea  si, cs:[StartString]
  call PrintString

; ****************************************************************************
; Print over
; ****************************************************************************

  mov   ax,cs         ; ax = 0
  mov   ss,ax         ; ss = 0
  add   ax,1000h
  mov   ds,ax

  mov   sp,07c00h     ; sp = 0x7c00
  mov   bp,sp         ; bp = 0x7c00

  mov   ah,8                                ; ah = 8 - Get Drive Parameters Function
  mov   byte ptr [bp+PhysicalDrive],dl      ; BBS defines that BIOS would pass the booting driver number to the loader through DL
  int   13h                                 ; Get Drive Parameters
  xor   ax,ax                   ; ax = 0
  mov   al,dh                   ; al = dh
  inc   al                      ; MaxHead = al + 1
  push  ax                      ; 0000:7bfe = MaxHead
  mov   al,cl                   ; al = cl
  and   al,03fh                 ; MaxSector = al & 0x3f
  push  ax                      ; 0000:7bfc = MaxSector

  cmp   word ptr [bp+SectorSignature],0aa55h  ; Verify Boot Sector Signature
  jne   BadBootSector
  mov   cx,word ptr [bp+RootEntries]      ; cx = RootEntries
  shl   cx,FAT_DIRECTORY_ENTRY_SHIFT      ; cx = cx * 32 = cx * sizeof(FAT_DIRECTORY_ENTRY) = Size of Root Directory in bytes
  mov   bx,cx                             ; bx = size of the Root Directory in bytes
  and   bx,BLOCK_MASK                     ; See if it is an even number of sectors long
  jne   BadBootSector                     ; If is isn't, then the boot sector is bad.
  mov   bx,cx                             ; bx = size of the Root Directory in bytes
  shr   bx,BLOCK_SHIFT                    ; bx = size of Root Directory in sectors
  mov   al,byte ptr [bp+NoFats]           ; al = NoFats
  xor   ah,ah                             ; ah = 0  ==> ax = NoFats
  mul   word ptr [bp+SectorsPerFat]       ; ax = NoFats * SectorsPerFat
  add   ax,word ptr [bp+ReservedSectors]  ; ax = NoFats * SectorsPerFat + ReservedSectors = RootLBA
  push  ds
  pop   es
  xor   di,di                             ; Store directory in es:di = 1000:0000
  call  ReadBlocks                        ; Read entire Root Directory
  add   ax,bx                             ; ax = NoFats * SectorsPerFat + ReservedSectors + RootDirSectors = FirstClusterLBA (FirstDataSector)
  mov   word ptr [bp],ax                  ; Save FirstClusterLBA (FirstDataSector) for later use

  ; dx - variable storage (initial value is 0)
  ; bx - loader (initial value is 0)
  xor   dx, dx
  xor   bx, bx

FindEFILDR:
  cmp   dword ptr [di],LOADER_FILENAME_PART1         ; Compare to "EFIL"
  jne   FindVARSTORE
  cmp   dword ptr [di+4],LOADER_FILENAME_PART2
  jne   FindVARSTORE
  cmp   dword ptr [di+7],LOADER_FILENAME_PART3
  jne   FindVARSTORE
  mov   bx, word ptr [di+26]              ; bx = Start Cluster for EFILDR  <----------------------------------
  test  dx, dx
  je    FindNext                          ; Efivar.bin is not loaded
  jmp   FoundAll

FindVARSTORE:
  ; if the file is not loader file, see if it's "EFIVAR  BIN"
  cmp   dword ptr [di], 056494645h        ; Compare to "EFIV"
  jne   FindNext
  cmp   dword ptr [di+4], 020205241h      ; Compare to "AR  "
  jne   FindNext
  cmp   dword ptr [di+7], 04e494220h      ; Compare to " BIN"
  jne   FindNext
  mov   dx, di                            ; dx = Offset of Start Cluster for Efivar.bin <---------------------
  add   dx, 26
  test  bx, bx
  je    FindNext                          ; Efildr is not loaded
  jmp   FoundAll
  
FindNext:
  ; go to next find
  add   di,FAT_DIRECTORY_ENTRY_SIZE       ; Increment di
  sub   cx,FAT_DIRECTORY_ENTRY_SIZE       ; Decrement cx
  ; TODO: jump to FindVarStore if ...
  jne   FindEFILDR
  jmp   NotFoundAll

FoundAll:
FoundEFILDR:
  mov     cx,bx                               ; cx = Start Cluster for EFILDR  <----------------------------------
  mov     ax,cs                               ; Destination = 2000:0000
  add     ax,2000h
  mov     es,ax
  xor     di,di
ReadFirstClusterOfEFILDR:
  mov     ax,cx                               ; ax = StartCluster
  sub     ax,2                                ; ax = StartCluster - 2
  xor     bh,bh                               
  mov     bl,byte ptr [bp+SectorsPerCluster]  ; bx = SectorsPerCluster
  push    dx
  mul     bx
  pop     dx                                  ; ax = (StartCluster - 2) * SectorsPerCluster
  add     ax, word ptr [bp]                   ; ax = FirstClusterLBA + (StartCluster-2)*SectorsPerCluster
  xor     bh,bh
  mov     bl,byte ptr [bp+SectorsPerCluster]  ; bx = Number of Sectors in a cluster
  push    es
  call    ReadBlocks
  pop     ax
JumpIntoFirstSectorOfEFILDR:
  mov     word ptr [bp+JumpSegment],ax
JumpFarInstruction:
  db      0eah
JumpOffset:
  dw      0000h
JumpSegment:
  dw      2000h


PrintString:
  mov  ax,0b800h
  mov  es,ax
  mov  ax, 07c0h
  mov  ds, ax
  mov  cx, 7
  mov  di, 160
  rep  movsw
  ret
; ****************************************************************************
; ReadBlocks - Reads a set of blocks from a block device
;
; AX    = Start LBA
; BX    = Number of Blocks to Read
; ES:DI = Buffer to store sectors read from disk
; ****************************************************************************

; cx = Blocks
; bx = NumberOfBlocks
; si = StartLBA

ReadBlocks:
  pusha
  add     eax,dword ptr [bp+LBAOffsetForBootSector]    ; Add LBAOffsetForBootSector to Start LBA
  add     eax,dword ptr [bp+HiddenSectors]    ; Add HiddenSectors to Start LBA
  mov     esi,eax                             ; esi = Start LBA
  mov     cx,bx                               ; cx = Number of blocks to read
ReadCylinderLoop:
  mov     bp,07bfch                           ; bp = 0x7bfc
  mov     eax,esi                             ; eax = Start LBA
  xor     edx,edx                             ; edx = 0
  movzx   ebx,word ptr [bp]                   ; bx = MaxSector
  div     ebx                                 ; ax = StartLBA / MaxSector
  inc     dx                                  ; dx = (StartLBA % MaxSector) + 1
  sub     bx,dx                               ; bx = MaxSector - Sector
  inc     bx                                  ; bx = MaxSector - Sector + 1
  cmp     cx,bx                               ; Compare (Blocks) to (MaxSector - Sector + 1)
  jg      LimitTransfer
  mov     bx,cx                               ; bx = Blocks
LimitTransfer:
  push    cx
  mov     cl,dl                               ; cl = (StartLBA % MaxSector) + 1 = Sector
  xor     dx,dx                               ; dx = 0
  div     word ptr [bp+2]                     ; ax = ax / (MaxHead + 1) = Cylinder  
                                              ; dx = ax % (MaxHead + 1) = Head

  push    bx                                  ; Save number of blocks to transfer
  mov     dh,dl                               ; dh = Head
  mov     bp,07c00h                           ; bp = 0x7c00
  mov     dl,byte ptr [bp+PhysicalDrive]      ; dl = Drive Number
  mov     ch,al                               ; ch = Cylinder
  mov     al,bl                               ; al = Blocks
  mov     ah,2                                ; ah = Function 2
  mov     bx,di                               ; es:bx = Buffer address
  int     013h
  jc      DiskError
  pop     bx
  pop     cx
  movzx   ebx,bx
  add     esi,ebx                             ; StartLBA = StartLBA + NumberOfBlocks
  sub     cx,bx                               ; Blocks = Blocks - NumberOfBlocks
  mov     ax,es
  shl     bx,(BLOCK_SHIFT-4)
  add     ax,bx
  mov     es,ax                               ; es:di = es:di + NumberOfBlocks*BLOCK_SIZE
  cmp     cx,0
  jne     ReadCylinderLoop
  popa
  ret

; ****************************************************************************
; ERROR Condition:
; ****************************************************************************
NotFoundAll:
  ; if we found EFILDR, continue
  test bx,bx
  jne  FoundEFILDR
BadBootSector:
DiskError:
  lea  si, cs:[ErrorString]
  call PrintString
Halt:
  jmp   Halt

StartString:
  db 'B', 0ch, 'S', 0ch, 't', 0ch, 'a', 0ch, 'r', 0ch, 't', 0ch, '!', 0ch
ErrorString:
  db 'B', 0ch, 'E', 0ch, 'r', 0ch, 'r', 0ch, 'o', 0ch, 'r', 0ch, '!', 0ch

; ****************************************************************************
; LBA Offset for BootSector, need patched by tool for HD boot.
; ****************************************************************************

  org 01fah
LBAOffsetForBootSector:
  dd        0h

; ****************************************************************************
; Sector Signature
; ****************************************************************************

  org 01feh
SectorSignature:
  dw        0aa55h      ; Boot Sector Signature

  end