
;*********************************************
;	boot.asm
;		A Simple Bootloader (Volume Boot Record, VBR)
;		Loads the kernel-loader from Fat32
;*********************************************

bits 16

org	0	


VBR_SEG				equ	0x07C0
VBR2_OFFSET			equ	0x200  			; 512
VBR2_SECTNUM		equ	32+2			; partition_lba_begin+2, so the second one of the ReservedSectors
FILE_BUFF_SEG		equ	0x0050

; Function-offsets in VBR2
READFILE_OFFS		equ 0
CLUSTER2LBA_OFFS	equ 2


start:	jmp	main

;*********************************************
;	BIOS Parameter Block
;*********************************************

; BPB Begins 3 bytes from start. We do a far jump, which is 3 bytes in size.
; If you use a short jump, add a "nop" after it to offset the 3rd byte.
; IN ORDER TO BE A FAR JUMP PUT THE FUNCTIONS BEFORE MAIN !!!!
nop	; !!!!!

bpbOEM					db "mkdosfs",0		; 8 chars
bpbBytesPerSector		dw 0x0200			; 8
bpbSectorsPerCluster	db 0x08				; 10
bpbReservedSectors		dw 0x0020			; 11
bpbNumberOfFATs			db 0x02				; 13
bpbRootEntries			dw 0x0000			; 14
bpbTotalSectors			dw 0x0000			; 16
bpbMedia				db 0xF8				; 18
bpbSectorsPerFAT		dw 0x0000			; 19
bpbSectorsPerTrack		dw 0x003E			; 21
bpbHeadsPerCylinder		dw 0x007C			; 23
bpbHiddenSectors		dd 0x00000000		; 25
bpbLargeTotalSectors	dd 0x0077FFDF		; 29
bpbSectorsPerFat		dd 0x00001DF8		; 33
bpbMirroringFlags		dw 0x0000			; 37
bpbVersion				dw 0x0000			; 39
bpbRootDirCluster		dd 0x00000002		; 41
bpbLocationFSInfSector	dw 0x0001			; 45
bpbLocationBackupSector	dw 0x0006			; 47
bpbReservedBootFName	times 12 db 0x00	; 49
bpbPhysDriveNum			db 0x00				; 61
bpbFlags				db 0x00				; 62
bpbExtendedBootSig		db 0x29				; 63
bpbVolumeSerialNum		dd 0xE58C48B6		; 64
; DOS 7.1 Extended BPB (79 bytes, without bpbOEM)
bpbVolumeLabel			db "           "	; 11 chars	; 68
bpbFSType				db "FAT32   "		; 8 chars	; 79


readsectors_offs	dw ReadSectors
print_offs			dw Print
data_offs			dw dataarea

;*********************************************
;	Bootloader Entry Point
;*********************************************

main:
	;----------------------------------------------------
	; MBR passes partition-entry in DS:SI, and drivenum in DL, save them to a temporary location
	;----------------------------------------------------
			mov bx, ds
			mov cx, si

	;----------------------------------------------------
	; Adjust segment registers
	;----------------------------------------------------
     
			cli						; disable interrupts
			mov ax, VBR_SEG			; setup registers to point to our segment
			mov ds, ax
			mov es, ax
			mov fs, ax
			mov gs, ax

	;----------------------------------------------------
	; create stack
	;----------------------------------------------------
     
			mov ax, 0x0000			; set the stack
			mov ss, ax
			mov sp, 0xFFFF
			sti						; restore interrupts

	;----------------------------------------------------
	; Save values from MBR to variables
	;----------------------------------------------------

			mov [drivenum], dl
			mov [part_entry_seg], bx		; won't that memory be overwritten !? (MBR relocates itself to 0x0600 !?)
			mov [part_entry_offs], cx
			push es
			mov es, bx
			mov di, cx
			mov eax, [es:di+8]
			pop es
			mov [partition_lba_begin], eax

	;----------------------------------------------------
	; Ensure 80*25
	;----------------------------------------------------

			mov ax, 3				; mode 80*25, clearscreen
			int 10h

	;----------------------------------------------------
	; Display loading message
	;----------------------------------------------------
     
			mov si, msgLoading
			call Print

	;----------------------------------------------------
	; Read VBR2
	;----------------------------------------------------

			mov eax, VBR2_SECTNUM
			mov cx, 1
			mov bx, VBR_SEG
			mov di, VBR2_OFFSET
			call ReadSectors			; I have checked the memory with memdump, it contained VBR2

	;----------------------------------------------------
	; Init FAT32-related variables
	;----------------------------------------------------

			; calc fat_begin_lba and cluster_begin_lba
			mov ecx, [partition_lba_begin]
			xor ebx, ebx
			mov bx, [bpbReservedSectors]
			add ecx, ebx
			mov [fat_begin_lba], ecx
			xor eax, eax
			mov al, [bpbNumberOfFATs]
			mov ebx, [bpbSectorsPerFAT]
			mul ebx
			add ecx, eax
			mov [cluster_begin_lba], ecx
				; root_dir_lba
			mov ebx, [bpbRootDirCluster]

			mov ecx, VBR_SEG		; they say that this is automatically done by the CPU (call cs:ecx) but it doesn't seem like that
			shl ecx, 4
			add cx, [VBR2_OFFSET+CLUSTER2LBA_OFFS]
			call ecx
;mov dx, cx
;mov cx, 4
;call stdio16_put_hex
;jmp $
mov ax,0xb800
mov gs, ax
mov BYTE [gs:80*24*2], byte '1'
;jmp $
			mov [root_dir_lba], eax
				; dir_entries_per_cluster_num
			xor eax, eax
			mov al, [bpbSectorsPerCluster]
			shl	eax, 4										; *16, (512/32=16 (number of dir-entries per sector))
			mov [dir_entries_per_cluster_num], eax

	;----------------------------------------------------
	; Load  file
	;----------------------------------------------------
			mov ax, FILE_BUFF_SEG
			mov es, ax
			mov di, 0
			mov ecx, VBR_SEG		; they say that this is automatically done by the CPU (call cs:ecx) but it doesn't seem like that (cs=0 !?)
			shl ecx, 4
			add cx, [VBR2_OFFSET+READFILE_OFFS]
			call ecx
mov ax,0xb800
mov gs, ax
mov BYTE [gs:80*24*2], byte '2'
			cmp ecx, 0
			jnz	.Fail

			mov si, msgCRLF
			call Print

			; pass on the values to loader
			mov dl, [drivenum]
			mov ebx, [partition_lba_begin]

			push WORD FILE_BUFF_SEG
			push WORD 0
			retf

.Fail		mov si, msgFailure
			call Print
			mov ah, 0
			int 0x16								; await keypress
			int 0x19								; warm boot computer


;************************************************
; Reads a series of sectors
; IN: 	EAX (Starting sector)
;		CX (Number of sectors to read)
;		BX:DI (Buffer to read to)
;************************************************
ReadSectors:
			pusha
			mov bp, 0x0005				; max. 5 retries
.Again		mov dl, [drivenum]
			mov BYTE [buff], 0x10		; size of this structure (1 byte)
			mov BYTE [buff+1], 0		; always zero (1 byte)
			mov WORD [buff+2], cx		; number of sectors to read (2 bytes)
			mov WORD [buff+4], di		; segment:offset ptr to memory to read to (4 bytes) 
			mov WORD [buff+6], bx 
			mov DWORD [buff+8], eax		; read from sector (8 bytes)
			mov DWORD [buff+12], 0 
			mov ah, 0x42
			mov si, buff 
			int 0x13
			jnc	.Ok
			dec bp
			jnz	.Again
			mov si, msgErrReadSector
			call Print
		jmp $
			int 0x18
.Ok			mov si, msgProgress
 			call Print
			popa
			ret


; IN: DS:SI (0 terminated string)
Print:		lodsb				; load next byte from string from SI to AL
			or	al, al			; Does AL=0?
			jz .Back			; Yep, null terminator found-bail out
			mov	ah, 0x0E		; Nope-Print the character
			int	0x10
			jmp	Print			; Repeat until null terminator found
.Back		ret					; we are done, so return


dataarea:
drivenum			db 0		; the drive we booted from
partition_lba_begin	dd 0
;	fat_begin_lba(DWORD)			= Partition_LBA_begin + Number_Of_Reserved_Sectors
;	cluster_begin_lba(DWORD)		= Partition_LBA_begin + Number_Of_Reserved_Sectors + (Number_Of_FATs * Sectors_Per_FAT)
;	sectors_per_cluster(BYTE)		= Sectors Per Cluster
;	root_dir_first_cluster(DWORD)	= Root Dir First Cluster
fat_begin_lba				dd 0
cluster_begin_lba			dd 0
root_dir_lba				dd 0
;lba_addr = cluster_begin_lba + (cluster_number - 2) * sectors_per_clusters (See usbfat32.asm for more details)
dir_entries_per_cluster_num		dd 0

buff 			times 16 db 0

;part_entry	times 16 db 0
part_entry_seg		dw 0
part_entry_offs		dw 0

;msgLoading			db 0x0D, 0x0A, "Loading Boot Image ", 0x0D, 0x0A, 0x00
msgLoading			db 0x0D, 0x0A, "Loading", 0x0D, 0x0A, 0x00
msgCRLF				db 0x0D, 0x0A, 0x00
msgProgress			db ".", 0x00
;msgFailure			db 0x0D, 0x0A, "ERROR: Key to Reboot", 0x0A, 0x00
msgFailure			db 0x0D, 0x0A, "Fail", 0x0A, 0x00
;msgErrReadSector	db 'Error reading sector', 0x0D, 0x0A, 0
msgErrReadSector	db 'ReadS', 0x0D, 0x0A, 0


times 510-($-$$) db 0
dw 0xAA55
