; KolibriOS bootloader
; this code has been written by diamond in 2005,2006,2007 specially for KolibriOS

; this code is loaded by our bootsector to 0000:8000
	format	binary
	use16

out_string = 0x7DAB
read_cluster = 0x7D15
relative_read = 0x7D22
next_cluster = 0x7D65

	org	0x8000
start:
; cs=ds=0, es undefined, ss=0, sp=7C00
	movzx	esp, sp
	push	1000h
	pop	es
; say hi to user
	mov	si, start_msg
	call	out_string
; parse image name
	mov	eax, [7C2Ch]	; root_cluster
	and	eax, 0xFFFFFFF
	mov	[cur_obj], root_string
.parsedir:
	push	ax
	mov	si, [imgnameofs]
	push	si
@@:
	lodsb
	cmp	al, 0
	jz	@f
	cmp	al, '\'
	jnz	@b
	dec     si
	mov     [missing_slash], si
	inc     si
@@:
	xchg	ax, [esp+2]
	mov	byte [si-1], 0
	mov	[imgnameofs], si
	call	fat32_parse_dir
	call    restore_slash
	pop	cx
	test	cl, cl
	jz	.end
	test	byte [es:di+0Bh], 10h
	mov	si, notdir_string
	jz	find_error_si
	jmp	.parsedir
.end:
	test	byte [es:di+0Bh], 10h
	mov	si, directory_string
	jnz	find_error_si
; parse FAT chunk
; runlist at 5000:0000
	mov	di, 4
	push	5000h
	pop	es
	mov	dword [es:di-4], 1
	stosd
.parsefat:
	call	next_cluster
	jnc	.done
	mov	ecx, [es:di-8]
	add	ecx, [es:di-4]
	cmp	eax, ecx
	jz	.contc
	mov	dword [es:di], 1
	scasd
	stosd
	jmp	.parsefat
.contc:
	inc	dword [es:di-8]
	jmp	.parsefat
.done:
	xor	eax, eax
	stosd
read_img_file:
	xor	si, si
	push	es
	pop	fs
; yes! Now read file to 0x100000
	xor	edi, edi
; read buffer to 1000:0000 and move it to extended memory
	push	1000h
	pop	es
	xor	bx, bx
.img_read_block:
	lods dword [fs:si]		; eax=length
	xchg	eax, ecx
	jecxz	.img_read_done
	lods dword [fs:si]		; eax=disk cluster
.img_read_cluster:
	pushad
; read part of file
	movzx	esi, byte [7C0Dh]
	mul	esi
	add	eax, [7A04h]
	push	ax
	mov	ax, 0x200
	div	si
	cmp	cx, ax
	jb	@f
	mov	cx, ax
@@:
	pop	ax
	add	[esp+1Ch], ecx
	sub	[esp+18h], cx
	imul	cx, si
	push	cx
	call	relative_read
	pop	cx
; move it to extended memory
	mov	byte [sou_addr+2], 1
.move_loop:
	push	cx
	cmp	cx, 80h
	jbe	@f
	mov	cx, 80h
@@:
	mov	ah, 87h
	xchg	cl, ch
	mov	si, movedesc
	push	cx es
	push	ds
	pop	es
	int	15h
	pop	es cx
	test    ah, ah
	mov	si, exmem_string
	jnz	find_error_si
	add	[dest_addr], ecx
	add	[dest_addr], ecx
	inc	byte [sou_addr+2]
	mov	al, ch
	mov	ah, cl
	pop	cx
	sub	cx, ax
	jnz	.move_loop
	popad
	test	cx, cx
	jnz	.img_read_cluster
	jmp	.img_read_block
.img_read_done:
; kolibri.img loaded; now load kernel.mnt
load_kernel:
	push	ds
	pop	es
	mov	[cur_obj], kernel_mnt_name
; read boot sector
	xor	eax, eax
	mov	bx, 500h
	mov	cx, 1
	call	read_img
; init vars
	mov	ax, [50Eh]	; reserved_sect
	add	ax, [51Ch]	; hidden
	mov	word [fat_start], ax
	xchg	ax, bx
	movzx	ax, byte [510h]		; num_fats
	mul	word [516h]		; fat_length
	add	ax, bx
; read root dir
	mov	bx, 700h
	mov	cx, [511h]	; dir_entries
	add	cx, 0Fh
	shr	cx, 4
	call	read_img
	add	ax, cx
	mov	[img_data_start], ax
	shl	cx, 9
	mov	di, bx
	add	bx, cx
	mov	byte [bx], 0
.scan_loop:
	cmp	byte [di], 0
	mov	si, notfound_string
	jz	find_error_si
	mov	si, kernel_mnt_name
	call	fat_compare_name
	jz	.found
	and	di, not 1Fh
	add	di, 20h
	jmp	.scan_loop
.found:
	and	di, not 1Fh
	mov	si, directory_string
	test	byte [di+0Bh], 10h
	jnz	find_error_si
; found, now load it to 1000h:0000h
	mov	ax, [di+1Ah]
; first cluster of kernel.mnt in ax
; translate it to sector on disk in kolibri.img
	push	ax
	dec	ax
	dec	ax
	movzx	cx, byte [50Dh]
	mul	cx
	add	ax, [img_data_start]
; now ax is sector in kolibri.img
	mov	[kernel_mnt_in_img], ax
	movzx	cx, byte [7C0Dh]
	div	cx
; now ax is cluster in kolibri.img and
; dx is offset from the beginning of cluster
	movzx	eax, ax
	push	5000h
	pop	ds
	xor	si, si
	mov	si, 1
.scani:
	sub	eax, [si]
	jb	.scanidone
; sanity check
	cmp	dword [si], 0
	push	data_error_msg
	jz	find_error_sp
	pop	cx
; next chunk
	add	si, 8
	jmp	.scani
.scanidone:
	add	eax, [si]	; undo last subtract
	add	eax, [si+4]	; get cluster
	push	0
	pop	ds
	movzx	ecx, byte [7C0Dh]
	push	dx
	mul	ecx		; get sector
	pop	dx
	movzx	edx, dx
	add	eax, edx
	add	eax, [7A04h]
	mov	[kernel_mnt_1st], eax
	pop	ax
	push	1000h
	pop	es
.read_loop:
	push	ax
	xor	bx, bx
	call	img_read_cluster
	shl	cx, 9-4
	mov	ax, es
	add	ax, cx
	mov	es, ax
	pop	ax
	call	img_next_cluster
	jc	.read_loop
	mov	ax, 'KL'
	mov	si, loader_block
	jmp	1000h:0000h

img_next_cluster:
	mov	bx, 700h
	push	ax
	shr	ax, 1
	add	ax, [esp]
	mov	dx, ax
	shr	ax, 9
	add	ax, word [fat_start]
	mov	cx, 2
	push	es
	push	ds
	pop	es
	call	read_img
	pop	es
	and	dx, 1FFh
	add	bx, dx
	mov	ax, [bx]
	pop	cx
	test	cx, 1
	jz	.1
	shr	ax, 4
.1:
	and	ax, 0FFFh
	mov	si, bad_cluster_string
	cmp	ax, 0FF7h
	jz	find_error_si
	ret
img_read_cluster:
	dec	ax
	dec	ax
	movzx	cx, byte [50Dh]	; sects_per_clust
	mul	cx
	add	ax, [img_data_start]
	movzx	eax, ax
;	call	read_img
;	ret
read_img:
; in: ax = sector, es:bx->buffer, cx=length in sectors
	pushad
	movzx	ebx, bx
	mov	si, movedesc
	shl	eax, 9
	add	eax, 93100000h
	mov	dword [si+sou_addr-movedesc], eax
	mov	eax, 9300000h
	mov	ax, es
	shl	eax, 4
	add	eax, ebx
	mov	[si+dest_addr-movedesc], eax
	mov	ah, 87h
	shl	cx, 8	; mul 200h/2
	push	es
	push	0
	pop	es
	int	15h
	pop	es
	cmp	ah, 0
	mov	si, exmem_string
	jnz	find_error_si
	popad
	ret

movedesc:
	times 16 db 0
; source
	dw	0xFFFF		; segment length
sou_addr dw	0000h		; linear address
	db	1		; linear address
	db	93h		; access rights
	dw	0
; destination
	dw	0xFFFF		; segment length
dest_addr dd	93100000h	; high byte contains access rights
				; three low bytes contains linear address (updated when reading)
	dw	0
	times 32 db 0

find_error_si:
	push	si
find_error_sp:
	mov	si, error_msg
	call	out_string
	mov	si, [cur_obj]
	call	out_string
	mov	si, colon
	call	out_string
	pop	si
	call	out_string
	mov     si, newline
	call    out_string
	jmp	$

file_not_found:
	mov	si, [esp+2]
	mov	[cur_obj], si
	push	notfound_string
	jmp	find_error_sp

restore_slash:
	mov     si, [missing_slash]
	test    si, si
	jz      @f
	and     [missing_slash], 0
	mov     byte [si], '\'
@@:     ret

	include 'fat32.inc'

if 0
write1st:
; callback from kernel.mnt
; write first sector of kernel.mnt from 1000:0000 back to disk
	push	cs
	pop	ds
	push	cs
	pop	es
; sanity check
	mov	bx, 500h
	mov	si, bx
	mov	cx, 1
	push	cx
	mov	eax, [kernel_mnt_1st]
	push	eax
	call	relative_read
	push	1000h
	pop	es
	xor	di, di
	mov	cx, 8
	repz	cmpsw
	mov	si, data_error_msg
	jnz	find_error_si
; ok, now write back to disk
	or	byte [read.patch1+2], 1
	or	byte [read.patch2+2], 1
	xor	bx, bx
	pop	eax
	pop	cx
	call	relative_read
	and	byte [read.patch1+1], not 1
	and	byte [read.patch2+2], not 2
; and to image in memory (probably this may be done by kernel.mnt itself?)
	mov	dword [sou_addr], 93010000h
	movzx	eax, [kernel_mnt_in_img]
	shl	eax, 9
	add	eax, 93100000h
	mov	dword [dest_addr], eax
	mov	si, movedesc
	push	ds
	pop	es
	mov	ah, 87h
	mov	cx, 100h
	int	15h
	cmp	ah, 0
	mov	si, exmem_string
	jnz	find_error_si
	retf
else
write1st = 0
end if

loader_block:
	db	1	; version
	dw	1	; flags - image is loaded
	dw	write1st	; offset
	dw	0		; segment

imgnameofs dw kolibri_img_name

; -----------------------------------------------
; ------------------ Settings -------------------
; -----------------------------------------------

; must be in lowercase, see ntfs_parse_dir.scan, fat32_parse_dir.scan
kernel_mnt_name 	db	'kernel.mnt',0
kolibri_img_name 	db	'kolibri.img',0

missing_slash   dw      0

start_msg	db	2,' KolibriOS bootloader, FAT32 flash version'
newline		db	13,10,0
error_msg	db	'Error'
colon		db	': ',0
root_string	db	'\',0
nodata_string	db	'$DATA '
notfound_string db	'not found',0
directory_string db	'is a directory',0
notdir_string	db	'not a directory',0
exmem_string	db	'extended memory error',0
bad_cluster_string db	'bad cluster',0
data_error_msg db	'data error',0

        align 2

; uninitialized data follows
cur_obj 		dw	?
img_data_start		dw	?
kernel_mnt_in_img	dw	?
fat_start		dw	?
kernel_mnt_1st		dd	?