;This is the JOS1 FAT12 Bootsector.
;It is actually a slightly modified version of
;the BOS bootsector by Christoffer Bubach.
;It is Public Domain. Thanks bubach!

;What it does:
;1.Prints a loading message.
;2.Loads KERNEL.SYS from a FAT12 file system,
;  to 0x08:0x10000 (code+00010000h)
;3.Enters protected mode
;4.Jumps to the kernel

;On error:
;1.Prints an error message.

;Note: When the kernel is loaded, bl contains the status of A20
;(1)Whether it was successfully turned on or (0)Not.
;Also, EAX contains 0xB0B007 - l337 5p34k for "BOS BOOT".

;-By NotTheCHEAT
;All of the comments following this on are by bubach, unless otherwise noted.

;------------------------------------------------------------;
;                 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
;  pmode address of kernel: 0x0008:0x10000 -> kernel code - NotTheCHEAT
;  0x1000:0x0000 -> 0x9000:0xffff  576kb free space (where the kernel is loaded)
;  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, 0x1A
	  mov	  cx, 12
	  mov	  dx, 0x0102
	  int	  0x10
;Removed code to move cursor; BIOS call 10h function 13h moves the cursor
;when AL=01h or AL=03h.
	  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 &amp; functions        ;
;-----------------------------------;
	  loading	  db  'Loading JOS1'
	  filename	  db  'KERNEL  SYS'
	  failure	  db  'Load 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, ax
	  mov	  es, ax
	  mov	  fs, ax
	  mov	  gs, ax
	  mov	  ss, ax
	  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. ;
;-------------------------------------;
if $-7c00h > 512 
  display 'WARNING: boot code is more than 512 bytes'
  display 'Cannot continue!'
else
	  rb boot+512-2-$
end if
	  dw 0xAA55