
;*********************************************
;	boot2.asm
;		Loaded by boot.asm to a free sector. 
;		VBR2 (Volume Boot Record, VBR)
;		Implements necessary functions
;		(called from boot.asm) 
;		to read from Fat32 in real mode.
;		It would be possible to have code that 
;		reads from Fat32 in just one sector 
;		(or one VBR), but it is more convenient this way.
;*********************************************

bits 16

org	0x0200		; the offset from the start of the segment (i.e. from 0x7C00 set in VBR)

; VBR (boot.asm) related stuff
VBR_OFFSET			equ	0 ;0x07C0

BPB_OFFS			equ	3
READSECTORS_OFFS	equ	(BPB_OFFS+87)
PRINT_OFFS			equ	(READSECTORS_OFFS+2)
DATAAREA_OFFS		equ	(PRINT_OFFS+2)

; bpb-offsets in VBR
BYTESPERSECTOR_OFFS		equ 8
SECTORSPERCLUSTER_OFFS	equ 10
ROOTDIRCLUSTER_OFFS		equ 41


; dataarea-offsets in VBR
DRIVENUM_OFFS				equ 0
PART_BEGIN_OFFS				equ 1
FAT_BEGIN_OFFS				equ 5
CLUSTER_BEGIN_OFFS			equ 9
ROOT_DIR_OFFS				equ	13
DIR_ENTRIES_PERCLUS_OFFS	equ 17

; local
SECTOR_BUFF_SEG			equ	0x0810

END_OF_CLUSTER_MARKER	equ 0x0FFFFFF8	
DELETED_ENTRY			equ	0xE5
LFN_MASK				equ	0x0F

DIR_ENTRY_LEN	equ 32	; bytes

; Short Filename-entry (SFN)
SFN_NAME		equ	0
SFN_ATTRIB		equ	11
SFN_TIME10		equ	13
SFN_TIME		equ	14
SFN_DATE		equ	16	; Create
SFN_LA_DATE		equ 18	; Last access
SFN_CLUSTER_HI	equ 20
SFN_LA_MOD_TIM	equ 22
SFN_LA_MOD_DAT	equ 24	; Last Modify
SFN_CLUSTER_LO	equ 26
SFN_FILESIZE	equ 28

; Attribs
ATTRIB_READONLY		equ	0	; bit0
ATTRIB_HIDDEN		equ	1
ATTRIB_SYSTEM		equ	2
ATTRIB_VOLUMEID		equ	3
ATTRIB_DIRECTORY	equ	4
ATTRIB_ARCHIVE		equ	5


readfile_offs		dw ReadFile
cluster2lba_offs	dw Cluster2LBA


; IN: ES:DI(memaddr)
; OUT: ECX(size of file in bytes)
ReadFile:
mov ax,0xb800
mov gs, ax
mov BYTE [gs:80*24*2], byte 'R'
			mov [memaddr_seg], es
			mov [memaddr_offs], di
			; read root directory
			mov eax, [VBR_OFFSET+BPB_OFFS+ROOTDIRCLUSTER_OFFS]
			mov [cluster_num], eax
			mov eax, [VBR_OFFSET+DATAAREA_OFFS+ROOT_DIR_OFFS]
			and eax, 0x0FFFFFFF								; upper 4 bits of a cluster-number are reserved
.Read		xor cx, cx
			mov cl, [VBR_OFFSET+BPB_OFFS+SECTORSPERCLUSTER_OFFS]
			mov bx, SECTOR_BUFF_SEG
			mov di, 0
			call [VBR_OFFSET+READSECTORS_OFFS]
			mov ax, SECTOR_BUFF_SEG
			mov es, ax
			mov di, 0
.GetEntry	cmp BYTE [es:di], 0								; end of dir-entries?
			jz	.Back
			cmp BYTE [es:di], DELETED_ENTRY
			jz	.Inc
			; check entry
			cmp BYTE [es:di+SFN_ATTRIB], LFN_MASK
			jz	.Inc										; skip long FileName
			xor ax, ax
			mov al, [es:di+SFN_ATTRIB]
			bt	ax, ATTRIB_DIRECTORY						; is it a directory?
			jc	.Inc
			; compare strings, if not found ==> Inc
			mov cx, 11										; max. length of Short File Name
			mov si, filename
			call StrCmp		; AX, CX
			cmp ax, 0
			jnz	.Inc
			xor ebx, ebx
			mov bx, [es:di+SFN_CLUSTER_HI]
			shl	ebx, 16
			mov bx, [es:di+SFN_CLUSTER_LO]
			and ebx, 0x0FFFFFFF								; upper 4 bits of a cluster-number are reserved
			mov eax, [es:di+SFN_FILESIZE]
			mov [filesize], eax
			; read file from its cluster
				; read cluster, then from FAT the next ones
.NextClus	mov [cluster_num], ebx
			call Cluster2LBA		; fills EAX
			xor cx, cx
			mov cl, [VBR_OFFSET+BPB_OFFS+SECTORSPERCLUSTER_OFFS]
			mov bx, [memaddr_seg]
			mov di, [memaddr_offs]
			call [VBR_OFFSET+READSECTORS_OFFS]
			mov	ax, [VBR_OFFSET+BPB_OFFS+BYTESPERSECTOR_OFFS]					; calculate this just once instead ?
			xor bx, bx
			mov bl, [VBR_OFFSET+BPB_OFFS+SECTORSPERCLUSTER_OFFS]
			mul bx
			add [memaddr_offs], ax
			call GetNextCluster		; ES, DS
			cmp ebx, END_OF_CLUSTER_MARKER
			jge	.Back
			jmp .NextClus
.Inc		add di, DIR_ENTRY_LEN	
			xor ebx, ebx
			mov bx, di
			shr	ebx, 5										; /32
			cmp ebx, [VBR_OFFSET+DATAAREA_OFFS+DIR_ENTRIES_PERCLUS_OFFS]
			jnz	.GetEntry
			call GetNextCluster		; ES, DS
			cmp ebx, END_OF_CLUSTER_MARKER
			jge	.Back
			mov [cluster_num], ebx
			call Cluster2LBA
			jmp .Read
.Back		mov ecx, [filesize]
			ret


; IN: EBX(clusternum)
; OUT: EAX
Cluster2LBA:
;mov ax, 0
;;mov ax, 0x07E0
;mov ds, ax
mov ax,0xb800
mov gs, ax
mov BYTE [gs:80*24*2], byte 'C'
mov si, txtClus
call Print2
			push ebx
			push edx
			sub ebx, 2
			xor eax, eax
			mov al, [VBR_OFFSET+BPB_OFFS+SECTORSPERCLUSTER_OFFS]
			mul ebx
			add eax, [VBR_OFFSET+DATAAREA_OFFS+CLUSTER_BEGIN_OFFS]
			pop edx
			pop ebx
			ret


; IN:
; OUT: EBX(clusternum)
GetNextCluster:
; push/pop es, ds !?
			push eax
			push ecx
			push edx
			push ebp
			mov ebx, [cluster_num]
			and ebx, 0x0FFFFFFF	
			shr ebx, 7										; EBX: sectornumber in FAT
			push ebx
			mov ebp, [cluster_num]
			and ebp, 0x0FFFFFFF	
			shl ebp, 2
			xor edx, edx
			mov eax, ebp
			xor ebx, ebx
			mov bx, [VBR_OFFSET+BPB_OFFS+BYTESPERSECTOR_OFFS]
			div ebx
			mov ebp, edx
			pop ebx
			add ebx, [VBR_OFFSET+DATAAREA_OFFS+FAT_BEGIN_OFFS]
			; read 1 sector from FAT
			mov eax, ebx
			mov cx, 1
			mov bx, SECTOR_BUFF_SEG
			mov di, 0
			call [VBR_OFFSET+READSECTORS_OFFS]
			mov ax, SECTOR_BUFF_SEG
			mov es, ax
			mov di, bp
			mov ebx, [es:di]
			and ebx, 0x0FFFFFFF								; upper 4 bits of a cluster-number are reserved
			pop ebp
			pop edx
			pop ecx
			pop eax
			ret


;*************************************************;
; StrCmp  (case sensitive) 
;	DS:SI: addr of string1; In
;	ES:DI: addr of string2; In
;	CX: length; In
;	AX: <0 if s1 < s2; 0 if s1 == s2; >0 if s1 > s2 ; Out
;	skips chars in DS:SI that are > 126 ( greater then ASCII of '~' )
;*************************************************;
StrCmp:
			push bx
			push dx
			xor ax, ax
			xor bx, bx
.Next		mov al, BYTE [ds:si+bx]
			mov dl, BYTE [es:di+bx]
			cmp al, 126
			ja	.Inc					; skip chars > 126
			cmp al, dl
			jnz	.Sub
.Inc		inc bx
			cmp bx, cx
			jge	.Sub
			jmp .Next
.Sub		sub al, dl
.Back		pop dx
			pop bx
			ret


; IN: DS:SI (0 terminated string)
Print2:
			pusha
.Next		lodsb				; load next byte from string from SI to AL
			cmp al, 0
			jz .Back			; Yep, null terminator found-bail out
			mov	ah, 0x0E		; Nope-Print the character
			int	0x10
			jmp	.Next			; Repeat until null terminator found
.Back		popa
			ret


; ES:DI
PrintStr:
			pushad
			mov cx, di
			add cx, 11
.Next		mov al, [es:di]
			mov ah, 0x0E
			int 0x10
			inc di
			cmp di, cx
			jnz	.Next
			popad
			ret


filename		db "KRNLDR  SYS"
filesize		dd 0
memaddr_seg		dw 0
memaddr_offs	dw 0

cluster_num		dd	0

txtClus 	db 'Clus', 0 ;0x0D, 0x0A, 0
;txtRead db 'Read', 0

