;------------------------------------------------------------;
;                 BOS - FAT12 bootsector                     ;
;------------------------------------------------------------;
;  - FAT12 compatible.                                       ;
;  - Loads a binary file from the floppy, max ~576kb.        ;
;  - Sets A20 and protected mode.                            ;
;                                                            ;
;  Thanks to: Petroff Heroj and John S. Fine for examples.   ;
;                                                            ;
;          by: Christoffer Bubach, 2003-2005                 ;
;  http://bubach.1go.dk/BOS/        bubach85@hotmail.com     ;
;                                                            ;
;------------------------------------------------------------;

; other notes:
;  this code is public domain, and you can use it for
;  anything you want. but if you do, please act polite and
;  give credit. ;-)
;
;            mem map
;  0x0000:0x0000 -> 0x0000:0x0500  BIOS stuff
;  0x0000:0x0500 -> 0x0000:0x2100  root
;  0x0000:0x2100 -> 0x0000:0x3300  fat
;  0x0000:0x3300 -> 0x0000:0x7c00  18,25kb free space
;  0x0000:0x7c00 -> 0x0000:0x7e00  bootsector
;  0x0000:0x7e00 <- 0x0000:0xffff  32,5kb stack
;  0x1000:0x0000 -> 0x9000:0xffff  576kb free space
;  0xa000:0x0000 -> .............  VGA mem etc.

use16
org 0x7C00

boot:     jmp     near start
          nop

;------------------------------------------;
;  Standard BIOS Parameter Block, "BPB".   ;
;------------------------------------------;
          bpbOEM          db  'BOS 0.03'
          bpbSectSize     dw  512
          bpbClustSize    db  1
          bpbReservedSec  dw  1
          bpbFats         db  2
          bpbRootSize     dw  224
          bpbTotalSect    dw  2880
          bpbMedia        db  240
          bpbFatSize      dw  9
          bpbTrackSect    dw  18
          bpbHeads        dw  2
          bpbHiddenSect   dd  0
          bpbLargeSect    dd  0
     ;---------------------------------;
     ;  extended BPB for FAT12/FAT16   ;
     ;---------------------------------;
          bpbDriveNo      db  0
          bpbReserved     db  0
          bpbSignature    db  0                       ; 0 = nothing more. 41 = three more (below)..
;          bpbID           dd  1
;          bpbVolumeLabel  db  'BOOT FLOPPY'
;          bpbFileSystem   db  'FAT12   '


;----------------------------------------;
;   starting point of bootsector code    ;
;----------------------------------------;
start:
          cli

          xor     ax, ax                              ; initialize all the necessary
          mov     ds, ax                              ; registers.
          mov     es, ax
          mov     ss, ax
          mov     sp, 0xFFFF                          ; Stack..

          mov     [bpbDriveNo], dl

          sti


;----------------------------------;
;   clear screen and print some    ;
;----------------------------------;
          mov     ax, 3                               ; Set mode 0x03
          int     0x10

          mov     bp, loading                         ; Print loading message.
          mov     ax, 0x1301
          mov     bx, 7
          mov     cx, 12
          mov     dx, 0x0102
          int     0x10

          mov     bl, 2                               ; Set cursor.
          mov     ah, 2
          mov     dx, 0x0201
          int     0x10

          mov     ah, 9                               ; Print 14 green dots.
          mov     al, '.'
          mov     cx, 14
          int     0x10


;---------------------------;
;    load FAT and root      ;
;---------------------------;
          mov     di, 0x0050                          ; Load the root to
          mov     ax, 19                              ; 0x0000:0x0500 (0x500/0x10)
          mov     cx, 14
          call    read_sectors

          mov     di, 0x0210                          ; Load the fat to
          mov     ax, 1                               ; 0x0000:0x2100
          mov     cx, 9
          call    read_sectors


     ;------------------------;
     ;  search for the file   ;
     ;------------------------;
          mov     dx, [bpbRootSize]
          mov     bx, 0x0500
     filesearch:
          cld
          mov     si, filename
          mov     cx, 11
          mov     di, bx
          repe    cmpsb
          je      found
          add     bx, 32
          dec     dx
          jz      error
          jmp     filesearch


;-----------------------------------;
;      variables & functions        ;
;-----------------------------------;
          loading         db  'Starting BOS'
          filename        db  'KERNEL  SYS'
          failure         db  'Read error!'
          a20_on          db  1


     ;-----------------------------------------------;
     ;   read a number of sectors (one at a time)    ;
     ;-----------------------------------------------;
     ;  in:                                          ;
     ;    di = segment to save at                    ;
     ;    ax = sector to read                        ;
     ;    cx = number of sectors                     ;
     ;  out:                                         ;
     ;    di = updated (added for next read)         ;
     ;    ax = updated (added for next read)         ;
     ;-----------------------------------------------;
     read_sectors:
          pusha
          mov     bl, byte [bpbTrackSect]             ; bl = number of sectors per track
          div     bl                                  ; al = ax / bl
          mov     cl, ah                              ; cl = real sector number
          add     cl, 1

          xor     ah, ah                              ; del the rest of the div before
          mov     bl, byte [bpbHeads]                 ; bl = number of heads
          div     bl                                  ; ah = rest of ( ax / bx ), al = ax / bx
          mov     ch, al                              ; ch = number of track
          mov     dh, ah                              ; dh = the head number

          mov     ax, cx                              ; save cx in ax
          mov     cx, 6                               ; try it 6 times
     .next_try:
          push    es
          push    cx
          mov     cx, ax                              ; restore cx
          push    cx

          xor     ax, ax
          mov     dl, [bpbDriveNo]                    ; reset drive
          push    dx
          int     0x13
          jc      .failed

          pop     dx
          pop     cx
          xor     bx, bx
          mov     es, di
          mov     ax, 0x0201                          ; function 2, 1 sector
          int     0x13
          jnc     .ok                                 ; if it was ok, check next..

     .failed:
          pop     dx
          pop     ax

          pop     cx
          pop     es
          loop    .next_try                           ; else try once again if there is an error
          jmp     error                               ; if cx = 0 and the read operation always failed, halt
     .ok:
          pop     cx                                  ; from the next_try loop
          pop     es
          popa
          add     di, 32                              ; add 32 (512/16) to segment
          inc     ax                                  ; add sector counter
          loop    read_sectors
          ret


     ;----------------------------------------------------;
     ;  show a message and wait for a key before reboot   ;
     ;----------------------------------------------------;
     error:
          ;push    0x0000
          ;pop     es
          mov     bp, failure
          mov     ax, 0x1301
          mov     bx, 4
          mov     cx, 43
          mov     dx, 0x0401
          int     0x10

          mov     ah, 0
          int     0x16
          int     0x19


;-----------------------------------;
;   the file is found, load it.     ;
;-----------------------------------;
found:
          mov     bp, [bx+26]                         ; bp=cluster number from directory entry
          mov     di, 0x1000                          ; 1000 (segment)

     .next_block:
          xor     cx, cx
          mov     cl, [bpbClustSize]                  ; reset sector count to 1 cluster
          mov     si, bp                              ; si=next should-be cluster for
                                                      ; contiguous reads
     .next_contiguous:
          mov     ax, 3                               ; 3
          mul     si                                  ; multiply cluster number by 3
                                                      ; dx assumed to be 0, it's a floppy!
          shr     ax, 1                               ; divide by two
          push    bp
          xchg    bp, ax                              ; bp=ax
          mov     ax, word [0x2100+bp]                ; ax=FAT element with junk
                                                      ; (addressing with bp)
          pop     bp
          jc      byte .odd_cluster                   ; jump if the value was odd

     .even_cluster:
          and     ax, 0x0FFF                          ; leave only lower 12 bits
          jmp     .got_cluster                        ; got it

     .odd_cluster:
          push    cx                                  ; preserve sector count
          mov     cl, 4                               ; shift four bits right
          shr     ax, cl                              ; (leave only bits 4-15)
          pop     cx                                  ; restore sector count

     .got_cluster:
          inc     si                                  ; si=current cluster+1
          cmp     ax, si                              ; next cluster=current cluster+1?
          jne     byte .force_read                    ; is it still contiguous?

          add     cl, [bpbClustSize]                  ; increase sector count by 1 cluster
          adc     ch, 0
          jmp     .next_contiguous

     .force_read:
          xchg    bp, ax                              ; ax=bp (base cluster), bp=new cluster
          dec     ax                                  ; decrease by 2 to get the actual... (1)
          dec     ax                                  ; ...cluster number (2)

          xor     dx, dx
          mov     dl, [bpbClustSize]
          mul     dx                                  ; multiply by sectors per cluster
                                                      ; (dx ignored)
          add     ax, 33                              ; assume data-area start at sector 33
          call    read_sectors                        ; read cx sectors at ax to es:0 :)

          cmp     bp, 0x0FF8                          ; the new cluster is EOF (FF8-FFF)?
          jb      byte .next_block                    ; if not in this range, read next block

;-----------------------;
;  the file is loaded   ;
;-----------------------;
quit:
a20:                                                  ; Enable A20
          in      al, 0x64
          test    al, 2
          jnz     a20
          mov     al, 0xD1
          out     0x64, al
     .d6:
          in      al, 0x64
          and     ax, 2
          jnz     .d6
          mov     al, 0xDF
          out     0x60, al

     .a20_check:
          mov     al, byte [fs:0]                     ; check a20, is it on?
          mov     ah, al
          not     al
          xchg    al, byte [gs:0x10]
          cmp     ah, byte [fs:0]
          mov     [gs:0x10], al
          jz      floppy_off
          mov     [a20_on], 0                         ; it's not on save for file..

floppy_off:
          mov     dx, 0x3F2                           ; turn of the floppy motor.
          mov     al, 0
          out     dx, al

pmode:
          cli                                         ; set protected mode (32-bit)
          lgdt    [gdtr]
          mov     eax, cr0
          or      eax, 1
          mov     cr0, eax

          jmp     0x08:flush


;----------------------------------------;
; start of 32-bit area.                  ;
;     flush segments and jump to kernel  ;
;----------------------------------------;
use32
flush:
          mov     eax, 0x10                           ; refresh all segment registers
          mov     ds, eax
          mov     es, eax
          mov     fs, eax
          mov     gs, eax
          mov     ss, eax
          mov     esp, 0xfffc

          mov     eax, 0xB05B007                      ; report "BOSboot".. ;-)
          mov     bl, [a20_on]                        ; report if a20 is on.
          jmp     0x08:0x10000                        ; jump to loaded file (64kb in mem)



;--------------------------------;
;  global descriptor table (gdt) ;
;--------------------------------;
gdt:                      dw  0x0000, 0x0000, 0x0000, 0x0000
codesel:                  dw  0xFFFF, 0x0000, 0x9800, 0x00CF
datasel:                  dw  0xFFFF, 0x0000, 0x9200, 0x00CF
gdt_end:

gdtr:                     dw  gdt_end - gdt - 1
                          dd  gdt

;-------------------------------------;
; set the BOOT-signature at byte 510. ;
;-------------------------------------;
          rb boot+512-2-$
          dw 0xAA55