; Script-OS Boot sector	Version 0.01
; (c) 2005 Jason Hunt
;
; This is the boot loader
; It is copied to the boot sector of the disk
; It's only function is to load KERNEL.BIN from
; the dos formatted floppy disk it was booted from
;
; Memory map after boot:
; 0000:0000 - 0000:ffff		Stack
; 07c0:0000 - 07c0:01ff		Boot sector
; 07c0:0200 - 07c0:03ff		FAT
; 1000:0000 - 	     		Kernel
;
; 

[CPU 386]
[BITS 16]
[ORG 0]

    jmp		short boot_code			
    nop		; allign bios parameter block

%define		ENTRY_SEG	0x07c0

;; create a fat12 parameter block

os_name			db 'ScriptOS'	; (8) Operating sys name
bytes_per_sect		dw 0x200	; (2) Bytes per sector
sect_per_cluster	db 1		; (1) Sectors per cluster
num_reserved		dw 1		; (2) Reserved sectors
num_fats		db 2		; (1) Numer of FATs
max_root		dw 0x00E0	; (2) Max number root entires
num_sectors		dw 0x0B40	; (2) Number of sectors
media			db 0xF0		; (1) Media type
sect_per_fat		dw 9		; (2) Sectors per FAT
sect_per_track		dw 0x12         ; (2) Sectors per track
num_heads		dw 2            ; (2) Number of heads
num_hidden		dd 0            ; (4) Hidden Sectors
    			dd 0            ; (4) Number of Huge sectors
drive_num		db 0            ; (1) Drive number
    			db 0            ; (1) Reserved
    			db 0x29         ; (1) Signature
			dd 0xcafebabe   ; (4) Volume ID
			db 'SCRIPT-OS  '; (11) Label
			db 'FAT12   '   ; (8) Filesystem
boot_code:
	cli				; Disable interrups

	mov	ax, ENTRY_SEG		; We know the bios loads into here	
	mov	es, ax 			; 
	mov	ds, ax			; Setup data segment

	xor	ax, ax	
	mov     ss, ax			; Setup stack segment
	mov     sp, 0xffff		; Stack starts at 0000:ffff
	
	;mov	ax, 0x0003

	mov	al, 0x03		; Clear screen
	mov	ah, 0x00		; BIOS set video mode function
	int	0x10					

	mov	si,bootmsg		; Display "loading kernel..."
	call	put_string

	; Load the kernel

	; Figure out size of root directory
	mov     ax, 0x20		; Dir entires are 32 bytes
	mul     word [max_root]		; Multiply by max entries
	div     word [bytes_per_sect]	; Convert to sectors
	mov	bx, ax			; save size in sectors for later
	
	; Figure out starting LBA of root directory
	xor	ah, ah
	mov     al, BYTE [num_fats]	; Number of FATs			
	mul     WORD [sect_per_fat]	; Multiple by sectors per fat	
	add     ax, WORD [num_hidden]	; Add number of hidden sectors
	add     ax, WORD [num_reserved]	; Add number of reserved sectors
	add	bx, ax			; Add start to size
	mov	word [data_start], bx	; Save starting address of data

	mov	bx, 0x200		; Load root into [07c0:0200], right after boot sector

	mov	cl, 1			; Read one sector of root
	call    read_disk		; Read root directory
			
	mov	dx, word [max_root]
	mov	bx, 0x0200

.find_loop:				; Seach for kernel image
	mov	si, bx
	mov	di, kernel_name		; es:di points to kernel name

	mov	cx, 0x0b		; Filenames are 11 chars long
	repz	cmpsb			; Compare filename with kernel_name
	jz	.found			; Find the filename?

	add	bx, 0x20		; Next dir entry

	dec	dx			; Done checking?
	jnz	.find_loop
	jmp	find_error


.found:	
					; We now have the directory entry for the file
	mov	ax, word[si+0x0f]	; Skip unneeded data and get starting cluster

	mov	word[kernel_cluster], ax; Save cluster for later use
	call	cluster_to_lba		; ax now has has lba number

	; load fat into memory

	mov	cx, word[sect_per_fat]	; cx has fat size in sectors
	
	mov	ax, word [num_reserved]	; Skip reserved and hidden
	add	ax, word [num_hidden]	; ax now has locaation of FAT

	mov	bx, 0x0200		; Setup for read

	call	read_disk		; Read fat into 0x07c0:0200

;	xor	ax, ax
	mov	ax, 0x1000		; Setup to load kernel into [1000:0000]
	mov	es, ax
	xor	bx, bx

.load_kernel_loop:	;  ax has current LBA address
			;  bx has current memory offset
			;  es:bx points to kernel memory

	mov	ax, word[kernel_cluster]	; ax has first cluster of kernel


	call	cluster_to_lba


	mov	cl, 1
	
	call	read_disk			; Load kernel cluster into memory

	; use fat to compute the next cluster
	push bx

	mov	ax, word[kernel_cluster]	; Load current cluster	
	mov	bx, ax
	shr	bx, 1
	add	bx, ax				; Divide by two, then add origional for mul by (3/2)
	add	bx, 0x0200			; Offset of fat
	mov	dx, word[bx]			; dx has value from fat
	and	ax, 0x0001			; Test current cluser for even or odd
	jnz	.odd
	and	dx, 0x0fff			; Mask off low 12 bits
	jmp	.next
.odd:
	shr	dx, 4				; Move over unused 4 bits
.next:
	pop	bx
	add	bx, 0x0200			; Index for next cluster

	mov	ax, dx
	mov	word[kernel_cluster], dx	; Store next cluster
	cmp	ax, 0x0ff8			; EOF flag?
	jb	.load_kernel_loop

	; The kernel is now loaded into memory

	mov	dx,0x3f2			; Disable floppy drive motor
	xor	al, al
	out	dx, al
	
	jmp	0x1000:0000			; Start kernel


; Utility functions


; cluster number is in ax, convert to LBA address
cluster_to_lba:
	push bx
	dec	ax
	dec	ax
	xor	bh, bh
	mov	bl, byte[sect_per_cluster]	
	mul	bx				; Multiply cluster by sectors per cluster
	add	ax, word [data_start]		; Add starting address of data
	pop	bx
	ret


; Read lba address stored in dx:ax into es:bx
;
;	number of sectors to read is in cl
;
read_disk:
	mov	word [tempLBA], ax
	mov 	byte [num_read], cl

	
	mov	byte[try_count], 0x0a		; Number of attempts

.read_loop:
	dec	byte[try_count]	
	jz	load_error			; If any attepmts remain, retry read

	; convert from LBA to CHS

	mov	ax, word[tempLBA]

	xor	dx, dx	
	div	word[sect_per_track]	; Divide LBA by sectors per track, quo in ax, rem in dx
	inc	dl			; Adjust to sector number
	mov	cl, dl			; cl = sector number (LBA mod sect_per_track) + 1
	xor	dx, dx			; Clear remainder
	div	word[num_heads]		; Divide whats left by number of heads	
	mov	dh, dl			; dh = head number (LBA / sect_per_track) mod num heads
	mov	ch, al				; ch = cylender number (track)
	mov     dl, byte [drive_num]  	; Drive number
	mov     ah, 0x02		; BIOS read sector
	mov     al, byte[num_read]               
   	int     0x13                  	; Call bios

	jnc	.done_read		; Retry if error
	
	mov 	al, 0x00		; Reset drive
	int 	0x13
	jmp	.read_loop

.done_read:
	ret				; Data loaded succesfully


; Display error message and lock system
;
find_error:
	mov	si, finderror
	call	put_string
	jmp	($)

load_error:
	mov	si, loaderror
	call	put_string
	jmp	($)

; Print string at [ds:si]
put_string:
	lodsb
	cmp	al, 0
	jz	.done			; Found null
	mov	ah, 0x0e		; Write char funciton
	int 	0x10			; Call bios
	jmp	put_string		; Next char
.done:
	ret

; name of kernel file on disk
kernel_name	db "KERNEL  BIN", 0

; messages
bootmsg		db 0x0d, 0x0a, "(Stage-0) Script-OS Loading kernel.", 0x0d, 0x0a, 0
loaderror	db "Load kernel fail.", 0x0d, 0x0a, 0
finderror	db "Find kernel fail.", 0x0d, 0x0a, 0
a20error	db "Unable to set A20.", 0x0d, 0x0a, 0

; varibles used by boot code
try_count	db 0
num_read	db 0
tempLBA		dw 0


data_start	dw 0

kernel_cluster	dw 0		; Start of kernel cluster

times 510-($-$$) db 0		; Pad to boot sector size

dw 0xAA55			; Magic boot sector number
















