; Copyright (c) 2008-2009, diamond
; All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions are met:
;       * Redistributions of source code must retain the above copyright
;       notice, this list of conditions and the following disclaimer.
;       * Redistributions in binary form must reproduce the above copyright
;       notice, this list of conditions and the following disclaimer in the
;       documentation and/or other materials provided with the distribution.
;       * Neither the name of the <organization> nor the
;       names of its contributors may be used to endorse or promote products
;       derived from this software without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka <Lrz> ''AS IS'' AND ANY
; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
; DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;*****************************************************************************

; KordOS bootloader, based on mtldr, KolibriOS bootloader, by diamond
; It is used when main bootloader is Windows loader.

; this code is loaded:
; NT/2k/XP: by ntldr to 0D00:0000
; 9x: by io.sys from config.sys to xxxx:0100
; Vista: by bootmgr to 0000:7C00
	format binary
	use16

; in any case, we relocate this code to 0000:0600
	org 0x600
; entry point for 9x and Vista booting
	call	@f
	db	'NTFS'
@@:
	pop	si
	sub	si, 3
	cmp	si, 100h
	jnz	boot_vista
	mov	si, load_question + 100h - 600h
	call	out_string
;	mov	si, answer + 100h - 0600h		; already is
xxy:	mov	ah, 0
	int	16h
	or	al, 20h
	mov	[si], al
	cmp	al, 'y'
	jz	xxz
	cmp	al, 'n'
	jnz	xxy
; continue load Windows
;	call	out_string
;	ret
out_string:
	push	bx
@@:
	lodsb
	test	al, al
	jz	@f
	mov	ah, 0Eh
	mov	bx, 7
	int	10h
	jmp	@b
@@:
	pop	bx
	ret
xxz:
; boot KordOS
	call	out_string
; 9x bootloader has already hooked some interrupts; to correctly remove all DOS handlers,
; issue int 19h (reboot interrupt) and trace its DOS handler until original BIOS handler is reached
	xor	di, di
	mov	ds, di
	mov	word [di+4], new01handler + 100h - 600h
	mov	[di+6], cs
	pushf
	pop	ax
	or	ah, 1
	push	ax
	popf
; we cannot issue INT 19h directly, because INT command clears TF
;	int	19h	; don't issue it directly, because INT command clears TF
; so instead we use direct call
;	pushf		; there will be no IRET
	call	far [di + 19h*4]
xxt:
	xor	di, di
	mov	ds, di
	cmp	word [di + 8*4+2], 0F000h
	jz	@f
	les	bx, [di + 8*4]
	mov	eax, [es:bx+1]
	mov	[di + 8*4], eax
@@:
	mov	si, 100h
boot_vista:
; relocate cs:si -> 0000:0600
	push	cs
	pop	ds
	xor	ax, ax
	mov	es, ax
	mov	di, 0x600
	mov	cx, 2000h/2
	rep	movsw
	jmp	0:real_entry

load_question	db	'Load KordOS? [y/n]: ',0
answer	db	?
	db	13,10,0

new01handler:
; [sp]=ip, [sp+2]=cs, [sp+4]=flags
	push	bp
	mov	bp, sp
	push	ds
	lds	bp, [bp+2]
	cmp	word [ds:bp], 19cdh
	jz	xxt
	pop	ds
	pop	bp
	iret

; read from hard disk
; in: eax = absolute sector
;     cx = number of sectors
;     es:bx -> buffer
; out: CF=1 if error
read:
	pushad
	add	eax, [bp + partition_start - dat]
	cmp	[bp + use_lba - dat], 0
	jz	.chs
; LBA read
	push	ds
.lbado:
	push	ax
	push	cx
	cmp	cx, 0x7F
	jbe	@f
	mov	cx, 0x7F
@@:
; create disk address packet on the stack
; dq starting LBA
	push	0
	push	0
	push	eax
; dd buffer
	push	es
	push	bx
; dw number of blocks to transfer (no more than 0x7F)
	push	cx
; dw packet size in bytes
	push	10h
; issue BIOS call
	push	ss
	pop	ds
	mov	si, sp
	mov	dl, [bp + boot_drive - dat]
	mov	ah, 42h
	int	13h
	jc	.disk_error_lba
	add	sp, 10h		; restore stack
; increase current sector & buffer; decrease number of sectors
	movzx	esi, cx
	mov	ax, es
	shl	cx, 5
	add	ax, cx
	mov	es, ax
	pop	cx
	pop	ax
	add	eax, esi
	sub	cx, si
	jnz	.lbado
	pop	ds
	popad
	ret
.disk_error_lba:
	add	sp, 14h
	pop	ds
	popad
	stc
	ret

.chs:
	pusha
	pop	edi		; loword(edi) = di, hiword(edi) = si
	push	bx

; eax / (SectorsPerTrack) -> eax, remainder bx
	movzx	esi, [bp + sectors - dat]
	xor	edx, edx
	div	esi
	mov	bx, dx		; bx = sector-1

; eax -> dx:ax
	push	eax
	pop	ax
	pop	dx
; (dword in dx:ax) / (NumHeads) -> (word in ax), remainder dx
	div	[bp + heads - dat]

; number of sectors: read no more than to end of track
	sub	si, bx
	cmp	cx, si
	jbe	@f
	mov	cx, si
@@:

	inc	bx
; now ax=track, dl=head, dh=0, cl=number of sectors, ch=0, bl=sector
; convert to int13 format
	movzx	edi, cx
	mov	dh, dl
	mov	dl, [bp + boot_drive - dat]
	shl	ah, 6
	mov	ch, al
	mov	al, cl
	mov	cl, bl
	or	cl, ah
	pop	bx
	mov	si, 3
	mov	ah, 2
@@:
	push	ax
	int	13h
	jnc	@f
	xor	ax, ax
	int	13h	; reset drive
	pop	ax
	dec	si
	jnz	@b
	add	sp, 12
	popad
	stc
	ret
@@:
	pop	ax
	mov	ax, es
	mov	cx, di
	shl	cx, 5
	add	ax, cx
	mov	es, ax
	push	edi
	popa
	add	eax, edi
	sub	cx, di
	jnz	.chs
	popad
	ret

disk_error2	db	'Fatal: cannot read partitions info: '
disk_error_msg	db	'disk read error',0
disk_params_msg	db	'Fatal: cannot get drive parameters',0
start_msg	db	2,' KordOS bootloader',13,10,0
part_msg	db	'looking at partition '
part_char	db	'0'	; will be incremented before writing message
		db	' ... ',0
errfs_msg	db	'unknown filesystem',13,10,0
fatxx_msg	db	'FATxx'
newline		db	13,10,0
ntfs_msg	db	'NTFS',13,10,0
error_msg	db	'Error'
colon		db	': ',0
root_string	db	'\',0
nomem_msg	db	'No memory',0
filesys_string	db	'(filesystem)',0
directory_string db	'is a directory',0
notdir_string	db	'not a directory',0

; entry point for NT/2k/XP booting
; ntldr loads our code to 0D00:0000 and jumps to 0D00:0256
	repeat	600h + 256h - $
		db	1	; any data can be here; 1 in ASCII is a nice face :)
	end repeat
; cs=es=0D00, ds=07C0, ss=0
; esi=edi=ebp=0, esp=7C00
	xor	si, si
	jmp	boot_vista

real_entry:
; ax = 0
	mov	ds, ax
	mov	es, ax
; our stack is 4 Kb: memory range 2000-3000
	mov	ss, ax
	mov	sp, 3000h
	mov	bp, dat
	sti	; just for case
; say hi to user
	mov	si, start_msg
	call	out_string
; we are booting from hard disk identified by [boot_drive]
	mov	dl, [bp + boot_drive - dat]
; is LBA supported?
	mov	[bp + use_lba - dat], 0
	mov	ah, 41h
	mov	bx, 55AAh
	int	13h
	jc	.no_lba
	cmp	bx, 0AA55h
	jnz	.no_lba
	test	cl, 1
	jz	.no_lba
	inc	[bp + use_lba - dat]
	jmp	disk_params_ok
.no_lba:
; get drive geometry
	mov	ah, 8
	mov	dl, [bp + boot_drive - dat]
	int	13h
	jnc	@f
	mov	si, disk_params_msg
	call	out_string
	jmp	$
@@:
	movzx	ax, dh
	inc	ax
	mov	[bp + heads - dat], ax
	and	cx, 3Fh
	mov	[bp + sectors - dat], cx
disk_params_ok:
; determine size of cache for folders
	int	12h	; ax = size of available base memory in Kb
	sub	ax, 94000h / 1024
	jc	nomem
	shr	ax, 3
	mov	[bp + cachelimit - dat], ax	; size of cache - 1
; scan all partitions
new_partition_ex:
	xor	eax, eax	; read first sector of current disk area
	mov	[bp + extended_part_cur - dat], eax	; no extended partition yet
	mov	[bp + cur_partition_ofs - dat], 31BEh	; start from first partition
	push	es
	mov	cx, 1
	mov	bx, 3000h
	call	read
	pop	es
	jnc	new_partition
	mov	si, disk_error2
	call	out_string
	jmp	$
new_partition:
	mov	bx, [bp + cur_partition_ofs - dat]
	mov	al, [bx+4]	; partition type
	test	al, al
	jz	next_partition
	cmp	al, 5
	jz	@f
	cmp	al, 0xF
	jnz	not_extended
@@:
; extended partition
	mov	eax, [bx+8]	; partition start
	add	eax, [bp + extended_part_start - dat]
	mov	[bp + extended_part_cur - dat], eax
next_partition:
	add	[bp + cur_partition_ofs - dat], 10h
	cmp	[bp + cur_partition_ofs - dat], 31FEh
	jb	new_partition
	mov	eax, [bp + extended_part_cur - dat]
	test	eax, eax
	jz	partitions_done
	cmp	[bp + extended_part_start - dat], 0
	jnz	@f
	mov	[bp + extended_part_start - dat], eax
@@:
	mov	[bp + extended_parent - dat], eax
	mov	[bp + partition_start - dat], eax
	jmp	new_partition_ex
partitions_done:
	mov	si, total_kaput
	call	out_string
	jmp	$
not_extended:
	mov	eax, [bx+8]
	add	eax, [bp + extended_parent - dat]
	mov	[bp + partition_start - dat], eax
; try to load from current partition
; inform user
	mov	si, part_msg
	inc	[si + part_char - part_msg]
	call	out_string
; read bootsector
	xor	eax, eax
	mov	[bp + cur_obj - dat], filesys_string
	push	es
	mov	cx, 1
	mov	bx, 3200h
	call	read
	pop	es
	mov	si, disk_error_msg
	jc	find_error_si
	movzx	si, byte [bx+13]
	mov	word [bp + sect_per_clust - dat], si
	test	si, si
	jz	unknown_fs
	lea	ax, [si-1]
	test	si, ax
	jnz	unknown_fs
; determine file system
; Number of bytes per sector == 0x200 (this loader assumes that physical sector size is 200h)
	cmp	word [bx+11], 0x200
	jnz	unknown_fs
; is it NTFS?
	cmp	dword [bx+3], 'NTFS'
	jnz	not_ntfs
	cmp	byte [bx+16], bl
	jz	ntfs
not_ntfs:
; is it FAT? FAT12/FAT16/FAT32?
; get count of sectors to dword in cx:si
	mov	si, [bx+19]
	xor	cx, cx
	test	si, si
	jnz	@f
	mov	si, [bx+32]
	mov	cx, [bx+34]
@@:
	xor	eax, eax
; subtract size of system area
	sub	si, [bx+14]	; BPB_ResvdSecCnt
	sbb	cx, ax
	mov	ax, [bx+17]	; BPB_RootEntCnt
	add	ax, 0xF
	rcr	ax, 1
	shr	ax, 3
	sub	si, ax
	sbb	cx, 0
	push	cx
	push	si
	mov	ax, word [bx+22]
	test	ax, ax
	jnz	@f
	mov	eax, [bx+36]
@@:
	movzx	ecx, byte [bx+16]
	imul	ecx, eax
	pop	eax
	sub	eax, ecx
; now eax = count of sectors in the data region
	xor	edx, edx
	div	[bp + sect_per_clust - dat]
; now eax = count of clusters in the data region
	mov	si, fatxx_msg
	cmp	eax, 0xFFF5
	jae	test_fat32
; test magic value in FAT bootsector - FAT12/16 bootsector has it at the offset +38
	cmp	byte [bx+38], 0x29
	jnz	not_fat
	cmp	ax, 0xFF5
	jae	fat16
fat12:
	mov	[bp + get_next_cluster_ptr - dat], fat12_get_next_cluster
	mov	di, cx		; BPB_NumFATs
	mov	ax, '12'
	push	ax		; save for secondary loader
	mov	word [si+3], ax
	call	out_string
	movzx	ecx, word [bx+22]	; BPB_FATSz16
; FAT12: read entire FAT table (it is no more than 0x1000*3/2 = 0x1800 bytes)
.fatloop:
; if first copy is not readable, try to switch to other copies
	push	0x6000
	pop	es
	xor	bx, bx
	movzx	eax, word [0x320E]	; BPB_RsvdSecCnt
	push	cx
	cmp	cx, 12
	jb	@f
	mov	cx, 12
@@:
	call	read
	pop	cx
	jnc	fat1x_common
	add	eax, ecx	; switch to next copy of FAT
	dec	di
	jnz	.fatloop
	mov	si, disk_error_msg
	jmp	find_error_si
fat16:
	mov	[bp + get_next_cluster_ptr - dat], fat16_get_next_cluster
	mov	ax, '16'
	push	ax		; save for secondary loader
	mov	word [si+3], ax
	call	out_string
; FAT16: init FAT cache - no sectors loaded
	mov	di, 0x3400
	xor	ax, ax
	mov	cx, 0x100/2
	rep	stosw
fat1x_common:
	mov	bx, 0x3200
	movzx	eax, word [bx+22]	; BPB_FATSz16
	xor	esi, esi	; no root cluster
	jmp	fat_common
test_fat32:
; FAT32 bootsector has it at the offset +66
	cmp	byte [bx+66], 0x29
	jnz	not_fat
	mov	[bp + get_next_cluster_ptr - dat], fat32_get_next_cluster
	mov	ax, '32'
	push	ax		; save for secondary loader
	mov	word [si+3], ax
	call	out_string
; FAT32 - init cache for FAT table: no sectors loaded
	lea	si, [bp + cache1head - dat]
	mov	[si], si		; no sectors in cache:
	mov	[si+2], si		; 'prev' & 'next' links point to self
	mov	[bp + cache1end - dat], 3400h	; first free item = 3400h
	mov	[bp + cache1limit - dat], 3C00h
	mov	eax, [bx+36]	; BPB_FATSz32
	mov	esi, [bx+44]	; BPB_RootClus
	jmp	fat_common
not_fat:
unknown_fs:
	mov	si, errfs_msg
	call	out_string
	jmp	next_partition
fat_common:
	push	ss
	pop	es
	movzx	edx, byte [bx+16]	; BPB_NumFATs
	mul	edx
	mov	[bp + root_start - dat], eax	; this is for FAT1x
; eax = total size of all FAT tables, in sectors
	movzx	ecx, word [bx+17]	; BPB_RootEntCnt
	add	ecx, 0xF
	shr	ecx, 4
	add	eax, ecx
	mov	cx, word [bx+14]	; BPB_RsvdSecCnt
	add	[bp + root_start - dat], ecx	; this is for FAT1x
	add	eax, ecx
; cluster 2 begins from sector eax
	movzx	ebx, byte [bx+13]	; BPB_SecPerClus
	sub	eax, ebx
	sub	eax, ebx
	mov	[bp + data_start - dat], eax
; no clusters in folders cache
	mov	di, foldcache_clus - 2
	xor	ax, ax
	mov	cx, 7*8/2 + 1
	rep	stosw
	mov	[bp + root_clus - dat], esi
; load secondary loader
	mov	[bp + load_file_ptr - dat], load_file_fat
load_secondary:
	push	0x1000
	pop	es
	xor	bx, bx
	mov	si, kernel_name
	mov	cx, 0x30000 / 0x200
	call	[bp + load_file_ptr - dat]
; say error if needed
	mov	si, error_too_big
	dec	bx
	js	@f
	jz	find_error_si
	mov	si, disk_error_msg
	jmp	find_error_si
@@:
; fill loader information and jump to secondary loader
	mov	al, 'h'		; boot device: hard drive
	mov	ah, [bp + boot_drive - dat]
	sub	ah, 80h		; boot device: identifier
	pop	bx		; restore file system ID ('12'/'16'/'32'/'nt')
	mov	si, callback
	jmp	1000h:0000h

nomem:
	mov	si, nomem_msg
	call	out_string
	jmp	$

ntfs:
	push	'nt'		; save for secondary loader
	mov	si, ntfs_msg
	call	out_string
	xor	eax, eax
	mov	[bp + data_start - dat], eax
	mov	ecx, [bx+40h]	; frs_size
	cmp	cl, al
	jg	.1
	neg	cl
	inc	ax
	shl	eax, cl
	jmp	.2
.1:
	mov	eax, ecx
	shl	eax, 9
.2:
	mov	[bp + frs_size - dat], ax
; standard value for frs_size is 0x400 bytes = 1 Kb, and it cannot be set different
; (at least with standard tools)
; we allow extra size, but no more than 0x1000 bytes = 4 Kb
	mov	si, invalid_volume_msg
	cmp	eax, 0x1000
	ja	find_error_si
; must be multiple of sector size
	test	ax, 0x1FF
	jnz	find_error_si
	shr	ax, 9
	xchg	cx, ax
; initialize cache - no data loaded
	lea	si, [bp + cache1head - dat]
	mov	[si], si
	mov	[si+2], si
	mov	word [si+4], 3400h	; first free item = 3400h
	mov	word [si+6], 3400h + 8*8	; 8 items in this cache
; read first MFT record - description of MFT itself
	mov	[bp + cur_obj - dat], mft_string
	mov	eax, [bx+30h]	; mft_cluster
	mul	[bp + sect_per_clust - dat]
	push	0x8000
	pop	es
	xor	bx, bx
	push	es
	call	read
	pop	ds
	call	restore_usa
; scan for unnamed $DATA attribute
	mov	[bp + freeattr - dat], 4000h
	mov	ax, 80h
	call	load_attr
	push	ss
	pop	ds
	mov	si, nodata_string
	jc	find_error_si
; load secondary loader
	mov	[bp + load_file_ptr - dat], load_file_ntfs
	jmp	load_secondary

find_error_si:
	push	si
find_error_sp:
	cmp	[bp + in_callback - dat], 0
	jnz	error_in_callback
	push	ss
	pop	ds
	push	ss
	pop	es
	mov	si, error_msg
	call	out_string
	mov	si, [bp + cur_obj - dat]
@@:
	lodsb
	test	al, al
	jz	@f
	cmp	al, '/'
	jz	@f
	mov	ah, 0Eh
	mov	bx, 7
	int	10h
	jmp	@b
@@:
	mov	si, colon
	call	out_string
	pop	si
	call	out_string
	mov	si, newline
	call	out_string
	mov	sp, 0x3000
	jmp	next_partition
error_in_callback:
; return status: file not found, except for read errors
	mov	bx, 2
	cmp	si, disk_error_msg
	jnz	@f
	inc	bx
@@:
	mov	ax, 0xFFFF
	mov	dx, ax
	mov	sp, 3000h - 6
	ret

callback:
; in: ax = function number; only functions 1 and 2 are defined for now
; save caller's stack
	mov	dx, ss
	mov	cx, sp
; set our stack (required because we need ss=0)
	xor	si, si
	mov	ss, si
	mov	sp, 3000h
	mov	bp, dat
	mov	[bp + in_callback - dat], 1
	push	dx
	push	cx
; set ds:si -> ASCIIZ name
	lea	si, [di+6]
; set cx = limit in sectors; 4Kb = 8 sectors
	movzx	ecx, word [di+4]
	shl	cx, 3
; set es:bx = pointer to buffer
	les	bx, [di]
; call our function
	stc	; unsupported function
	dec	ax
	jz	callback_readfile
	dec	ax
	jnz	callback_ret
	call	continue_load_file
	jmp	callback_ret_succ
callback_readfile:
; function 1: read file
; in: ds:di -> information structure
;	dw:dw	address
;	dw	limit in 4Kb blocks (0x1000 bytes) (must be non-zero and not greater than 0x100)
;	ASCIIZ	name
; out: bx=0 - ok, bx=1 - file is too big, only part of file was loaded, bx=2 - file not found, bx=3 - read error
; out: dx:ax = file size (0xFFFFFFFF if file was not found)
	call	[bp + load_file_ptr - dat]
callback_ret_succ:
	clc
callback_ret:
; restore caller's stack
	pop	cx
	pop	ss
	mov	sp, cx
; return to caller
	retf

read_file_chunk.resident:
; auxiliary label for read_file_chunk procedure
	mov	di, bx
	lodsw
read_file_chunk.resident.continue:
	mov	dx, ax
	add	dx, 0x1FF
	shr	dx, 9
	cmp	dx, cx
	jbe	@f
	mov	ax, cx
	shl	ax, 9
@@:
	xchg	ax, cx
	rep	movsb
	xchg	ax, cx
	clc	; no disk error if no disk requests
	mov	word [bp + num_sectors - dat], ax
	ret

read_file_chunk:
; in: ds:si -> file chunk
; in: es:bx -> buffer for output
; in: ecx = maximum number of sectors to read (high word must be 0)
; out: CF=1 <=> disk read error
	lodsb
	mov	[bp + cur_chunk_resident - dat], al
	test	al, al
	jz	.resident
; normal case: load (non-resident) attribute from disk
.read_block:
	lodsd
	xchg	eax, edx
	test	edx, edx
	jz	.ret
	lodsd
; eax = start cluster, edx = number of clusters, cx = limit in sectors
	imul	eax, [bp + sect_per_clust - dat]
	add	eax, [bp + data_start - dat]
	mov	[bp + cur_cluster - dat], eax
	imul	edx, [bp + sect_per_clust - dat]
	mov	[bp + num_sectors - dat], edx
	and	[bp + cur_delta - dat], 0
.nonresident.continue:
	cmp	edx, ecx
	jb	@f
	mov	edx, ecx
@@:
	test	dx, dx
	jz	.read_block
	add	[bp + cur_delta - dat], edx
	sub	[bp + num_sectors - dat], edx
	sub	ecx, edx
	push	cx
	mov	cx, dx
	call	read
	pop	cx
	jc	.ret
	test	cx, cx
	jnz	.read_block
.ret:
	ret

cache_lookup:
; in: eax = value to look, si = pointer to cache structure
; out: di->cache entry; CF=1 <=> the value was not found
	push	ds bx
	push	ss
	pop	ds
	mov	di, [si+2]
.look:
	cmp	di, si
	jz	.not_in_cache
	cmp	eax, [di+4]
	jz	.in_cache
	mov	di, [di+2]
	jmp	.look
.not_in_cache:
; cache miss
; cache is full?
	mov	di, [si+4]
	cmp	di, [si+6]
	jnz	.cache_not_full
; yes, delete the oldest entry
	mov	di, [si]
	mov	bx, [di]
	mov	[si], bx
	push	word [di+2]
	pop	word [bx+2]
	jmp	.cache_append
.cache_not_full:
; no, allocate new item
	add	word [si+4], 8
.cache_append:
	mov	[di+4], eax
	stc
	jmp	@f
.in_cache:
; delete this sector from the list
	push	si
	mov	si, [di]
	mov	bx, [di+2]
	mov	[si+2], bx
	mov	[bx], si
	pop	si
@@:
; add new sector to the end of list
	mov	bx, di
	xchg	bx, [si+2]
	push	word [bx]
	pop	word [di]
	mov	[bx], di
	mov	[di+2], bx
	pop	bx ds
	ret

include 'fat.inc'
include 'ntfs.inc'

total_kaput	db	13,10,'Fatal error: cannot load the secondary loader',0
error_too_big	db	'file is too big',0
nodata_string	db	'$DATA '
error_not_found	db	'not found',0
noindex_string	db	'$INDEX_ROOT not found',0
badname_msg	db	'bad name for FAT',0
invalid_volume_msg db	'invalid volume',0
mft_string	db	'$MFT',0
fragmented_string db	'too fragmented file',0
invalid_read_request_string db 'cannot read attribute',0

kernel_name	db	'kord/loader',0

align 4
dat:

extended_part_start	dd	0	; start sector for main extended partition
extended_part_cur	dd	?	; start sector for current extended child
extended_parent		dd	0	; start sector for current extended parent
partition_start		dd	0	; start sector for current logical disk
cur_partition_ofs	dw	?	; offset in MBR data for current partition
sect_per_clust		dd	0
; change this variable if you want to boot from other physical drive
boot_drive	db	80h
in_callback	db	0

; uninitialized data
use_lba		db	?
cur_chunk_resident db	?
align 2
heads		dw	?
sectors		dw	?
cache1head	rw	2
cache1end	dw	?
cache1limit	dw	?
data_start	dd	?
cachelimit	dw	?
load_file_ptr	dw	?
cur_obj		dw	?
missing_slash	dw	?
root_clus	dd	?
root_start	dd	?
get_next_cluster_ptr	dw	?
frs_size	dw	?
freeattr	dw	?
index_root	dw	?
index_alloc	dw	?
cur_index_seg	dw	?
cur_index_cache	dw	?
filesize	dd	?
filesize_sectors dd	?
cur_cluster	dd	?
cur_delta	dd	?
num_sectors	dd	?
sectors_read	dd	?
cur_chunk_ptr	dw	?

rootcache_size	dw	?	; must be immediately before foldcache_clus
if $-dat >= 0x80
warning: unoptimal data displacement!
end if
foldcache_clus	rd	7
foldcache_mark	rw	7
foldcache_size	rw	7
fat_filename	rb	11

if $ > 2000h
error: file is too big
end if

; for NT/2k/XP, file must be 16 sectors = 0x2000 bytes long
repeat 0x2600 - $
	db	2	; any data can be here; 2 is another nice face in ASCII :)
end repeat
