; KolibriOS bootloader
; bootsector for loading from FAT32 flash (or hard) drive
; intended for use with mtldr_f file in root folder
; this code has been written by diamond in 2005,2006,2007 specially for KolibriOS

; this code is loaded by BIOS to 0000:7C00
	org	0x7C00
	jmp	@f
	nop
	times 57h db 0
;	file 'bt2.dat':3,57h
@@:
	cmp	byte [si], 80h
	jnz	@f
	mov	eax, [si+8]
	mov	[cs:7C1Ch], eax
@@:
	xor	eax, eax
	mov	ds, ax
	mov	ss, ax
	mov	sp, 7C00h
	mov	bp, sp
	mov	[boot_drive], dl
	cld
	sti
	push	800h
	pop	es
	movzx	ebx, word [bp+0Eh]	; reserved_sect
	mov	[fat_start], ebx
	mov	al, byte [bp+10h]	; num_fats
	mul	dword [bp+24h]		; sect_fat
	add	eax, ebx
; cluster 2 begins from sector eax
	movzx	ebx, byte [bp+0Dh]	; sects_per_clust
	add	bx, bx
	sub	eax, ebx
	mov	[data_start], eax
	mov	eax, [bp+2Ch]		; root_cluster
	and	eax, 0FFFFFFFh
fat32_parse_dir:
	xor	bx, bx
	mov	di, bx
	push	eax
	call	read_cluster
	movzx	cx, byte [bp+0Dh]	; sects_per_clust
	shl	cx, 4			; *0x200/0x20
scan_cluster:
	cmp	byte [es:di], 0
	jz	file_not_found
	push	cx di
	mov	cx, 11
	mov	si, mtldr_f
	repz	cmpsb
	pop	di cx
	jz	file_found
	add	di, 20h
	loop	scan_cluster
	pop	eax
	call	next_cluster
	jnc	file_not_found
	jc	fat32_parse_dir
file_found:
	pop	eax
	mov	ax, [es:di+14h]
	and	ax, 0FFFh
	shl	eax, 10h
	mov	ax, [es:di+1Ah]
; eax contains first cluster
@@:
	xor	bx, bx
	push	eax
	call	read_cluster
	mov	ax, es
	movzx	cx, byte [bp+0Dh]
	shl	cx, 5
	add	ax, cx
	mov	es, ax
	pop	eax
	call	next_cluster
	jc	@b
	jmp	0:8000h

file_not_found:
	mov	si, file_not_found_msg
sayerr:
	call	out_string
	jmp	$

read_cluster:
; in: eax = cluster, bx->buffer
	movzx	ecx, byte [bp+0Dh]
	mul	ecx
	add	eax, [data_start]

; read procedure
; in: eax = absolute sector
;     cx = number of sectors
;     es:bx -> buffer
read:
	add	eax, [bp+1Ch]	; hidden sectors
	push	es
read_loop:
	pushad
; allocate disk address packet on the stack
; qword +8: absolute block number
	push	0
	push	0 		; dword +C is high dword
	push	eax		; dword +8 is low dword
; dword +4: buffer address
	push	es		; word +6 is segment
	push	bx		; word +4 is offset
; word +2: number of blocks, limited to 7Fh
	sub	cx, 7Fh
	sbb	ax, ax
	and	ax, cx
	add	ax, 7Fh
	push	ax
	shl	ax, 5
	mov	cx, es
	add	cx, ax
	mov	es, cx
; word +0: size of packet = 10h
	push	10h
; now pair ss:sp contain address of disk address packet
	mov	ax, 4200h
	mov	dl, [boot_drive]
	mov	si, sp
	int	13h
	mov	si, disk_read_err
	jc	sayerr
	popaw
	popad
	add	eax, 7Fh
	sub	cx, 7Fh
	ja	read_loop
	pop	es
	ret

next_cluster:
	push	es
	push	ds
	pop	es
	mov	bx, 7E00h
; sector is 200h bytes long, one entry in FAT occupies 4 bytes
; => 80h entries in sector
	push	eax
	shr	eax, 7		; div 80h
	cmp	eax, [fat_cur_sector]
	jz	@f
	mov	[fat_cur_sector], eax
	add	eax, [fat_start]
	mov	cx, 1
	call	read
@@:
	pop	eax
	and	eax, 7Fh
	mov	eax, [7E00h+eax*4]
	and	eax, 0FFFFFFFh
	cmp	eax, 0FFFFFF7h
	mov	si, bad_cluster
	jz	sayerr
	pop	es
	ret

out_string:
	lodsb
	test	al, al
	jz	.xxx
	mov	ah, 0Eh
	mov	bx, 7
	int	10h
	jmp	out_string
.xxx:	ret

file_not_found_msg	db	'Cannot find file '
mtldr_f			db	'MTLD_F32   '
			db	13,10,0
disk_read_err		db	'Disk read error',13,10,0
bad_cluster		db	'Bad cluster',13,10,0
fat_cur_sector	dd	-1

	times (7DFEh - $) db 0
	db	55h, 0AAh

virtual at 7A00h
fat_start	dd	?
data_start	dd	?
boot_drive	db	?
end virtual