/*
 *  grldrstart.S -- Startup code for GRLDR
 *  Copyright (C) 2004-2007  Tinybit(tinybit@tom.com)
 *  Copyright (C) 2007  Bean(bean@windrv.net)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * This program is used to generate the GRLDR file.
 *
 * Use the following shell command to generate the GRLDR file:
 *
 * 	cat grldrstart pre_stage2 > grldr
 *
 */

#define ASM_FILE
#include <shared.h>
#define ASM_FILE
	
#ifndef STAGE1_5
#include <stage2_size.h>
#else
#error cannot compile with STAGE1_5
#endif

#ifdef GRLDR_MBR
	.file	"mbrstart.S"
#elif defined(GRLDR_INSTALL)
	.file	"bootlacestart.S"
#else
	.file	"grldrstart.S"
#endif

#ifdef GRLDR_INSTALL
	//.data
#else
	.text

	.globl	start, _start

start:
_start:
#endif

_start1:

	/* Tell GAS to generate 16-bit real mode instructions */

	.code16

	. = _start1 + 0x00

	/* 1 byte at offset 0x00 will be overwritten for storing the EBIOS
	 * indicator later. This is safe because the jmp instruction only
	 * get executed once. The write happens after the jmp instruction
	 * have got executed.
	 *
	 * The value written would be 0x42 for EBIOS present(LBA) and 0x02
	 * for non-present(CHS).
	 *
	 */

	/* No cli, we use stack! BIOS or caller usually sets SS:SP=0000:0400 */

	jmp	1f	/* FAT32/NTFS routine comes to offset 0 */

	. = _start1 + 0x02

	.byte	0x80	/* bit0=1: disable GRLDR search on floppy */
			/* bit1=1: disable the boot of the previous MBR with
			 *	   invalid partition table */
			/* bit2=1: disable the feature of unconditional
			 *	   entrance to the command-line */
			/* bit3=1: disable geometry tune */
			/* bit7=1: disable the boot of the previous MBR prior
				   to the search for GRLDR */

	/* GRLDR.MBR uses offset 0x03 to indicate a timer counter. */

	/* 0xff indicates waiting forever,
	 * other value specifies the time in seconds to wait */

	. = _start1 + 0x03

#ifdef GRLDR_MBR
	.byte	0	// use 0 instead of 5 for grldr.mbr to work as usual.
#elif defined(GRLDR_INSTALL)
	.byte	5	// for bootlace.com
#else
	.byte	5	// for grldr
#endif

	/* a key press to wait. if AX returned from int16 equals this word,
	 * the desired action will occur. */

	. = _start1 + 0x04

	.word	0x3920		/* the space bar */

	. = _start1 + 0x06

	.byte	0xff	/* preferred boot drive number, 0xff for no-drive(i.e., drive not defined) */
	.byte	0xff	/* preferred partition number, 0xff for whole drive(a floppy that has no partition table) */

	. = _start1 + 8

	/* 2 bytes at offset 0x08 will be overwritten for storing the geometry
	 * reported by INT 13/AH=8.
	 *
	 * lo byte = Sectors Per Track
	 * hi byte = Max Head Number(=number of heads minus 1)
	 *
	 * If INT 13/AH=8 returns error, then the word here will be 0xFFFF. 
	 *
	 */

#if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL))

	/* filled in by mkisofs using the -boot-info-table option */

#;bi_pvd:	.long 0xDEADBEEF	/* LBA of primary volume descript */
#;bi_file:	.long 0xDEADBEEF	/* LBA of boot file */
#;bi_length:	.long 0xDEADBEEF	/* Length of boot file */
#;bi_csum:	.long 0xDEADBEEF	/* Checksum of boot file */
#;bi_reserved:	.space (10*4)		/* Reserved */

	. = _start1 + 0x40

#else

	/* filled in with BPB in case the drive(typically USB) is treated as floppy by buggy BIOSes */

	. = _start1 + 0x60

#endif	/* ! defined(GRLDR_MBR) && (! defined(GRLDR_INSTALL)) */

1:
	call	1f

#if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL))

	. = _start1 + 0x43

#else

	. = _start1 + 0x63

#endif	/* ! defined(GRLDR_MBR) && (! defined(GRLDR_INSTALL)) */

1:
	popw	%bx			/* Instruction Pointer of 1b */

#if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL))

	subw	$(1b - _start1), %bx	/* CS:BX=_start1 */

#else

	subw	$(1b - _start1), %bx	/* CS:BX=_start1 */

#endif	/* ! defined(GRLDR_MBR) && (! defined(GRLDR_INSTALL)) */

	shrw	$4, %bx
	movw	%cs, %ax
	addw	%ax, %bx		/* BX:0000=_start1 */

#if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL))

	/* we are booted from BOOT.INI, or whole GRLDR image already loaded */

	/* Let CS:0000=_start1 */
	pushw	%bx			/* BX:0000=_start1 */

	#;pushw	$(1f - _start1)
	.byte	0x6A, (1f - _start1)

	lret
	. = . - (. - _start1) / 0x80
1:
	addw	$((grldr_signature - _start1 + 4 + STAGE2_SIZE - 4) >> 4), %bx
	movw	%bx, %ds

	cmpl	$0xCE1A02B0, ((STAGE2_SIZE - 4) & 0x0F)
	pushw	%cs
	popw	%ds			/* DS:0000=_start1 */
	je	grldr_real_start	/* whole image loaded. boot it! */

	/* bad! we might be loaded by a buggy BIOS with a no-emulation-mode
	 * bootable CD. The buggy BIOS might load only 1 CD-ROM sector(2048
	 * bytes) of our grldr image. So we need this check.
	 */

	/* Our cdrom_check code begins at 0x1BE and overlaps the partition
	 * table. Just in case someone replace it with a partition table and
	 * use this sector as an MBR, we do this additional test for safety.
	 */

	/* We should avoid using opcode 0x00 and 0x80 at cdrom_check. */

	/* Note that if cdrom_check code is present, then we are booting from
	 * no-emulation mode cdrom.
	 */

	testb	$0x7F, cdrom_check - _start1	/* is it 0x00 or 0x80? */
	jz	1f			/* yes, cdrom_check not found */
	call	cdrom_check		/* no, cdrom_check is present */
1:
//	/* DS:0000=_start1 */
//
//	/* Let CS:0000=_start1 */
//	pushw	%ds
//
//	#;pushw	$(1f - _start1)
//	.byte	0x6A, (1f - _start1)
//
//	lret
//	. = . - (. - _start1) / 0x80
//1:
#else
	/* BX:0000=_start1 */

	movw	%bx, %ds

	/* Let CS:0000=_start1 */
	pushw	%bx

	#;pushw	$(1f - _start1)
	.byte	0x6A, (1f - _start1)

	lret
	. = . - (. - _start1) / 0x80
1:
#endif

	/* CS:0000=DS:0000=_start1 */

	/* we are loaded by BIOS or another boot loader */

#define GRLDR_CS 0x2000		/* grldr code segment */
				/* hope this segment never be used by all */
				/*	subsequent partition boot records */
#if 0
	/* for single sector boot record */
#define	MONITOR	0x7e10
#else
	/* for 4-sector NTFS boot record */
#define	MONITOR	0x8410
#endif

//	cli
	pushw	$GRLDR_CS
	popw	%ss
	movw	$0x9000, %sp	/* SS:SP=0x9d000, keep away from EBDA data */
//	sti
	
	/* Extended BIOS Data Area should not take up space below 0x9d000 */
	
	/* 
	 * 0x07c00-0x07dff	This sector. Another boot loader load us here
	 * 0x0d000-0x14dff	partition/floppy boot track(bootsector,etc)
	 * 0x94000-0x9bdff	master boot track(MBR,etc,usually 63 sectors)
	 * 0x9be00-0x9c3ff	3 sectors for temp extended partition entries
	 * 0x9c400-0x9cfff	6 sectors for stack
	 */

#define FS_BOOT	0xd00		/* segment of partition boot track */

	xorw	%cx, %cx
//	pushw	%cx		/* CX=0 */
	movw	$0x0080, %dx
//	movb	$8, %ah		/* read drive parameters changes DX,ES,DI,BX */
//	call	int13
//	popw	%ax		/* AX=0 */

	pushw	%ss		/* SS=0x9400 */
	popw	%es		/* ES=0x9400 */

//	jc	1f

//	andb	$63, %cl	/* AL=sectors per track, CF cleared */

//	stc
//	jz	1f

//	xchgw	%ax, %cx	/* this moves CL to AL, and CX=0 */
	movw	$(((pre_stage2_start - _start1) >> 9) + 0x200), %ax
//	movb	$0x02, %ah
	movw	%ax, %bp	/* save AX to BP: read 1 track */
	xorw	%bx, %bx	/* ES already has a known value of 0x9400 */
	incw	%cx
	call	int13
//	stc
//	int	$0x13		/* read master boot track to ES:0000 */
	jc	1f
	negb	%ah		/* set CF=1 if non-zero */
1:
	pushw	%cs		/* DS=0 */
	popw	%ds		/* DS=CS */
	pushfw			/* CF=1 on error */
	
	/* CS=DS=old segment. ES=SS=new segment. */

	/* Move the code and error messages from DS:0000 to 9400:0000, do not
	 * touch the partition table
	 */
	xorw	%si, %si
	xorw	%di, %di
	movw	$223, %cx	/* 223 words = 446 bytes = 0x1be bytes */
	cld
	repz movsw		/* SI=DI=0x1be, CX=0 */

	/********************************************************************/
	/*  At this moment we are still not sure whether the helper is ok.  */
	/********************************************************************/

disk_serial_number_structure:

#if (defined(GRLDR_MBR)) || (defined(GRLDR_INSTALL))

	/********************************************************************/
	/* This piece of code is structured! It contains address 0x1FFC and */
	/* a disk serial number that can be created by grub4dos installers. */
	/********************************************************************/

	movw	$0x1FFC, %bx		/* .byte 0xBB, 0xFC, 0x1F */

	/* if the boot loader has loaded more than one sector, we use them */
	movl	$0x93cb4d05, %eax	/* date-time for disk serial number */

		/* "MOV EAX"(0x66, 0xB8) followed by the changeable S.N. */
#else
	movw	$(grldr_signature - _start1), %bx	/* BX=0x1FFC */

	/* if the boot loader has loaded more than one sector, we use them */
	movl	$0xAA555247, %eax	/* "GR" 0x55 0xAA */
#endif

disk_serial_number_structure_end:

//#if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL))
	cmpl	%eax, (%bx)		/* DS=old segment of 07C0:0000 */
	jne	1f

	/* The MOVE_HELPER code is in the old segment of 07C0:0000 */

	call	move_helper	/* SI=0x1be, CX=0 */
1:
//#endif

	/* Jump to new segment! */
#if 1
	ljmp	$GRLDR_CS, $(1f - _start1)
#else
	pushw	%ss		/* 0x9400 */

	//pushw	$(1f - _start1)
	.byte	0x6A, (1f - _start1)

	lret
	. = . - (. - _start1) / 0x80
#endif
1:

	/* We are at the new segment. CS=ES=SS=new segment. */

	/* But DS is still old segment. */

	pushw	%ss
	popw	%ds

	/* CS=DS=ES=SS=new segment. */

	//movw	$0x01be, %si

	/* check the existence of helper */
	cmpl	%eax, (%bx)

#if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL))

	jne	Error_or_prev_MBR	/* Missing helper */

#else

	je	1f

	/* try to load helper from floppy */

	pushal

	movw	0x18, %cx	/* BPB sectors per track at offset 0x18 */

	cmpw	$0x3F, %cx
	ja	3f

	/* CH=0 */

	movb	$(((pre_stage2_start - _start1) >> 9) - 1), %al
	cmpb	%al, %cl
	jbe	3f

	//decw	%ax		/* skip the first sector already loaded */
	
	movw	$3, %di		/* retry 3 times on read failure */
2:
	movb	$2, %ah		/* BIOS disk read */
	cwd			/* DX=0 for floppy head 0 */
	movw	$0x200, %bx	/* ES:BX immediately follow this sector */
	movb	$2, %cl		/* CH=0,skip the first sector already loaded */

	call	read_disk_with_reset_and_dec_di
	jnc	3f

	jnz	2b
3:
	popal
	cmpl	%eax, (%bx)		/* helper loaded? */

	jne	Error_or_prev_MBR	/* Missing helper */

	/* Helper is loaded from floppy, so we set floppy as preferred. */
	//movw	$0xff00, 0x06
	//movw	%cx, 0x06	/* CX=0 */
	call	set_floppy_preferred
1:
#endif

	popfw			/* CF=1 on error */
	jc	try_floppy	/* harddisk (hd0) failed, try floppy (fd0) */

	/*********************************/
	/* Helper is successfully loaded */
	/*********************************/
1:
	pushw	%cs
	popw	%ds
	lodsw
	movb	%ah, %dh	/* head number */
	lodsw
	movw	%ax, %cx	/* sector and cylinder number */
	andb	$63, %al
	//stc
	jz	helper_call_c
	
	///* use BP to calculate the sectors to read within 1 track */
	////subw	%bp, %ax
	//subb	$4, %al
	//decw	%ax		/* decb	%al */
	//negb	%al		/* AL=sectors upto the end of the track */
	movb	$0x01, %al
7:
	movw	$3, %di		/* retry 3 times on read failure */
2:
	movb	$0x02, %ah
	pushw	$FS_BOOT
	popw	%es		/* ES=FS_BOOT */
	xorw	%bx, %bx	/* read partition boot track to FS_BOOT:0000 */

#if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL))
	pushaw
//	int	$0x13		/* read partition boot track to FS_BOOT:0000 */
	call	int13
	popaw

	jnc	helper_call

	pushaw
	xorw	%ax, %ax
//	int	$0x13
	call	int13
	popaw
	decw	%di
#else
	call	read_disk_with_reset_and_dec_di
	jnc	helper_call
#endif

	jnz	2b

helper_call_c:

	stc

helper_call:
	/* find GRLDR in this partition
	 * before the call:
	 *	CF=1		: indicates an invalid or corrupt entry
	 *	CF=0		: indicates a valid entry
	 *
	 * on return:
	 * 	CF=1		: means "below", try next entry
	 *	CF=0,ZF=1	: means "equal", helper did nothing, so we need
	 *			  a further try to boot via NT bootsector
	 *	CF=0,ZF=0	: means "above", helper succeeded, boot it now
	 */
	call	helper_start	/* change to jmp 6f if helper not present */
	ja	filesystem_boot	/* helper succeeded, directly boot it */
6:

add_sub_si:

	/* extended partition check routine will adjust this to
	 *
	 *	0x83, 0xEE, 0x04 for "subw $4, %si"
	 *
	 *			 or
	 *
	 *	0x83, 0xC6, 0xFC for "addw $-4, %si" 
	 *
	 * so that SI keeps the value 0x1fe.
	 */
	addw	$12, %si	/* 0x83, 0xC6, 0x0C */

	. = add_sub_si + 3

	/* extended partition check routine will adjust the word 0x1fe at
	 * (add_sub_si + 5). The value 0x1ff or greater indicates there are
	 * entries need to be treated. The value 0x1fe indicates no entries
	 * left, and the floppy should be checked.
	 */

	cmpw	$0x01fe, %si	/* 0x81, 0xFE, 0xfe, 0x01 */
				/* All entries checked done? */
	jb	1b		/* No, check the next entry */
	ja	5f		/* floppy already checked. Fail and hang */

try_floppy:

	movw	$0x31b2, %si	/* a value big enough */
	//movb	$8, %ah		/* read drive parameters changes DX,ES,DI,BX */
	//cwd			/* DL=0 for floppy */
	//pushw	%dx		/* DX=0 */
	//call	int13
	//popw	%ax		/* AX=0 */
	//jc	5f		/* floppy failure, issue "Error" and hang */
	//cwd			/* DX=0 */
	//xchgw	%ax, %cx	/* this moves CL to AL, and CX=0 */
	//andb	$63, %al	/* AL=sectors per track */
	//jz	5f		/* invalid value. floppy failure. hangs */
	movb	$4, %al
	movw	$1, %cx
	xorw	%dx, %dx
	jmp	7b

5:
Error_or_prev_MBR:

	/* GRLDR not found, print "Error" or launch previous MBR */
	movw	$(message_string - _start1), %si

	call	print_message	/* CS:SI points to message string */
3:	jmp	3b

int13:
	pushw	%ds
	pushw	%es
//	pushw	%bx
	pushw	%dx
	pushw	%si
	pushw	%di
	pushw	%bp
	stc
	int	$0x13
	popw	%bp
	popw	%di
	popw	%si
	popw	%dx
//	popw	%bx
	popw	%es
	popw	%ds
	ret

#if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL))
filesystem_boot:
	/* The partition boot record successfully modified, just boot it */

	/*
	 * The boot might fail, but we want to take back the control.
	 * So we save the registers now.
	 */
	pushw	%ds
	pushw	%es
	pushal

	/* DS=CS=GRLDR_CS, ES=FS_BOOT */

	/* save GRLDR_CS */

	movw	%es, %bx	# save old ES to BX

	cli
	lgdt	gdt - _start1
	movl	%cr0, %eax
	orb	$1, %al
	movl	%eax, %cr0

	movw	$8, %si
	movw	%si, %es

	xorl	%esi, %esi
	xorl	%edi, %edi
	movl	$(0x9000 / 4), %ecx

	cld
	repz movsl

	movw	$16, %si
	movw	%si, %es

	andb	$0xfe, %al
	movl	%eax, %cr0

	movw	%bx, %es	# restore ES from BX

	/* move FS_BOOT:0000 to 0:7c00 */
#if 0
	/* for single sector boot record */
	movw	$0x0200, %cx	/* move 2 sectors, the old FS_BOOT:0000 will keep untouched.  */
#else
	/* for 4-sector NTFS boot record */
	movw	$0x0400, %cx	/* move 4 sectors, the old FS_BOOT:0000 will keep untouched.  */
#endif
	xorw	%si, %si
	pushw	%si	/* SI=0, for the segment of 0000:7c00 */
	movw	$0x7c00, %di
	pushw	%di	/* DI=0x7c00, for the offset of 0000:7c00 */
	pushw	%es	/* ES=FS_BOOT */
	popw	%ds	/* DS=FS_BOOT */
	pushw	%si	/* SI=0 */
	popw	%es	/* ES=0 */
	cld
	repz movsw

	movw	$MONITOR, %di
	movw	$(restore_GRLDR_CS - _start1), %si
	movw	$((gdt_end - restore_GRLDR_CS) / 4), %cx
	cld
	repz cs movsl		/* CS segment override prefix(=0x2E) */

	pushw	%es	/* ES=0 */
	popw	%ds	/* DS=0 */
	sti
	lret	//ljmp	$0, $0x7c00
#endif

try_next_partition:

	cli
	movw	$GRLDR_CS, %ax
	movw	%ax, %ss
	movw	$(0x9000-36), %sp
	sti

	/* restore the registers and continue */
	popal
	popw	%es
	popw	%ds
	jmp	add_sub_si

#if (defined(GRLDR_MBR)) || (defined(GRLDR_INSTALL))
read_disk_with_reset_and_dec_di:
	pushaw
//	int	$0x13
	call	int13
	popaw

	jnc	3f

	pushaw
	xorw	%ax, %ax
//	int	$0x13
	call	int13
	popaw
	decw	%di
	stc
3:
	ret
#endif

	/* prints string CS:SI (modifies AX BX SI) */
3:
	//xorw	%bx, %bx	/* video page 0 */
	movb	$0x0e, %ah	/* print char in AL */
	int	$0x10		/* via TTY mode */

print_message:

	lodsb	%cs:(%si), %al	/* get token */
	cmpb	$0, %al		/* end of string? */
	jne	3b
	ret

message_string:

#if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL))
	.ascii	"\r\nMissing helper.\0"
#else
	.ascii	"\r\nMissing MBR-helper.\0"
#endif

#;buggy_bios_string:
#;
#;	.ascii	"\r\nBuggy BIOS!\0"

	/* Make sure the above code does not occupy the partition table */

#if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL))
	/* offset value here must be less than or equal to 0x1be */
	. = . - ((. - _start1) / 0x1bf)
#else
	/* offset value here must be less than or equal to 0x1b8 */
	. = . - ((. - _start1) / 0x1b9)
#endif

	/* The following code may occupy the same area as the partition table */

#if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL))

	/* we are not booted from MBR. So we can reuse the area of partition
	 * table for our code.
	 */

	. = _start1 + 0x1be

cdrom_check:

	/* DS=CS points to the sector start. */

	/* BX segment points to near the end of GRLDR image. */

	popw	%ax	/* old return IP */

	/* set BX as the new safe stack. */
	movw	%bx, %ss
	movw	$0xFFF0, %sp

	pushw	%ax	/* old return IP */

	/* check if DL is no-emulation-mode bootable CDROM. */
	pushw	%ds

	cmpb	$0x80, %dl
	jb	1f	/* not a valid no-emulation-mode cdrom drive number */

	cmpw	$0xAA55, 0x7FE		/* 2048 bytes loaded? */
	jne	1f

//	cmpw	$0xAA55, 0x5FE		/* 2048 bytes loaded? */
//	jne	1f

	movw	$0x0180, %si
	movw	$0x4B01, %ax
	pushw	$0x0040
	//.byte	0x6A, 0x40
	popw	%ds
	pushw	%ds
	popw	%es
	movb	$0x13, (%si)
	call	int13

	/* ignore CF */
#;	jc	2f	/* not in emulation mode */
	xorl	%eax, %eax
	xorw	%bp, %bp
	testb	$0x0F, 1(%si)	/* boot media type is No Emulation? */
	jnz	2f	/* no, it simulates floppy or hard disk. */
	cmpb	%dl, 2(%si)	/* drive number */
	jnz	2f	/* invalid drive */

	/* OK! it is no-emulation-mode cdrom drive. */
	movl	4(%si), %eax	/* LBA of GRLDR */
	incw	%bp

2:
	jmp	cdrom_helper
1:
	popw	%ds
	ret

#else

	. = _start1 + 0x1fe	/* boot signature */

#endif	/* ! defined(GRLDR_MBR) && (! defined(GRLDR_INSTALL)) */

/* partition entries in the extended partitions will overwrite code here upto
 * 0x3fd.
 *
 * the extended partition entries will occupy a temp area at 0x9be00-0x9c3ff 
 */

#if (defined(GRLDR_MBR)) || (defined(GRLDR_INSTALL))
	.word	0xaa55
#endif

	. = _start1 + 0x200

/* if it is in the Master Boot Track, the second sector can be used to backup
 * the previously working MBR, typically, the MS MBR. if the backup copy of
 * the MBR cannot boot(because, e.g., it depends on another sector of code
 * that does not exist for now), then please do not set the ending signature
 * to 0xAA55, that is to say, if the signature is already 0xAA55, you should
 * change it to another value(for example, 0x0000).
 */

#if (! defined(GRLDR_INSTALL))
#if 0
print_cl:
	pushaw

	movw	%cx, %ax
	movb	$16, %cl
	divb	%cl		# quo=AL, rem=AH
	orw	$0x3030, %ax

	cmpb	$0x39, %ah
	jbe	1f
	addb	$7, %ah
1:
	cmpb	$0x39, %al
	jbe	1f
	addb	$7, %al
1:
	movb	%ah, %cl

	xorw	%bx, %bx

	movb	$0x0e, %ah
	int	$0x10

	movb	$0x0e, %ah
	movb	%cl, %al
	int	$0x10

	movw	$0x0e20, %ax
	int	$0x10

	popaw
	ret
#else
#if 0
	.word	5, 0x47, 0x52, 0x4c, 0x44, 0x52, 4, 0x24
	.word	0x49, 0x33, 0x30, 0xe000, 0, 0x3000, 0, 0
#else
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90

	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
#endif
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90

	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90

	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90

	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
#endif
	. = _start1 + 0x256	/* cmdcons comes here */

#if 0
	jmp	1f
#else
	.byte	0x90, 0x90
#endif

	. = _start1 + 0x258

	.byte	0x90, 0x90

	. = _start1 + 0x25a

	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90
	.byte	0x90, 0x90

	. = _start1 + 0x26a
1:
	//movw	%cs, %ax
	//movw	%ax, %ds
	//jmp	single_boot_sector

	/* a value < 0x80 here means we are not booted from no-emulation-mode
	 * bootable CD.
	 */
	movb	$0x7F, %dl
	jmp	_start1

#endif	/* (! defined(GRLDR_INSTALL)) */

#if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL))
cdrom_helper:

	/* IP and old_DS is on the stack. */

	/* DS=ES=40h */

	/* Stack is high and safe. */

	/* EAX is LBA. if EAX==0, LBA is unknown. */

	/* check if the first sector is the same as the current one */

	/* load the first sector onto the sector immediately follows */
1:
	pushw	%cs
	popw	%bx		/* BX=CS=old_DS=load_segment */
	addw	$0x0080, %bx	/* buffer segment */
	movw	%bx, %es	/* ES changed! */
	call	load_cd_sector

	/* compare the two sectors */
	movw	$0x200, %cx
	xorw	%si, %si
	xorw	%di, %di
	cld
	cs repz cmpsl
	je	load_the_rest	/* 1st sector is ok, continue */
not_grldr:
	testw	%bp, %bp
	jz	2f
	xorw	%bp, %bp
	xorl	%eax, %eax
2:
	incl	%eax
	jnz	1b		/* try next */

cd_no_grldr:

	popw	%ds		/* DS=load_segment */

	# Here we use error message and routine in FAT32 boot sector
	# which is also inside the 2048-byte CD sector.

	movw	$(msg_BootError_32 - _start1), %si
	jmp	boot_error_32
	
load_cd_sector:
	/* input:	EAX	LBA
	 *		BX	buffer segment(buffer offset=0)
	 *		DS	0x40 (or another safe one)
	 * output:
	 *		SI			changed(=0x1A0)
	 *		16 bytes at DS:SI	destroyed
	 */

	movw	$0x1A0, %si

	/* disk address packet */
	movl	$0x00010010, (%si)	/* load 1 sector each time. */
	movw	$0, 4(%si)		/* buffer offset=0 */
	movw	%bx, 6(%si)		/* buffer segment */
	movl	%eax, 8(%si)		/* LBA lo 32 bits */
	movl	$0, 12(%si)		/* LBA hi 32 bits */

//	pushw	%ds
//	pushw	%es
	pushal
	movb	$0x42, %ah
	call	int13
	popal
//	popw	%es
//	popw	%ds
	ret

load_the_rest:

	/* load all sectors (except the first one) */

	/* EAX = first sector(LBA) of GRLDR */

	pushl	%eax

	pushw	%cs
	popw	%bx		/* BX=CS=old_DS=load_segment */
//	movw	%bx, %es
	/* 6144 = 0x1800 = 3 sectors > 4KB, this is for the additional 4KB-preset-menu at the end of grldr */
	movw	$((grldr_signature - _start1 + 4 + STAGE2_SIZE - 1 + 6144) / 2048), %cx	/* sectors to load */
1:
	incl	%eax		/* next sector */
	addw	$0x0080, %bx	/* buffer segment */

	call	load_cd_sector

	loop	1b

	/* loading is completed. BX=segment of the last sector. */

	subw	$0x0181, %bx	/* decw	%bx */
	movw	%bx, %ds

	popl	%eax
//	subl	$((grldr_signature - _start1 + 4 + STAGE2_SIZE - 1 + 6144) / 2048), %eax

	/* check the ending signature */
	cmpl	$0xCE1A02B0, ((grldr_signature - _start1 + 4 + STAGE2_SIZE - 1) % 2048) + 13
	jne	not_grldr
#;	je	grldr_real_start	/* yes. boot it! */

#;	/* it is not our grldr image, return and use MBR-helper. */
#;
#;4:
#;	//jmp	grldr_real_start
#;	popw	%ds
#;	ret

grldr_real_start:

	#; FAT_12_16 no longer be used. So comment out.
	#;je	1f	/* jc	1f */
	#;//ZF=0	/* CF cleared, so we are coming from FAT_12_16 */
	#;popw	%dx		/* discard the cluster number */
	#;popw	%dx		/* this is our boot_drive/boot_partition */
	#;1:

	#; The partition number for no-emulation-mode bootable CDROM will be
	#; set to 0xFF later(in common.c). So comment out.
	#;cli
	#;movw	%cs, %ax
	#;cmpw	$0x1000, %ax
	#;jne	1f
	#;
	#;/* CS=0x1000, may be booted from ext2 or no-emulation-mode CDROM */
	#;
	#;cmpw	$0x1000, %di
	#;jne	2f
	#;cmpw	$0x7c00, %bp
	#;jne	2f
	#;movw	%es, %ax
	#;cmpw	$0x1000, %ax
	#;jbe	2f
	#;cmpw	$0x7c00, %si
	#;jbe	2f
	#;movl	%edx, %eax
	#;shrl	$16, %eax
	#;jnz	2f
	#;jecxz	1f		// booted from ext2 partition
	#;2:
	#;// booted from no-emulation-mode bootable CDROM
	#;movb	$0xff, %dh	// partition 0xff means whole drive(for CDROM)
	#;			#; if needed, 0xfe can be used as an indicator
	#;			#; here for the bootable CDROM and changed to
	#;			#; 0xff later.
	#;1:
	#;
	#;//if not booted from CDROM, don't touch the boot partition number(dh)

	cli
	xorw	%ax, %ax
	movw	%ax, %ss
	movw	$0x0400, %sp	/* tmp use real-mode IDT as stack */
	movw	%cs, %bp	/* save CS to BP */
	call	1f
1:
	popw	%bx		/* BX=Instruction Pointer of 1b */
	subw	$(1b - _start1), %bx
	movw	%bx, %cx
	shrw	$4, %bx
	addw	%bp, %bx
	pushw	%bx		/* new CS */
	andw	$0x000f, %cx
	addw	$(1f - _start1), %cx
	pushw	%cx		/* new IP */
	lret
1:
	movw	%ds, %cx	/* CX==BP==0x7C0 for pxe enabled */
	pushw	%cs
	popw	%ds

	/* CS=DS=BX, CS:0000 = _start1 */

	addw	$((pre_stage2_start - _start1) >> 4), %bx

	/* BX:0000 = pre_stage2_start */

	cmpw	$0x7C0, %bp
	jne	1f
	cmpw	%bp, %cx
	je	2f
1:
	/* disable pxe */
	orb	$0x01, (pre_stage2_start - _start1 + 5)
2:
	cmpw	$0x820, %bx
	jb	2f

	movw	$((0x8200 - (pre_stage2_start - _start1) - 0x400) >> 4), %cx

	/* Now CS(=DS) >= CX+0x40 */

	movw	%cx, %es
	xorw	%di, %di
	xorw	%si, %si

	/////////////////////////////////////////////////////////////
	//
	//                    CS
	//                    DS          0x820     BX
	//                    _start1---------------pre_stage2_start
	//          CX+0x40---------------0x820
	//   CX
	//   ES
	//
	/////////////////////////////////////////////////////////////

	movw	$0x200, %cx	/* move 2 sectors */
	cld
	repz movsw

	pushw	%es		/* ES:0000 = _start */
	pushw	$(1f - _start)
	lret			/* CS=ES, CS:0000 = _start1 */
1:

	/* move BX:0000 to 0820:0000 upward since BX >= 0x820 */

	cld

	movw	%bx, %ds
	movw	$0x820, %bx
	movw	%bx, %es

	xorw	%si, %si
	xorw	%di, %di

	movw	$6, %bx		/* 64K pages: 0x20000 - 0x7ffff */
1:
	movw	$0x8000, %cx
	repz movsw
	movw	%ds, %ax
	addw	$0x1000, %ax
	movw	%ax, %ds
	movw	%es, %ax
	addw	$0x1000, %ax
	movw	%ax, %es
	decw	%bx
	jnz	1b

	jmp	3f
2:

	/* move BX:0000 to 0820:0000 downward since BX < 0x820 */

	std

	addw	$0x7000, %bx
	movw	%bx, %ds
	movw	$0x7820, %bx
	movw	%bx, %es

	movw	$0xfffe, %si
	movw	%si, %di

	movw	$8, %bx		/* 64K pages: 0x08200 - 0x881ff */
1:
	movw	$0x8000, %cx
	repz movsw
	movw	%ds, %ax
	subw	$0x1000, %ax
	movw	%ax, %ds
	movw	%es, %ax
	subw	$0x1000, %ax
	movw	%ax, %es
	decw	%bx
	jnz	1b

	cld

3:

	/* put the config file name */
	xorw	%ax, %ax
	movw	%ax, %es
	movw	%ax, %ds

	xorl	%ebp, %ebp
	
	movb	%dh, 0x820A	/* this is the boot partition number */

	#; clear saved_entryno so that force_cdrom_as_boot_device be cleared
	#; later in common.c

	movl	%ebp, 0x820C	/* EBP=0, clear saved_entryno */

	movw    $0x0010, %cx	/* set max length of grub version string */
	movw    $0x8212, %di	/* version string */
	cld
	/* AL is already 0. Locate the end of version string */
	repnz scasb	/* find the location of the default config file name */

	jcxz	1f	/* failed, will not use the default config file name */

	movw    $0x4e, %cx	/* max length of config file name */

	movw	%cs, %si	/* CS:0000 = _start1 */
	shlw	$4, %si		/* 0000:SI = _start1 */

	addw	$(default_config_file - _start1), %si

	//movw	$(default_config_file + 0x8200 - pre_stage2_start), %si
	cld
	repz movsb	/* move file name to the config-file field of stage2 */
1:

	movw	$0x0003, %ax	/* set display mode: 80*25 color text */
	int	$0x10

	//xorw	%bx, %bx
	//movw	$(launch_pre_stage2 - _start1), %si
	//call	print_message	/* CS:SI points to message string */

	//xorw	%ax, %ax
	//movw	%ax, %ss
	//movw	$0x2000, %sp

	//sti

	ljmp	$0, $0x8200

//launch_pre_stage2:
//	.ascii	"\r\nBooting GRLDR...\r\n"

//	.byte	0		/* mark the end of ascii zero string */

default_config_file:
	.ascii	"/menu.lst"

	.byte	0		/* mark the end of ascii zero string */
#endif	/* ! defined(GRLDR_MBR) && (! defined(GRLDR_INSTALL)) */

	. = _start1 + 0x400

#define ALTERNATIVE_KERNEL


/*
 * The following is based on FreeDOS, modified heavily by Tinybit in Feb, 2004
 *
 * Merges LBA and CHS boot sectors to ONE FAT32 boot sector!
 *
 * Memory layout for GRLDR FAT32 single stage boot process:
 *
 *	...
 *	|-------| 1FE0:7E00
 *	|BOOTSEC| (GRUB does not use this relocation area)
 *	|RELOC.	| (overwritten by kernel loaded)
 *	|-------| 1FE0:7C00
 *	...
 *	|-------|
 *	|KERNEL	| (overwrites bootsec reloc.)
 *	|LOADED	| (holds 1 sector directory buffer before kernel load)
 *	|-------| 2000:0000
 *	...
 *	|-------| 0000:7E00
 *	|BOOTSEC| GRUB always run inside this sector,
 *	|ORIGIN | no relocation.
 *	|-------| 0000:7C00
 *	...
 *	|-------| 0060:0200
 *	|  FAT  | (only 1 sector buffered)
 *	|-------| 0060:0000
 *	...
 *
 */

/*
; This is an LBA-enabled FreeDOS FAT32 boot sector (single sector!).
; You can use and copy source code and binaries under the terms of the
; GNU Public License (GPL), version 2 or newer. See www.gnu.org for more.

; Based on earlier work by FreeDOS kernel hackers, modified heavily by
; Eric Auer and Jon Gentle in 7 / 2003.
;
; Features: Uses LBA and calculates all variables from BPB/EBPB data,
; thus making partition move / resize / image-restore easier. FreeDOS
; can boot from FAT32 partitions which start > 8 GB boundary with this
; boot sector. Disk geometry knowledge is not needed for booting.
;
; Windows uses 2-3 sectors for booting (sector stage, statistics sector,
; filesystem stage). Only using 1 sector for FreeDOS makes multi-booting
; of FreeDOS and Windows on the same filesystem easier.
;
; Requirements: LBA BIOS and 386 or better CPU. Use the older CHS-only
; boot sector if you want FAT32 on really old PCs (problems: you cannot
; boot from > 8 GB boundary, cannot move / resize / ... without applying
; SYS again if you use the CHS-only FAT32 boot sector).
;
; FAT12 / FAT16 hints: Use the older CHS-only boot sector unless you
; have to boot from > 8 GB. The LBA-and-CHS FAT12 / FAT16 boot sector
; needs applying SYS again after move / resize / ... a variant of that
; boot sector without CHS support but with better move / resize / ...
; support would be good for use on LBA harddisks.


; Memory layout for the FreeDOS FAT32 single stage boot process:

;	...
;	|-------| 1FE0:7E00
;	|BOOTSEC|
;	|RELOC.	|
;	|-------| 1FE0:7C00
;	...
;	|-------| 2000:0200
;	|  FAT  | (only 1 sector buffered)
;	|-------| 2000:0000
;	...
;	|-------| 0000:7E00
;	|BOOTSEC| overwritten by the kernel, so the
;	|ORIGIN | bootsector relocates itself up...
;	|-------| 0000:7C00
;	...
;	|-------|
;	|KERNEL	| maximum size 134k (overwrites bootsec origin)
;	|LOADED	| (holds 1 sector directory buffer before kernel load)
;	|-------| 0060:0000
;	...
*/

#define BOOTGRUB	/* undef this if compiled for loading FreeDOS */
//#undef BOOTGRUB

#ifdef	BOOTGRUB
#define LOADSEG		0x2000
#define FATSEG		0x0060
#else
#define LOADSEG		0x0060
#define FATSEG		0x2000
#endif

Entry_32:
	jmp	1f

	. = Entry_32 + 0x02

	/* The default mode is CHS. This is for maximum compatiblity with
	 * small-sized disks, e.g., floppies.
	 *
	 * Valid values are 0x90 for CHS mode, or 0x0e for LBA mode.
	 *
	 * If the BIOS int13 supports LBA, this byte can be safely set to 0x0e.
	 *
	 * Some USB BIOSes might have bugs when using CHS mode, so the format
	 * program should set this byte to 0x0e. It seems that (generally) all
	 * USB BIOSes have LBA support.
	 *
	 * If the format program does not know whether the BIOS has LBA
	 * support, it may operate this way:
	 *
	 * if (partition_start + total_sectors_in_partition) exceeds the CHS
	 * addressing ability(especially when it is greater than 1024*256*63),
	 * the caller should set this byte to 0x0e, otherwise, set to 0x90.
	 */

	.byte	0x90	/* for CHS. Another possible value is 0x0e for LBA */


	. = Entry_32 + 0x03

#ifdef	BOOTGRUB
	.ascii	"GRLDR   "	/* OEM name string (of OS which formatted the disk). */
#endif

	. = Entry_32 + 0x0b

	.word	0x200		/* bytes per sector. Must be 512 */

	. = Entry_32 + 0x0d

	/* Sectors per cluster. Valid values are 1, 2, 4, 8, 16, 32, 64 and 128.
	 * But a cluster size larger than 32K should not occur.
	 */

	.byte	1		/* sectors per cluster */

	. = Entry_32 + 0x0e

	/* Reserved sectors(number of sectors before the first FAT,
	 * including the boot sector), usually 1.
	 */

	.word	1		/* reserved sectors */

	. = Entry_32 + 0x10

	/* Number of FATs(nearly always 2). */

	.byte	2		/* number of FATs */

	. = Entry_32 + 0x11

	/* (Maximum number of root directory entries)Must be 0. */

	.word	0		/* Max dir entries for FAT12/FAT16 */

	. = Entry_32 + 0x13

	/* (Total number of sectors for small disks only)Must be 0. */

	.word	0		/* total sectors for FAT12/FAT16 */

	. = Entry_32 + 0x15

	/* Media descriptor byte, pretty meaningless now. */

	.byte	0xf8		/* media descriptor */

	. = Entry_32 + 0x16

	/* (Sectors per FAT)Must be 0. */

	.word	0		/* sectors per FAT for FAT12/FAT16 */

	. = Entry_32 + 0x18

	.word	18		/* sectors per track */

	. = Entry_32 + 0x1a

	.word	2		/* number of heads */

	. = Entry_32 + 0x1c

	/* Number of hidden sectors (those preceding the boot sector).
	 * Also referred to as the starting sector of the partition.
	 * For floppies, it should be 0.
	 */

	.long	0		/* hidden sectors */

	. = Entry_32 + 0x20

	/* Total number of sectors in the filesystem. */

	.long	0		/* total sectors for FAT32 */

	. = Entry_32 + 0x24

	/* FAT32 sectors per FAT. */

	.long	0

	. = Entry_32 + 0x28

	/* If bit 7 is clear then all FATs are updated, otherwise bits 0-3
	 * give the current active FAT, all other bits are reserved.
	 * This word is not used by grldr boot code.
	 */

	.word	0

	. = Entry_32 + 0x2a

	/* High byte is major revision number, low byte is minor revision
	 * number, currently both are 0.
	 * This word is not used by grldr boot code.
	 */

	.word	0

	. = Entry_32 + 0x2c

	/* Root directory starting cluster. */

	.long	0

	. = Entry_32 + 0x30

	/* File system information sector number.
	 * This word is not used by grldr boot code.
	 */

	.word	0

	. = Entry_32 + 0x32

	/* If non-zero this gives the sector which holds a copy of the
	 * boot record, usually 6.
	 * This word is not used by grldr boot code.
	 */

	.word	6

	. = Entry_32 + 0x34

	/* Reserved, 12 bytes, set to 0. */

	.long	0
	.long	0
	.long	0

	. = Entry_32 + 0x40

	/* drive number of the boot device.
	 * This byte is ignored for read. The program will write DL onto
	 * this byte. The caller should set drive number in DL. 
	 * We assume all BIOSes pass correct drive number in DL.
	 * That is to say, buggy BIOSes are not supported!!
	 */

	.byte	0

	. = Entry_32 + 0x41

	/* partition number of this filesystem in the boot drive.
	 * This byte is ignored for read. The boot code will write partition
	 * number onto this byte. See Entry + 0x5d below. 
	 */

	.byte	0

	. = Entry_32 + 0x42

	/* Signature (must be 28h or 29h to be recognised by NT). */

	.byte	0x29		/* extended boot signature for FAT12/FAT16 */

	. = Entry_32 + 0x43

	.long	0x0AC4AF63	/* volume serial number */

	. = Entry_32 + 0x47

	.ascii	"NO NAME    "	/* volume label, 11 bytes. */

	. = Entry_32 + 0x52

	.ascii	"FAT32   "	/* filesystem ID, 8 bytes. */

/*
;	bp is initialized to 7c00h
; %define bsOemName	bp+0x03	; OEM label (8)
%define bsBytesPerSec	bp+0x0b ; bytes/sector (dw)
%define bsSecPerClust	bp+0x0d	; sectors/allocation unit (db)
%define bsResSectors	bp+0x0e	; # reserved sectors (dw)
%define bsFATs		bp+0x10	; # of fats (db)
; %define bsRootDirEnts	bp+0x11	; # of root dir entries (dw, 0 for FAT32)
			; (FAT32 has root dir in a cluster chain)
; %define bsSectors	bp+0x13	; # sectors total in image (dw, 0 for FAT32)
			; (if 0 use nSectorHuge even if FAT16)
; %define bsMedia	bp+0x15	; media descriptor: fd=2side9sec, etc... (db)
; %define sectPerFat	bp+0x16	; # sectors in a fat (dw, 0 for FAT32)
			; (FAT32 always uses xsectPerFat)
%define sectPerTrack	bp+0x18	; # sectors/track
; %define nHeads	bp+0x1a	; # heads (dw)
%define nHidden		bp+0x1c	; # hidden sectors (dd)
; %define nSectorHuge	bp+0x20	; # sectors if > 65536 (dd)
%define xsectPerFat	bp+0x24	; Sectors/Fat (dd)
			; +0x28 dw flags (for fat mirroring)
			; +0x2a dw filesystem version (usually 0)
%define xrootClst	bp+0x2c	; Starting cluster of root directory (dd)
			; +0x30 dw -1 or sector number of fs.-info sector
			; +0x32 dw -1 or sector number of boot sector backup
			; (+0x34 .. +0x3f reserved)
%define drive		bp+0x40	; Drive number
			bp+0x41	; partition number for GRLDR

%define fat_sector	bp+0x44		; last accessed FAT sector (dd)
					; (overwriting unused bytes)
%define fat_start	bp+0x48		; first FAT sector (dd)
					; (overwriting unused bytes)
%define data_start	bp+0x4c		; first data sector (dd)
					; (overwriting unused bytes)

*/
		/* not used: [0x42] = byte 0x29 (ext boot param flag)
		 * [0x43] = dword serial
		 * [0x47] = label (padded with 00, 11 bytes)
		 * [0x52] = "FAT32",32,32,32 (not used by Windows)
		 * ([0x5a] is where FreeDOS parts start)
		 */

	. = Entry_32 + 0x5a
1:
	cli
	cld

#ifdef	BOOTGRUB

	. = Entry_32 + 0x5c

	/* the byte at offset 0x5d stores the real partition number for read.
	 * the format program or the caller should set it to a correct value.
	 * For floppies, it should be 0xff, which stands for whole drive.
	 */

	movb	$0xff, %dh	/* boot partition number */

	cmpb	$0xff, %dh	/* is floppy? */
	jne	1f
	movb	$0, %dl		/* yes, let drive number = 0 */
1:
#endif

	xorw	%ax, %ax
	movw	$0x7c00, %bp

#ifndef	BOOTGRUB
	movw	%ax, %ds
	movw	$0x1fe0, %ax
	movw	%ax, %es
	movw	%bp, %si	/* move from 0000:7c00 */
	movw	%bp, %di	/* move to 1fe0:7c00 */
	movw	$0x0100, %cx	/* one sector to move */
	repz movsw
	ljmp	$0x1fe0, $(1f - Entry_32 + 0x7c00)
1:
#endif
	movw	%ax, %ss	/* stack and BP-relative moves up, too */
	leaw	-0x20(%bp), %sp
	sti
	movw	%dx, 0x40(%bp)	/* BIOS passes drive number in DL */

	pushw	%ss
	movb	$0x41, %ah
	movw	$0x55AA, %bx
	int	$0x13
	popw	%ds
	jc	1f		/* No EBIOS */
	cmpw	$0xAA55, %bx
	jne	1f		/* No EBIOS */
	testb	$1, %cl
	jz	1f		/* No EBIOS */
	/* EBIOS supported */
	movb	$0x42, (ebios_32 - 1 - Entry_32 + 0x7c00)
1:
	pushw	%ss
	popw	%es

/* figure out where FAT and DATA area starts
 * (modifies EAX EDX, sets fat_start and data_start variables)
 */
	xorl	%eax, %eax
	movl	%eax, 0x44(%bp)	/* init buffer status */

	/* first, find fat_start */
	movw	0x0e(%bp), %ax	/* reserved sectors */
	addl	0x1c(%bp), %eax	/* hidden sectors */
	movl	%eax, 0x48(%bp)	/* first FAT sector */
	movl	%eax, 0x4c(%bp)	/* first data sector, initial value */

	/* next, find data_start */
	movl	0x10(%bp), %eax	/* number of fats, no movzbl needed: the */
				/* 2 words at 0x11(%bp) are 0 for fat32. */
	mull	0x24(%bp)	/* sectors per fat (EDX=0) */
	addl	%eax, 0x4c(%bp)	/* first DATA sector */

/* Searches for the file in the root directory.
 * Returns:	EAX = first cluster of file
 */

	movl	0x2c(%bp), %eax	/* root dir cluster */

1:
	pushl	%eax		/* save cluster */
	call	cluster_to_lba_32
		/* EDX is sectors per cluster, EAX is sector number */
	movw	$(msg_BootError_32 - Entry_32 + 0x7c00), %si
	jc	boot_error_32	/* EOC encountered */

2:
	lesw	(loadseg_off_32 - Entry_32)(%bp), %bx	/* load to loadseg:0 */
	call	readDisk_32

	xorw	%di, %di

	/* Search for kernel file name, and find start cluster */
3:
	movw	$11, %cx
	movw	$(filename_32 - Entry_32 + 0x7c00), %si
	repz cmpsb
	jz	1f	/* note that di now is at dirent+11 */

	addw	$0x20, %di
	andw	$-0x20, %di	/* 0xffe0 */
	cmp	0x0b(%bp), %di	/* bytes per sector */
	jnz	3b		/* next directory entry */

	decw	%dx	/* initially DX holds sectors per cluster */
	jnz	2b	/* loop over sectors in cluster */

	popl	%eax		/* restore current cluster */
	call	next_cluster_32
	jmp	1b		/* read next cluster */

#ifndef ALTERNATIVE_KERNEL
loadseg_off_32:
	.word	0
	.word	LOADSEG
#endif

1:
	/* kernel directory entry is found */
	pushw	%es:(0x14-11)(%di)	/* get cluster number HI */
	pushw	%es:(0x1a-11)(%di)	/* get cluster number LO */
	popl	%eax			/* convert to 32bit */

	xorw	%bx, %bx	/* read kernel at ES:BX=LOADSEG:0 */

/* read kernel */

2:
	pushl	%eax
	call	cluster_to_lba_32
		/* EDX is sectors per cluster, EAX is sector number */
	jnc	1f
	
	/* EOC encountered - done */
#ifdef	BOOTGRUB
	movw	0x40(%bp), %dx	/* boot_drive and boot_partition */
#else
	movb	0x40(%bp), %bl	/* FreeDOS kernel uses BL, not DL, for drive */
#endif
	ljmp	*(loadseg_off_32 - Entry_32)(%bp)

1:
	call	readDisk_32
	decw	%dx	/* initially DX holds sectors per cluster */
	jnz	1b	/* loop over sectors in cluster */

	popl	%eax
	call	next_cluster_32
	jmp	2b
		
/* given a cluster number, find the number of the next cluster in
 * the FAT chain. Needs fat_start.
 * input:	EAX - cluster
 *		EDX = 0
 * output:	EAX - next cluster
 *		EDX = undefined
 */

next_cluster_32:
	pushw	%es
	/* pushw	%di */
	pushw	%bx		/* hi word of EBX never used */

#if 1
	/* xorl	%edx, %edx */
	shll	$2, %eax	/* 32bit FAT */
	movzwl	0x0b(%bp), %ebx	/* bytes per sector */
	divl	%ebx		/* residue is in EDX */
	/* movw	%dx, %di */
#else	
	shll	$2, %eax	/* 32bit FAT */
	;xchgw	%ax, %di	/* movw	%ax, %di */
	movw	%ax, %di
	;shlw	$2, %di		/* 32bit FAT */

	pushw	%cx
	movw	0x0b(%bp), %bx	/* bytes per sector */
	bsfw	%bx, %cx
	;decw	%cx
	;decw	%cx
	decw	%bx
	andw	%bx, %di	/* mask to sector size */
	shrl	%cl, %eax
	popw	%cx
#endif	
	addl	0x48(%bp), %eax	/* add the first FAT sector number.  */ 
				/* EAX=absolute sector number */
	movw	$FATSEG, %bx
	movw	%bx, %es
	xorw	%bx, %bx

	/* is it the last accessed and already buffered FAT sector? */
	cmpl	0x44(%bp), %eax
	jz	1f
	movl	%eax, 0x44(%bp)	/* mark sector EAX as buffered */
	call	readDisk_32	/* read sector EAX to buffer */
1:
#if 1
	//.byte	0x67, 0x26, 0x80, 0x62, 0x03, 0x0f
	addr32 andb	$0x0f, %es:3(%edx)	/* mask out top 4 bits */

	//.byte	0x67, 0x66, 0x26, 0x8b, 0x02
	addr32 movl	%es:(%edx), %eax	/* read next cluster number */
#else
	andb	$0x0f, %es:3(%di)	/* mask out top 4 bits */
	movl	%es:(%di), %eax	/* read next cluster number */
#endif
	popw	%bx
	/* popw	%di */
	popw	%es
	ret

/* Convert cluster number to the absolute sector number
 * ... or return carry if EndOfChain! Needs data_start.
 * input:	EAX - target cluster
 * output:	EAX - absolute sector
 *		EDX - [bsSectPerClust] (byte)
 *		carry clear
 *		(if carry set, EAX/EDX unchanged, end of chain)
 */

cluster_to_lba_32:
	cmpl	$0x0ffffff8, %eax	/* check End Of Chain */
	cmc
	jb	1f			/* carry is stored if EOC */

	/* sector = (cluster-2) * clustersize + data_start */
	decl	%eax
	decl	%eax

	movzbl	0x0d(%bp), %edx		/* sectors per cluster */
	pushw	%dx			/* only DX would change */
	mull	%edx			/* EDX = 0 */
	popw	%dx
	addl	0x4c(%bp), %eax		/* data_start */
	/* here, carry is cleared (unless parameters are wrong) */
1:
	ret

/* Read a sector from disk, using LBA or CHS
 * input:	EAX - 32-bit DOS sector number
 *		ES:BX - destination buffer
 *		(will be filled with 1 sector of data)
 * output:	ES:BX points one byte after the last byte read.
 *		EAX - next sector
 */
 
readDisk_32:
	pushal
	xorl	%edx, %edx	/* EDX:EAX = LBA */
	pushl	%edx		/* hi 32bit of sector number */
	pushl	%eax		/* lo 32bit of sector number */
	pushw	%es		/* buffer segment */
	pushw	%bx		/* buffer offset */
	pushw	$1		/* 1 sector to read */
	pushw	$16		/* size of this parameter block */

	xorl	%ecx, %ecx
	pushl	0x18(%bp)	/* lo:sectors per track, hi:number of heads */
	popw	%cx		/* ECX = sectors per track */
	divl	%ecx		/* residue is in EDX */
				/* quotient is in EAX */
	incw	%dx		/* sector number in DL */
	popw	%cx		/* ECX = number of heads */
	pushw	%dx		/* push sector number into stack */
	xorw	%dx, %dx	/* EDX:EAX = cylinder * TotalHeads + head */
	divl	%ecx		/* residue is in EDX, head number */
				/* quotient is in EAX, cylinder number */
	xchgb	%dl, %dh	/* head number should be in DH */
				/* DL = 0 */
	popw	%cx		/* pop sector number from stack */
	xchgb	%al, %ch	/* lo 8bit cylinder should be in CH */
				/* AL = 0 */
	shlb	$6, %ah		/* hi 2bit cylinder ... */
	orb	%ah, %cl	/* ... should be in CL */
	
	movw	$0x201, %ax	/* read 1 sector */
ebios_32: /* ebios_32 - 1 points to 0x02 that can be changed to 0x42 */

	movw	%sp, %si	/* DS:SI points to disk address packet */
	movb	0x40(%bp), %dl	/* hard disk drive number */
	pushw	%es
	pushw	%ds
	int	$0x13
	popw	%ds
	popw	%es
	popaw			/* remove parameter block from stack */
	popal
	jc	disk_error_32	/* disk read error, jc 1f if caller handles */
	incl 	%eax		/* next sector */
	addw	0x0b(%bp), %bx	/* bytes per sector */
	jnc	1f		/* 64K bound check */
	pushw	%dx
	movw	%es, %dx
	addb	$0x10, %dh	/* add 1000h to ES */
				/* here, carry is cleared */
	movw	%dx, %es
	popw	%dx
1:
	/* carry stored on disk read error */
	ret

//	. = . - (. - readDisk_32)/91

msg_DiskReadError_32:

	.ascii	"disk error\0"

msg_BootError_32:

	.ascii	"No "

filename_32:

#ifdef	BOOTGRUB
	.ascii	"GRLDR      \0"
#else
	.ascii	"KERNEL  SYS\0"
#endif

#ifdef ALTERNATIVE_KERNEL
filename_end_32:

	. = Entry_32 + 0x1e8

loadseg_off_32:
	.word	0
	.word	LOADSEG

	. = Entry_32 + 0x1ec

boot_image_ofs_32:

	.word (filename_32 - Entry_32)+(filename_end_32 - filename_32 - 1)*2048
#endif

	. = Entry_32 + 0x1ee

disk_error_32:

	movw	$(msg_DiskReadError_32 - Entry_32 + 0x7c00), %si

boot_error_32:

/* prints string DS:SI (modifies AX BX SI) */

//print_32:
1:
	lodsb	(%si), %al	/* get token */
	//xorw	%bx, %bx	/* video page 0 */
	movb	$0x0e, %ah	/* print it */
	int	$0x10		/* via TTY mode */
	cmpb	$0, %al		/* end of string? */
	jne	1b		/* until done */

	/* The caller will change this to
	 *	ljmp	$0x9400, $(try_next_partition - _start1)
	 */

1:	jmp	1b

	. = Entry_32 + 0x1fc

	.word	0, 0xAA55 /* Win9x uses all 4 bytes as magic value here */

	. = Entry_32 + 0x200

	. = _start1 + 0x600

	//.arch	i8086, nojumps
	.arch	i186, nojumps
/*
 * The following is based on FreeDOS, modified heavily by Tinybit in Feb, 2004
 *
 * Merges FAT12 and FAT16 boot sectors to ONE FAT boot sector!
 *
 * Memory layout for GRLDR FAT single stage boot process:
 *
 *	+--------+
 *	|        |
 *	|GRLDR   | also used as max 128k FAT buffer
 *	|LOADED  | before GRLDR loading starts
 *	|--------| 2000:0000
 *	|        |
 *	|--------| 0000:7E00
 *	|BOOTSECT|
 *	|ORIGIN  |
 *	|--------| 0000:7C00
 *	|        |
 *	|--------| 0000:3000
 *	|CLUSTER |
 *	|LIST    |
 *	|--------| 0000:2000
 *	|        |
 *	+--------+
 */

/*
;
; File:
;                            boot.asm
; Description:
;                           DOS-C boot
;
;                       Copyright (c) 1997;
;                           Svante Frey
;                       All Rights Reserved
;
; This file is part of DOS-C.
;
; DOS-C is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License
; as published by the Free Software Foundation; either version
; 2, or (at your option) any later version.
;
; DOS-C is distributed in the hope that it will be useful, but
; WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
; the GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public
; License along with DOS-C; see the file COPYING.  If not,
; write to the Free Software Foundation, 675 Mass Ave,
; Cambridge, MA 02139, USA.
;
;
;	+--------+ 1FE0:7E00
;	|BOOT SEC|
;	|RELOCATE|
;	|--------| 1FE0:7C00
;	|        |
;	|--------| 1FE0:3000
;	| CLUSTER|
;	|  LIST  |
;	|--------| 1FE0:2000
;	|        |
;	|--------| 0000:7E00
;	|BOOT SEC| overwritten by max 128k FAT buffer
;	|ORIGIN  | and later by max 134k loaded kernel
;	|--------| 0000:7C00
;	|        |
;	|--------|
;	|KERNEL  | also used as max 128k FAT buffer
;	|LOADED  | before kernel loading starts
;	|--------| 0060:0000
;	|        |
;	+--------+
*/

#ifdef	BOOTGRUB
#define LOADSEG_12_16   0x2000
#define FATBUF          0x2000        /* offset of temp buffer for FAT chain */
#else
#define LOADSEG_12_16   0x0060
#define FATBUF          0x2000        /* offset of temp buffer for FAT chain */
#endif

Entry_12_16:
	jmp     1f

	. = Entry_12_16 + 0x02

	/* The default mode is CHS. This is for maximum compatiblity with
	 * small-sized disks, e.g., floppies.
	 *
	 * Valid values are 0x90 for CHS mode, or 0x0e for LBA mode.
	 *
	 * If the BIOS int13 supports LBA, this byte can be safely set to 0x0e.
	 *
	 * Some USB BIOSes might have bugs when using CHS mode, so the format
	 * program should set this byte to 0x0e. It seems that (generally) all
	 * USB BIOSes have LBA support.
	 *
	 * If the format program does not know whether the BIOS has LBA
	 * support, it may operate this way:
	 *
	 * if (partition_start + total_sectors_in_partition) exceeds the CHS
	 * addressing ability(especially when it is greater than 1024*256*63),
	 * the caller should set this byte to 0x0e, otherwise, set to 0x90.
	 */

	.byte	0x90	/* for CHS. Another possible value is 0x0e for LBA */


	. = Entry_12_16 + 0x03

#ifdef	BOOTGRUB
	.ascii	"GRLDR   "
#endif

	. = Entry_12_16 + 0x0b

	.word	0x200		/* bytes per sector */

	. = Entry_12_16 + 0x0d

	.byte	1		/* sectors per cluster */

	. = Entry_12_16 + 0x0e

	.word	1		/* reserved sectors */

	. = Entry_12_16 + 0x10

	.byte	2		/* number of FATs */

	. = Entry_12_16 + 0x11

	.word	224		/* Max dir entries */

	. = Entry_12_16 + 0x13

	.word	2880		/* total sectors in the filesystem */

	. = Entry_12_16 + 0x15

	.byte	0xf0		/* media descriptor */

	. = Entry_12_16 + 0x16

	.word	9		/* sectors per FAT */

	. = Entry_12_16 + 0x18

	.word	18		/* sectors per track */

	. = Entry_12_16 + 0x1a

	.word	2		/* number of heads */

	. = Entry_12_16 + 0x1c

	.long	0		/* hidden sectors */

	. = Entry_12_16 + 0x20

	.long	0		/* total sectors for large partitions */

	. = Entry_12_16 + 0x24

	/* drive number of the boot device.
	 * This byte is ignored for read. The program will write DL onto
	 * this byte. The caller should set drive number in DL. 
	 * We assume all BIOSes pass correct drive number in DL.
	 * That is to say, buggy BIOSes are not supported!!
	 */

	.byte	0

	. = Entry_12_16 + 0x25

	/* partition number of this filesystem in the boot drive.
	 * This byte is ignored for read. The boot code will write partition
	 * number onto this byte. See Entry_12_16 + 0x41 below. 
	 */

	.byte	0

	. = Entry_12_16 + 0x26

	.byte	0x29		/* extended boot signature */

	. = Entry_12_16 + 0x27

	.long	0x0AC4AF63	/* volume serial number */

	. = Entry_12_16 + 0x2b

	.ascii	"NO NAME    "	/* volume label */

	. = Entry_12_16 + 0x36

	.ascii	"FAT12   "	/* filesystem ID */

/*
;       bp is initialized to 7c00h
%define bsOemName       bp+0x03      ; OEM label
%define bsBytesPerSec   bp+0x0b      ; bytes/sector
%define bsSecPerClust   bp+0x0d      ; sectors/allocation unit
%define bsResSectors    bp+0x0e      ; # reserved sectors
%define bsFATs          bp+0x10      ; # of fats
%define bsRootDirEnts   bp+0x11      ; # of root dir entries
%define bsSectors       bp+0x13      ; # sectors total in image
%define bsMedia         bp+0x15      ; media descrip: fd=2side9sec, etc...
%define sectPerFat      bp+0x16      ; # sectors in a fat
%define sectPerTrack    bp+0x18      ; # sectors/track
%define nHeads          bp+0x1a      ; # heads
%define nHidden         bp+0x1c      ; # hidden sectors
%define nSectorHuge     bp+0x20      ; # sectors if > 65536
%define drive           bp+0x24      ; drive number
			bp+0x25      ; partition number for GRLDR
%define extBoot         bp+0x26      ; extended boot signature
%define volid           bp+0x27
%define vollabel        bp+0x2b
%define filesys         bp+0x36

%define RootDirSecs     bp+0x26         ; # of sectors root dir uses
					; (overwriting unused bytes)
%define fat_start       bp+0x28         ; first FAT sector
					; (overwriting unused bytes)
%define root_dir_start  bp+0x2c         ; first root directory sector
					; (overwriting unused bytes)
%define data_start      bp+0x30         ; first data sector
					; (overwriting unused bytes)
%define data_clusters   bp+0x34         ; # of clusters in data area
					; (overwriting unused bytes)
			bp+0x36		; bytes per FAT( > 0x1800 means FAT16)
					; (overwriting unused bytes)
*/
		/* not used: [0x26] = byte 0x29 (ext boot param flag)
		 * [0x27] = dword serial
		 * [0x2b] = label (padded with 00, 11 bytes)
		 * [0x36] = "FAT12" or "FAT16",32,32,32 (not used by Windows)
		 * ([0x3e] is where FreeDOS parts start)
		 */

	. = Entry_12_16 + 0x3e
1:
	cli
	cld

#ifdef	BOOTGRUB

	. = Entry_12_16 + 0x40

	/* the byte at offset 0x41 stores the real partition number for read.
	 * the format program or the caller should set it to a correct value.
	 * For floppies, it should be 0xff, which stands for whole drive.
	 */

	movb	$0xff, %dh	/* boot partition number */

	cmpb	$0xff, %dh	/* is floppy? */
	jne	1f
	movb	$0, %dl		/* yes, let drive number = 0 */
1:
#endif

	xorw	%ax, %ax
	movw	$0x7c00, %bp

#ifdef	BOOTGRUB

	movw	%ax, %ss	/* stack and BP-relative moves up, too */
	leaw	-0x20(%bp), %sp

	sti			/* after stack setup, we can use stack */

	movw	%dx, 0x24(%bp)	/* BIOS passes drive number in DL */

	pushaw			/* AX=0 */

	movb	$0x41, %ah
	movw	$0x55AA, %bx
	pushw	%dx
	int	$0x13
	popw	%dx
	pushw	%ss		/* SS=0 */
	popw	%ds		/* DS=0 */

	jc	1f		/* No EBIOS */
	cmpw	$0xAA55, %bx
	jne	1f		/* No EBIOS */
	testb	$1, %cl
	jz	1f		/* No EBIOS */
	testb	%dl, %dl	/* floppy? */
	jns	1f		/* yes, only use CHS for FAT12/16 on floppy. */
	/* EBIOS supported */
	movb	$0x42, (ebios_12_16 - 1 - Entry_12_16 + 0x7c00)
1:
	popaw			/* AX=0 */

	movw	%ax, %es	/* ES=0 */

	/* AX=SS=DS=ES=0, BP=0x7C00 */

#else
	movw	%ax, %ds
	movw	%bp, %si	/* move from 0000:7c00 */
	movw	%bp, %di	/* move to 1fe0:7c00 */
	movb	%dl, 0x24(%si)	/* BIOS passes drive number in DL */
//	xchgw	%ax, %dx	/* let DX = 0 */
	movw	$0x1fe0, %ax
	movw	%ax, %es
	movw	$0x0100, %cx	/* one sector to move */
	repz movsw
				/* CX = 0 */
	ljmp	$0x1fe0, $(1f - Entry_12_16 + 0x7c00)
1:
	movw	%ax, %ss	/* stack and BP-relative moves up, too */
	leaw	-0x20(%bp), %sp

	sti			/* after stack setup, we can use stack */

	pushw	%ax		/* AX=0x1FE0 */
	movb	$0x41, %ah
	movw	$0x55AA, %bx
	int	$0x13
	popw	%ds		/* DS=0x1FE0 */

	jc	1f		/* No EBIOS */
	cmpw	$0xAA55, %bx
	jne	1f		/* No EBIOS */
	testb	$1, %cl
	jz	1f		/* No EBIOS */
	/* EBIOS supported */
	movb	$0x42, (ebios_12_16 - 1 - Entry_12_16 + 0x7c00)
1:
	xorw	%ax, %ax	/* AX=0 */

#endif

	/* GET DRIVE PARMS: Calculate start of some disk areas */
1:
#if 0
	.arch	i486, nojumps

	movzwl	0x0e(%bp), %ebx	/* reserved sectors */
	addl	0x1c(%bp), %ebx	/* hidden sectors */
	movl	%ebx, 0x28(%bp)	/* ---- FAT start */

	movzbl	0x10(%bp), %eax	/* number of FATs */
	movzwl	0x16(%bp), %ecx	/* sectors per FAT */
	mull	%ecx		/* EDX=0, EAX=total sectors for FAT */
	addl	%eax, %ebx
	movl	%ebx, 0x2c(%bp)	/* ---- RootDir start */

	/* Calculate how many sectors the root directory occupies */

	movzwl	0x0b(%bp), %ecx	/* bytes per sector, should be 512 */

	movzwl	0x11(%bp), %eax	/* max number of RootDir entries */
	shll	$5, %eax	/* total bytes RootDir occupies */
	addl	%ecx, %eax
	decl	%eax
	/* xorl	%edx, %edx */	/* EDX=0 */
	divl	%ecx		/* EAX=AX=sectors for RootDir */

	movw	%ax, 0x26(%bp)	/* sectors for RootDir */

	addl	%eax, %ebx
	movl	%ebx, 0x30(%bp)	/* ---- DataArea start */

	. = . - (. - 1b)/66

	.arch	i186, nojumps
#else
	movw	0x1c(%bp), %si	/* number of hidden sectors(lo) */
	movw	0x1e(%bp), %di	/* number of hidden sectors(hi) */
	addw	0x0e(%bp), %si	/* number of reserved sectors */
	adcw	%ax, %di	/* DI:SI = first FAT sector */
				/* AX = 0 */

	movw	%si, 0x28(%bp)	/* FAT start sector(lo) */
	movw	%di, 0x2a(%bp)	/* FAT start sector(hi) */

	//xchgw	%ax, %dx	/* let AX = 0 */
	movb	0x10(%bp), %al	/* number of FATs */
	/* cbw */
	mulw	0x16(%bp)	/* sectors per FAT */
				/* DX:AX = total number of FAT sectors */
				/* DX = 0 since no too many FAT sectors */
	addw	%ax, %si
	adcw	%dx, %di	/* DI:SI = root directory start sector */
	movw	%si, 0x2c(%bp)	/* root directory starting sector(lo) */
	movw	%di, 0x2e(%bp)	/* root directory starting sector(hi) */

	/* Calculate how many sectors the root directory occupies */

	movw	0x0b(%bp), %bx	/* bytes per sector */
	movb	$5, %cl		/* divide BX by 32 */
	shrw	%cl, %bx	/* BX = directory entries per sector */

	movw	0x11(%bp), %ax	/* max number of root dir entries */
	addw	%bx, %ax
	decw	%ax
	/* xorw	%dx, %dx */	/* assuming DX = 0 */
	divw	%bx		/* AX = sectors per root directory */
	cwd			/* DX = 0 */

	movw	%ax, 0x26(%bp)	/* number of sectors the root dir occupies */

	addw	%ax, %si	/* DI:SI = first data sector */
	adcw	%dx, %di	/* assuming DX = 0 */

	movw	%si, 0x30(%bp)	/* data starting sector(lo) */
	movw	%di, 0x32(%bp)	/* data starting sector(hi) */

	. = . - (. - 1b)/63

#endif


#ifdef USE_TOTAL_CLUSTERS
	movw	0x13(%bp), %cx	/* total sectors(small) */
	jcxz	1f
	movw	%cx, 0x20(%bp)	/* total sectors(large)(lo) */
	movw	%dx, 0x22(%bp)	/* total sectors(large)(hi), assuming DX = 0 */
1:	
	movw	0x20(%bp), %ax	/* total sectors(large) */
	movw	0x22(%bp), %bx
	addw	0x1c(%bp), %ax	/* number of hidden sectors */
	adcw	0x1e(%bp), %bx
	subw	%si, %ax	/* data starting sector */
	sbbw	%di, %bx	/* BX:AX = total sectors in the data area */
	movb	0x0d(%bp), %dl	/* sectors per cluster(DH=0) */
	xchgw	%bx, %dx	/* DX:AX = total sectors in the data area */
				/* BX = sectors per cluster */
	divw	%bx		/* AX = total clusters in the data area */
	movw	%ax, 0x34(%bp)	/* total clusters in the data area */
#else
	movw	$0xffff, 0x36(%bp)	/* bytes per FAT( > 0x1800 means FAT16) */
	movw	0x16(%bp), %ax	/* sectors per FAT */
	mulw	0x0b(%bp)	/* bytes per sector */
	jc	1f
	movw	%ax, 0x36(%bp)
1:
#endif
	/* Searches for the file in the root directory
	 *
	 * Returns:
	 *	AX = first cluster of file
	 */

	/* First, read the whole root directory into the temporary buffer */

	movw	0x2c(%bp), %ax	/* root directory starting sector(lo) */
	movw	0x2e(%bp), %dx	/* root directory starting sector(hi) */
	movw	0x26(%bp), %cx	/* number of sectors the root dir occupies */
	lesw	(loadseg_off_12_16 - Entry_12_16)(%bp), %bx
				/* ES:BX = loadseg:0 */

//	pushw	%es
	call	readDisk_12_16	/* CX=0, AX,DX,ES changed */
//	popw	%es

	lesw	(loadseg_off_12_16 - Entry_12_16)(%bp), %di
				/* ES:DI = loadseg:0 */


	/* Search for kernel file name, and find start cluster */

	/* BX=0, CX=0 */

//	pushw	%cx
//	popw	%di

1:
	movw	$(filename_12_16 - Entry_12_16 + 0x7c00), %si	/* filename */

	movb	$11, %cl	/* length of kernel filename */

	pushw	%di
	repz cmpsb
	popw	%di

	jz	1f

	addw	$0x20, %di	/* next entry */
	jz	2f		/* or jc 2f, exceeding 64K */

	/* if the entry begins in 0, this also ends the dir. */

	cmpb	%ch, %es:(%di)	/* CH=0 */
	jnz	1b
2:
	movw	$(msg_BootError_12_16 - Entry_12_16 + 0x7c00), %si
	jmp	boot_error_12_16	/* fail if not found */

#ifndef ALTERNATIVE_KERNEL
loadseg_off_12_16:	.word	0
loadseg_seg_12_16:	.word	LOADSEG_12_16
#endif

1:

/****************************************************************************/

	/* BX=0, CX=0 */

	######################################################################
	# Reads the FAT chain and stores it in a temporary buffer in the
	# first 64KB.  The FAT chain is stored an array of 16-bit cluster
	# numbers, ending with 0.
	#
	# The file must fit in conventional memory, so it can't be larger
	# than 640KB. The sector size must be at least 512 bytes, so the
	# FAT chain can't be larger than around 3KB.
	######################################################################

	/********************************************************************/
	/* First, load the complete FAT into memory. The FAT can't be       */
	/* larger than 128KB, so it should fit in the temporary buffer.     */
	/********************************************************************/

	pushw	%es:0x1a(%di)	/* save first cluster number of file */

//	lesw	(loadseg_off_12_16 - Entry_12_16)(%bp), %bx
//				/* ES:BX = loadseg:0 */
	movw	0x16(%bp), %cx	/* sectors per FAT */
	movw	0x28(%bp), %ax	/* FAT start sector(lo) */
	movw	0x2a(%bp), %dx	/* FAT start sector(hi) */

	pushw	%es		/* ES=0x2000 */
	call	readDisk_12_16	/* CX=0, AX,DX,ES changed */
	popw	%ds		/* DS=0x2000 */

	popw	%ax		/* restore first cluster number of file */

	/********************************************************************/
	/* Then, extract the clusters of the file from the FAT              */
	/********************************************************************/

	/* Set ES:DI to the temporary storage for the FAT chain */

	pushw	%ds		/* ES=0x2000 */

	pushw	%ss
	popw	%es
	movw	$FATBUF, %di

	/* BX=0, CX=0 */
2:
	stosw			/* store cluster number */
	movw	%ax, %si	/* SI = cluster number */

	//////////////////////////////////////////////////////////////
	//
	// FAT16 can occupy 128KB, so the segment must be adjusted
	//
	//////////////////////////////////////////////////////////////

	popw	%dx		/* DX=0x2000, segment for FAT16 */
	pushw	%dx
	addw	%si, %si	/* multiply cluster number by two */
	jnc	1f
	addb	$0x10, %dh	/* overflow. Add 0x1000 to segment value */
1:

#ifdef USE_TOTAL_CLUSTERS
	cmpw	$0x0ff7, 0x34(%bp)	/* total clusters in the data area */
#else
	cmpw	$0x1801, 0x36(%bp)	/* bytes per FAT */
#endif
	jnb	3f

	/******** FAT12 ********/

	addw	%ax, %si	/* multiply cluster number by 3 ... */
	shrw	$1, %si		/* ... and divide by 2 */
	lodsw

	/* If the cluster number was even, the cluster value is now in
	 * bits 0-11 of AX. If the cluster number was odd, the cluster
	 * value is in bits 4-15, and must be shifted right 4 bits. If
	 * the number was odd, CF was set in the last shift instruction.
	 */

	jnc	1f
	movb	$4, %cl		/* CL=4 */
	shrw	%cl, %ax
1:
	andb	$0x0f, %ah	/* mask off the highest 4 bits */
	cmpw	$0x0ff7, %ax	/* check for EOF */
	jmp	4f

3:
	/******** FAT16 ********/

	movw	%dx, %ds	/* DS:SI points to next cluster */
	lodsw			/* AX = next cluster */

	cmpw	$0xfff7, %ax	/* check for EOF */
4:
	jbe	2b		/* continue if not EOF */

	/* Mark end of FAT chain with 0, so we have a single
	 * EOF marker for both FAT12 and FAT16 systems.
	 */

	xorw	%ax, %ax
	stosw

/****************************************************************************/

	/* Load the file into memory, one cluster at a time */

	popw	%es		/* ES=0x2000 */
//	lesw	(loadseg_off_12_16 - Entry_12_16)(%bp), %bx
//				/* ES:BX = loadseg:0 */
	pushw	%ss
	popw	%ds		/* DS=SS */
	movw	$FATBUF, %si	/* set DS:SI to the FAT chain */

1:
	/* CH=0 */

	lodsw			/* AX = next cluster to read */
	subw	$2, %ax		/* cluster numbers start with 2 */
	jc	1f		/* the cluster should be 0 for EOC */

	movb	0x0d(%bp), %cl	/* CH=0, CX=sectors per cluster */
	mulw	%cx
	addw	0x30(%bp), %ax	/* data starting sector(lo) */
	adcw	0x32(%bp), %dx	/* data starting sector(hi) */
				/* DX:AX = first sector to read */

	call	readDisk_12_16	/* CX=0, AX,DX,ES changed */

	jmp	1b		/* read next cluster */

1:
	/* EOC encountered - done */
#ifdef	BOOTGRUB
	movw	0x24(%bp), %dx	/* boot_drive and boot_partition */
#else
	movb	0x24(%bp), %bl	/* FreeDOS kernel uses BL, not DL, for drive */
#endif
	ljmp	*(loadseg_off_12_16 - Entry_12_16)(%bp)	/* boot it! */

/****************************************************************************/

/* Read a number of sectors into memory.
 *
 * Call with:	DS=SS
 *		DX:AX = 32-bit DOS sector number
 *		   CX = number of sectors to read
 * 		ES:BX = destination buffer
 *
 * Returns:	CX=0
 *		ES increased, BX untouched
 *		ES:BX points one byte after the last byte read.
 * 		DX:AX = next sector number after read
 *		All other registers preserved
 */
 
readDisk_12_16:
2:
	pushfw
	pushaw
	xorw	%cx, %cx
	pushw	%cx		/* hi word of hi dword */
	pushw	%cx		/* lo word of hi dword */
	pushw	%dx		/* hi word of lo dword */
	pushw	%ax		/* lo word of lo dword */
	pushw	%es		/* buffer segment */
	pushw	%bx		/* buffer offset */
	incw	%cx
	pushw	%cx		/* 1 sector to read */
	movb	$16, %cl
	pushw	%cx		/* size of this parameter block */

	xchgw	%ax, %cx	/* save AX to CX */

	/*
	 * translate sector number to BIOS parameters
	 *
	 * LBA = sector-1			offset in track
	 *     + head * sectPerTrack		offset in cylinder
	 *     + cyl * sectPerTrack * nHeads	offset in platter
	 *
	 */
#if 1
	pushw	%bx

	pushw	%dx
	movw	0x18(%bp), %ax	/* sectors per track */
	movw	%ax, %bx
	mulw	0x1a(%bp)	/* nHeads */
				/* DX=0, AX=sectors per cylinder */
	xchgw	%ax, %cx	/* restore AX from CX, and save AX to CX */
				/* CX=nHeads * sectPerTrack <= 256*63 */
				/* AX=LBA lo word */
	popw	%dx		/* DX:AX=LBA */

#else

	pushw	%bx
	movw	0x18(%bp), %ax	/* sectors per track */
	movw	%ax, %bx
	mulb	0x1a(%bp)	/* nHeads, but maybe a word value 0x100 */
	jnz	1f
	movb	%bl, %ah	/* nHeads=0x100, so AX=sectPerTrack*0x100 */
1:
	xchgw	%ax, %cx	/* restore AX from CX, and save AX to CX */
		/* DX:AX = LBA, CX = nHeads * sectPerTrack <= 256*63 */
#endif

	divw	%cx	 /* AX = cyl, DX = sector-1 + head * sectPerTrack */
	xchgw	%ax, %dx /* DX = cyl, AX = sector-1 + head * sectPerTrack */
	divb	%bl	/* sectors per track */
			 /* DX = cyl, AL = head, AH = sector-1 */
#if 1
	xchgb	%al, %ah /* DX = cyl, AH = head, AL = sector-1 */
	incw	%ax	 /* DX = cyl, AH = head, AL = sector */
	xchgw	%ax, %dx /* AX = cyl, DH = head, DL = sector */
	xchgw	%ax, %cx /* CX = cyl, DH = head, DL = sector */
	xchgb	%cl, %ch	/* set cyl number low 8 bits in CH */
	rorb	$1, %cl		/* move cyl high bits into bits 7-6 */
	rorb	$1, %cl		/*	(assumes top = 0)             */
	orb	%dl, %cl	/* merge sector into cylinder */
#else
	movw	%dx, %cx /* CX = cyl, AL = head, AH = sector-1 */

	/*
	 * the following manipulations are necessary in order to properly place
	 * parameters into registers.
	 * CH = cylinder number low 8 bits
	 * CL<7-6> = cylinder high two bits
	 * CL<5-0> = sector
	 */
	movb	%al, %dh	/* save head into DH for BIOS */
	xchgb	%cl, %ch	/* set cyl number low 8 bits in CH */
	rorb	$1, %cl		/* move cyl high bits into bits 7-6 */
	rorb	$1, %cl		/*	(assumes top = 0)	*/
	incb	%ah		/* AH = sector number */
	orb	%ah, %cl	/* merge sector into cylinder */
#endif
	popw	%bx
	
	movw	$0x0201, %ax	/* read 1 sector */
ebios_12_16: /* ebios_12_16 - 1 points to 0x02 that can be changed to 0x42 */

	movw	%sp, %si	/* DS:SI points to disk address packet */
	movb	0x24(%bp), %dl	/* drive number */

	pushw	%es
	pushw	%ds
	int	$0x13
	popw	%ds

jc_code_begin:
	jc	disk_error_12_16	/* caller could change it to NOPs */
jc_code_end:

	/* increment ES */
	popw	%bx		/* old ES on stack */
	leaw	0x20(%bx), %bx	/* LEA does not touch flags */
	movw	%bx, %es

	popaw			/* remove parameter block from stack */
	popaw
	popfw			/* restore IF, DF */
	incw 	%ax		/* next sector */
	jnz	1f
	incw	%dx
1:
	loop	2b

	ret

//	. = . - (. - readDisk_12_16)/99

msg_DiskReadError_12_16:

	.ascii	"disk error\0"

msg_BootError_12_16:

	.ascii	"No "

filename_12_16:

#ifdef	BOOTGRUB
	.ascii	"GRLDR      \0"
#else
	.ascii	"KERNEL  SYS\0"
#endif

#ifdef ALTERNATIVE_KERNEL
filename_end_12_16:

	. = Entry_12_16 + 0x1e8

loadseg_off_12_16:	.word	0
loadseg_seg_12_16:	.word	LOADSEG_12_16

	. = Entry_12_16 + 0x1ec

boot_image_ofs_12_16:

	.word (filename_12_16 - Entry_12_16)+(filename_end_12_16 - filename_12_16 - 1)*2048
#endif

	. = Entry_12_16 + 0x1ee

disk_error_12_16:

	movw	$(msg_DiskReadError_12_16 - Entry_12_16 + 0x7c00), %si

boot_error_12_16:

/* prints string DS:SI (modifies AX BX SI) */

//print_12_16:
1:
	lodsb	(%si), %al	/* get token */
	//xorw	%bx, %bx	/* video page 0 */
	movb	$0x0e, %ah	/* print it */
	int	$0x10		/* via TTY mode */
	cmpb	$0, %al		/* end of string? */
	jne	1b		/* until done */

	/* The caller will change this to
	 *	ljmp	$0x9400, $(try_next_partition - _start1)
	 */

1:	jmp	1b

	. = Entry_12_16 + 0x1fc

	.word	0, 0xAA55 /* Win9x uses all 4 bytes as magic value here */

	. = Entry_12_16 + 0x200

	. = _start1 + 0x800




	.arch	i486, nojumps

/*
 #; Ext2 boot sector for GRLDR
 */


#define	DEBUG	call	debug_print
#undef DEBUG

	//. = _start1 + 0x800

Entry_ext2:

	jmp     1f

	. = Entry_ext2 + 0x02

	/* The default mode is CHS. This is for maximum compatiblity with
	 * small-sized disks, e.g., floppies.
	 *
	 * Valid values are 0x02 for CHS mode, or 0x42 for LBA mode.
	 *
	 * If the BIOS int13 supports LBA, this byte can be safely set to 0x42.
	 *
	 * Some USB BIOSes might have bugs when using CHS mode, so the format
	 * program should set this byte to 0x42. It seems that (generally) all
	 * USB BIOSes have LBA support.
	 *
	 * If the format program does not know whether the BIOS has LBA
	 * support, it may operate this way:
	 *
	 * if (partition_start + total_sectors_in_partition) exceeds the CHS
	 * addressing ability(especially when it is greater than 1024*256*63),
	 * the caller should set this byte to 0x42, otherwise, set to 0x02.
	 */

	.byte	0x02	/* for CHS. Another possible value is 0x42 for LBA */

	. = Entry_ext2 + 0x03

#if 0

	.ascii	"ext2 grldr"

#else

msg_DiskReadError_ext2:

	.ascii	"I/O error\0"

#endif

	. = Entry_ext2 + 0x0d

	/* sectors per block. Valid values are 2, 4, 8, 16, 32.	 */

	.byte	2

	. = Entry_ext2 + 0x0e

	/* bytes per block.
	 * Valid values are 0x400, 0x800, 0x1000, 0x2000, 0x4000.
	 */

	.word	1024		/* bytes per block, at most 16K */

	. = Entry_ext2 + 0x10

	/* pointers in pointers-per-block blocks, that is, number of blocks
	 * covered by a double-indirect block.
	 * Valid values are 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000.
	 */

	.long	0x10000	/* number of blocks covered by double-indirect block */
			/* low word=0 */

	. = Entry_ext2 + 0x14

	/* pointers per block, that is, number of blocks covered by an indirect
	 * block. Valid values are 0x100, 0x200, 0x400, 0x800, 0x1000.
	 */

	.long	0x100		/* high word=0, low byte=0 */

	. = Entry_ext2 + 0x18

	/* this is default for 1.44M floppy, the caller should set it to 
	 * a correct value */

	.word	18	/* sectors per track */

	. = Entry_ext2 + 0x1a

	/* this is default for 1.44M floppy, the caller should set it to 
	 * a correct value */

	.word	2	/* number of heads */

	. = Entry_ext2 + 0x1c

	/* this is default for 1.44M floppy, the caller should set it to 
	 * a correct value */

	.long	0		/* hidden sectors */

	. = Entry_ext2 + 0x20

	/* total sectors in the filesystem(or in the partition).
	 * This value is informative. The code does not use it.
	 */

	/* this is default for 1.44M floppy, the caller should set it to 
	 * a correct value */

	.long	2880

	. = Entry_ext2 + 0x24

	/* This byte is ignored for read. The program will write DL onto
	 * this byte. The caller should set drive number in DL. 
	 * We assume all BIOSes pass correct drive number in DL.
	 * That is to say, buggy BIOSes are not supported!!
	 */

	.byte	0		/* drive number */

	. = Entry_ext2 + 0x25

	/* this is default for floppies, the caller should set it to 
	 * a correct value for hard-drive partitions */

	.byte	0xff		/* partition number, 0xff for whole drive */

	. = Entry_ext2 + 0x26

	.word	0x80		/* inode size */

	. = Entry_ext2 + 0x28

	/* this is default for 1.44M floppy, the caller should set it to 
	 * a correct value */

	.long	2048		/* s_inodes_per_group */

	. = Entry_ext2 + 0x2c

	/* block number for group descriptors = s_first_data_block + 1.
	 * Valid values are 2 for 1024-byte blocks, and 1 for otherwise.
	 */

	/* this is default for 1.44M floppy, the caller should set it to 
	 * a correct value */

	.long	2		/* block number for group descriptors */

	. = Entry_ext2 + 0x30
1:
	cld			/* 0xFC */

	xorw	%ax, %ax	/* 0x31, 0xC0; CF=0, ZF=1 */

	/* this byte `nop' will be changed to `cwd' by bootlace for floppy */
	nop			/* 0x90=nop, 0x99=cwd */
				/* cwd will set DL=0 forcibly for floppy A: */

	movw	%ax, %ss	/* constant SS=0 */
	movw	$0x7c00, %sp

	movw	%sp, %bp	/* constant BP=0x7c00 */

	pushw	%ax		/* 0x0000 at 0000:7bfe */
	movw	$0x1000, %bx
	pushw	%bx		/* 0x1000 at 0000:7bfc */
	pushw	%ax		/* 0x0000 at 0000:7bfa */
				/* SP=0x7bfa */

	/* the 6 bytes in the stack are used by read_block():
	 *	0000	----	-2(%bp)
	 *	1000	----	-4(%bp)
	 *	0000	----	-6(%bp)
	 * Don't touch them!
	 */

	movb	%dl, 0x24(%bp)	/* BIOS passes drive number in DL */

	pushw	%ss
	movb	$0x41, %ah
	movw	$0x55AA, %bx
	int	$0x13
	popw	%ds		/* constant DS=0 */
	jc	1f		#; No EBIOS

	//testb	$1, %cl
	//jz	1f		#; No EBIOS
#if 0
	/* gcc-4.0.1 does not generate 2-byte code. */
	rcrb	$1, %cl		#; also can be rorb $1, %cl
#else
	.byte	0xD0, 0xD9	#; ror cl: D0 C9
#endif
	jnc	1f		#; No EBIOS

	movb	$0x42, (ebios_ext2 - 1 - Entry_ext2 + 0x7c00)
1:
	xorl	%eax, %eax	/* CF=0, ZF=1 */

#if 0
	/* the INC touches ZF flag, so use MOV instead */

	incw	%ax
	incw	%ax		/* EAX=2=inode number for root dir */
#else

	/* MOV keeps all flags untouched, so it is better than INC */

	movb	$2, %al		/* EAX=2=inode number for root dir */
#endif

	/* CF=0, ZF=1 because MOV and PUSH do not touch Flags */

	/* read root dir to 0000:1000, and grldr to 1000:0000 */

4:
	/* EAX holds the inode number: for root dir or grldr */

	/* These 3 PUSHes is intended to place 1000:0000 onto the stack for
	 * grldr. For root dir, the stack is not used since CF is cleared.
	 * Although there is no corresponding POPs, this is safe enough
	 * because the program comes here only twice: the first is for
	 * the root dir, and the second is for grldr.
	 *
	 * For root dir, CF=0 and ZF=1. For grldr, CF=1.
	 */

	pushw	%di		/* 0x1000, see "jz 4b" below. */
	pushw	%ss		/* 0x0000 */
	pushfw

	/* SP=0x7bf4 for root dir, or 0x7bee for grldr */

	decl	%eax		/* EAX=(inode - 1) */

	/* inode numbers are far less than 0x7fffffff, so it is safe to
	 * initialise EDX with CDQ */

	cdq			/* let EDX=0 */

	divl	0x28(%bp)	/* s_inodes_per_group */
				/* EAX=group number */
	pushl	%edx		/* EDX=inode number in the group */

	/* group numbers are far less than 0x7fffffff, so it is safe to
	 * initialise EDX with CDQ */

	cdq			/* let EDX=0 */
	shll	$5, %eax	/* EAX=relative displacement of the group descriptor */
	divl	0x0e(%bp)	/* bytes per block */
				/* EAX=relative block number for the group descriptor */
				/* DX=displacement in the block */
				/* EDX high=0 */

	pushw	%dx		/* we don't care about EDX high word, because it is 0 */

	addl	0x2c(%bp), %eax	/* EAX=absolute block number for the group descriptor */
				/* CF=0, ZF=0 */

	call	read_block	/* 0000:1000 points to the block data containing the group descriptor */
				/* ES changed and > 0, BX=0x1000 */
				/* ECX=EDX=0 */
				/* CF=0, ZF=0 */

	popw	%si		/* DS:[BX+SI] points to the group descriptor */
				/* DS:[BX+SI+8] points to the starting block number of the group inode table */

	popl	%eax		/* inode number in the group */
//	shll	$7, %eax	/* inode struct size = 0x80 */
//				/* EAX=relative displacement of the inode struct */
//				/* EDX=0 */
	movw	0x26(%bp), %dx	/* EDX=inode size */
	mull	%edx		/* EDX:EAX=relative displacement of the inode struct */

	divl	0x0e(%bp)	/* bytes per block */
				/* EAX=relative block number for the inode struct */
	pushw	%dx		/* DX=displacement of the inode struct in the block */
				/* EDX high=0 */

	addl	8(%bx, %si), %eax	/* EAX=absolute block number for the inode struct */
					/* CF=0, ZF=0 */

	call	read_block	/* 0000:1000 points to the block data containing the inode struct */
				/* ES changed and > 0, BX=0x1000 */
				/* ECX=EDX=0 */
				/* CF=0, ZF=0 */

	popw	%si		/* DS:[BX+SI] points to the inode struct */

	addw	%bx, %si	/* DS:SI points to the inode struct */

	/* Move the inode struct to a known safe area(0000:0fa8 - 0000:0fff),
	 * that is, 0x58 bytes immediately before 0000:1000. We care about only
	 * the beginning 0x58 bytes of the 0x80-byte inode struct, the last
	 * 0x28 bytes are ignored. The area from 0xfa8+0x28 to 0xfa8+0x57
	 * stores 12 direct block pointers.
	 *  
	 *  
	 * At address   Initial value               Stores what?
	 * ==========   =============   ======================================
	 * 0xfa8+0x04      (const)           the size of the file in bytes
	 * 
	 * 0xfa8+0x08    total blocks           blocks left to read
	 * 
	 * 0xfa8+0x0c         0           serial number of the block to read
	 * 
	 */

	pushw	%ss
	popw	%es				/* ES=0 */

	leaw	-0x58(%bx), %di			/* BX=0x1000, so DI=0x0fa8 */
	//movw	$0x0fa8, %di
	movb	$0x2c, %cl			/* 0x2c words = 0x58 bytes */

	repz movsw				/* now ECX=0, BX=0x1000=DI */

	movl	%ecx, (0x0c - 0x58)(%di)	/* block serial number of the file */
						/* ECX=0 means first block */
						/* DI=0x1000 */

	movl	(0x04 - 0x58)(%di), %eax	/* i_size, the file size */
	decl	%eax

	divl	0x0e(%bp)			/* bytes per block */
						/* EDX=various */
	incl	%eax
	movl	%eax, (0x08 - 0x58)(%di)	/* total blocks for file data */

	/*
	 * 0000:1000	trebly indirect block
	 * 0000:8000	indirect block
	 * 0000:c000	double indirect block
	 * 1000:0000	the file data
	 */

	/* now DS:SI points to indirect block number */

	lodsl					/* indirect block number */
	testl	%eax, %eax
	jz	1f

	//pushw	%ss
	//popw	%es				/* ES=0 */
	movb	$0x80, %bh			/* ES:BX=0000:8000 */
#if 0
	stc
	call	read_block
#else
	call	read_block_c
#endif
						/* ES changed and > 0, BX=0x8000 */
						/* ECX=EDX=0 */
						/* ZF=0, CF=0 */

	/* now DS:SI points to double indirect block number */

	lodsl					/* double indirect block number */
	testl	%eax, %eax
	jz	1f

#if 0
	pushw	%ss
	popw	%es				/* ES=0 */
	movb	$0xc0, %bh			/* ES:BX=0000:c000 */
	stc
	call	read_block
#else
	movb	$0xc0, %bh			/* ES:BX=0000:c000 */
	call	read_block_c
#endif
						/* ES changed and > 0, BX=0xc000 */
						/* ECX=EDX=0 */
						/* ZF=0, CF=0 */
	
	/* now DS:SI points to trebly indirect block number */

	lodsl					/* trebly indirect block number */
	testl	%eax, %eax			/* CF=0, TEST always clears CF */
	jz	1f
						/* ZF=0 */
	//pushw	%ss
	//popw	%es				/* ES=0 */
	//movb	$0x10, %bh			/* ES:BX=0000:1000 */
	//stc
	call	read_block			/* 0000:1000 points to the block data */
						/* ES changed and > 0, BX=0x1000 */
						/* ECX=EDX=0 */
						/* ZF=0, CF=0 */

	/* the block at 0000:1000, which contains the indirect block numbers,
	 * is just overwritten by the trebly indirect block */
	
1:
	/* get absolute block number by block serial number */

	movl	(0x0c - 0x58)(%di), %ebx	/* block serial number of the file */
	subl	$12, %ebx
	jc	3f				/* direct block: block serial number < 12 */

	pushw	%bx
	subl	0x14(%bp), %ebx
	popw	%ax
	jnc	2f

	/* indirect block: 12 <= block serial number < 12 + 0x14(%bp) */

	//addw	0x14(%bp), %bx
	addb	$(0x70 / 4), %ah
	//xchgw	%ax, %bx
	jmp	8f

2:
	pushl	%ebx
	subl	0x10(%bp), %ebx
	jc	7f	/* EBX on the stack is < 0x10(%bp). double indirect block: */
			/* 12 + 0x14(%bp) <= block serial number < 12 + 0x14(%bp) + 0x10(%bp) */

	/* trebly indirect block: block serial number >= 12 + 0x14(%bp) + 0x10(%bp) */

	popl	%eax		/* discard the stack */
	xchgl	%eax, %ebx	/* move EBX to EAX */
				/* EDX=0 */
	divl	0x10(%bp)
				/* EAX=indirect block number, < 0x14(%bp) */
				/* EDX=block number, < 0x10(%bp) */

	pushl	%edx		/* EDX < 0x10(%bp) */
	testl	%edx, %edx
	jnz	7f

	/* EDX=0, so we need to load the double indirect block */

	shlw	$2, %ax
	xchgw	%ax, %bx

	/* get the double indirect block number from the trebly indirect
	 * block data */

	movl	(%bx, %di), %eax

//6:
	movw	$0xc000, %bx			/* ES:BX=0000:c000 */

	//pushw	%ss
	//popw	%es				/* ES=0 */
	//stc
	call	read_block_c	/* 0000:c000 points to the block data */
				/* ES changed and > 0, BX=0xc000 */
				/* ECX=EDX=0 */
				/* CF=0, ZF=0 */
7:
	popl	%eax		/* EAX < 0x10(%bp) */
	cdq			/* let EDX=0 (notice the above jc 7f and jnz 7f) */
	divl	0x14(%bp)
				/* EAX=indirect block number, < 0x14(%bp) */
				/* EDX=block number, < 0x14(%bp) */

	pushw	%dx		/* EDX < 0x14(%bp) */
	testw	%dx, %dx
	jnz	7f

	/* if DX=0, we need to load the indirect block */

	//addb	$(0xb0 / 4), %ah
	shlw	$2, %ax
	xchgw	%ax, %bx

	/* get the indirect block number from the double indirect block data */

	movl	0xb000(%bx, %di), %eax
	//movl	(%bx, %di), %eax
//5:
	movw	$0x8000, %bx			/* ES:BX=0000:8000 */

	//pushw	%ss
	//popw	%es				/* ES=0 */
	//stc
	call	read_block_c	/* 0000:8000 points to the block data */
				/* ES changed and > 0, BX=0x8000 */
				/* ECX=EDX=0 */
				/* CF=0, ZF=0 */
7:
	popw	%ax		/* AX < 0x14(%bp) */
8:
	xchgw	%ax, %bx
3:
	shlw	$2, %bx
	movl	(%bx, %di), %eax

	/* got it! EAX=absolute block number */

	/* read block data to 1000:0000. For root dir, read each block to
	 * 1000:0000(overwrite the previous read). For grldr, read blocks
	 * one by one to the area starting at 1000:0000. 
	 */

	popfw
	popw	%bx
	popw	%es
	pushfw

	/* CF=0 and ZF=1 for reading root dir, CF=1 for reading grldr */

	call	read_block	/* 1000:0000 points to the block data */
				/* ES changed and > 0x1000, BX=0 */
				/* ECX=EDX=0 */
				/* CF=0, ZF=0 */

	popfw
	pushw	%es
	pushw	%bx
	pushfw

	jc	3f		/* CF=1, we are reading grldr */

	/* We have just read a block of the root dir to 1000:0000. 
	 * So we check all dir entries in the block to see if anyone
	 * matches grldr.
	 */

	xorw	%si, %si
	pushw	%ss
	popw	%es		/* ES=0 */

2:
	pushw	%ds		/* DS=0 */
	movw	%di, %ds	/* DS=0x1000 */
	movw	$(filename_ext2 - Entry_ext2 + 0x7c00), %di

	pushw	%si
	lodsl			/* This is possible inode number for grldr */
	pushl	%eax		/* This is possible inode number for grldr */
	lodsw
	xchgw	%ax, %dx	/* rec_len */
	lodsw			/* AL=name_len, should be 5 for grldr */
				/* AH=file_type(1 for regular file) */
#if 0
	cmpw	$0x0105, %ax
	jnz	5f
	movb	%al, %cl	/* CH is already 0 */
	repz cmpsb
#else
	decb	%ah
	//jnz	5f
	xchgw	%ax, %cx	/* CX=name_len */
	repz cmpsb
	jnz	5f
	xchgw	%ax, %cx	/* movb	$0, %al */
	scasb
#endif
5:
	popl	%eax		/* This is possible inode number for grldr */
	popw	%si

	/* DS=0x1000, EAX=inode number */

	movw	%ds, %di	/* DI=0x1000 */
	popw	%ds		/* DS=0 */

	stc			/* indicates the new inode is for grldr */

	jz	4b		/* grldr is found with EAX=inode number */

	addw	%dx, %si
	cmpw	0x0e(%bp), %si	/* bytes per block */
	jb	2b

	/* file not found in this block, continue */

	/* We are lucky that CF=0, which indicates we are dealing with
	 * the root dir.
	 */

3:

	/* CF=1 for grldr, CF=0 for root dir. */

	incl	(0x0c - 0x58)(%di)
	decl	(0x08 - 0x58)(%di)
	jnz	1b

#if 0
	/* The above 2 instructions INC and DEC do not touch CF, so we
	 * can omit this POP-PUSH pair.
	 */

	popfw
	pushfw
#endif

	movw	$(msg_No_grldr_ext2 - Entry_ext2 + 0x7c00), %si

	jnc	boot_error_ext2		/* grldr not found in the root dir */

	/* All grldr blocks have been loaded to memory starting at 1000:0000,
	 * Before the boot, we pass boot_drive and boot_partition to grldr.
	 */

	/* ES>0x1000, BX=0, ECX=EDX=0, DI=0x1000, SS=0, SI>0x7c00, DS=0
	 * BP=0x7c00, SP<=0x7c00
	 */

	movw	0x24(%bp), %dx

	/* boot it now! */

	pushw	%di		/* 0x1000 */
	pushw	%ss		/* 0x0000 */
	lret

read_block_c:

	pushw	%ss
	popw	%es				/* ES=0 */
	stc
	
/* read_block - read a block
 * input:	CF	  - indicator for overlap or consecution
 *		EAX 	  = block number
 *		ES:BX	  - buffer
 *
 * output:	if CF is cleared on input, ES:BX is initialized to 0000:1000
 *		ES:BX	  - buffer filled with data
 *		ES, EAX	  - Changed
 *		ECX	  = 0
 *		EDX	  = 0
 *		ZF    = 0
 *		CF    = 0
 */

read_block:

	jc	1f

	.byte	0xC4, 0x5E, 0xFC	/* lesw -4(%bp), %bx */
					/* ES:BX=0000:1000 */
	jnz	1f

	//at this time, the gcc cannot generate 3 byte code
	.byte	0xC4, 0x5E, 0xFA	/* lesw -6(%bp), %bx */
					/* ES:BX=1000:0000 */
	//. = . - (. - read_block) / 6
1:
	movzbl	0x0d(%bp), %ecx	/* CX=sectors per block */
				/* ECX high=0 */
	. = . - (. - 1b) / 6
 	mull	%ecx		/* EAX=relative sector number */
				/* EDX=0 */
	. = . - (. - 1b) / 9
	addl	0x1c(%bp), %eax	/* EAX=absolute sector number */

#if 1
	/* pass through, saving 4 bytes(call and ret) */
#else
	call	readDisk_ext2
	ret
#endif

/* Read sectors from disk, using LBA or CHS
 * input:	EAX   = 32-bit LBA sector number
 *		CX    = number of sectors to read
 *		ECX high word  = 0
 *		ES:BX = destination buffer
 *
 * output:	No return on error
 *		BX not changed
 *		ES    = ES + 0x20 * CX
 *		EAX   = EAX + CX
 *		ZF    = 0
 *		CF    = 0
 */
 
readDisk_ext2:
2:
	pushal
	//xorl	%edx, %edx	/* EDX:EAX = LBA */
	pushl	%edx		/* hi 32bit of sector number */
	pushl	%eax		/* lo 32bit of sector number */
	pushw	%es		/* buffer segment */
	pushw	%bx		/* buffer offset */
	pushw	$1		/* 1 sector to read */
	pushw	$16		/* size of this parameter block */

	//xorl	%ecx, %ecx
	pushl	0x18(%bp)	/* lo:sectors per track, hi:number of heads */
	popw	%cx		/* ECX = sectors per track */
	divl	%ecx		/* residue is in EDX */
				/* quotient is in EAX */
				/* EDX high=0, DH=0 */
	incw	%dx		/* DL=sector number */
	popw	%cx		/* ECX = number of heads */
	pushw	%dx		/* push sector number into stack */
	xorw	%dx, %dx	/* EDX:EAX = cylinder * TotalHeads + head */
	divl	%ecx		/* residue is in EDX, head number */
				/* quotient is in EAX, cylinder number */
				/* EDX high=0, EAX high=0 */


	xchgb	%dl, %dh	/* head number should be in DH */
				/* DL = 0 */
	popw	%cx		/* pop sector number from stack */
	xchgb	%al, %ch	/* lo 8bit cylinder should be in CH */
				/* AL = 0 */
	shlb	$6, %ah		/* hi 2bit cylinder ... */
	orb	%ah, %cl	/* ... should be in CL */
	
	incw	%ax		/* AL=1, read 1 sector */

	/* Instead of 0x0e, the LBA indicator at 2(%bp) is 
	 *
	 *	0x42 for LBA
	 *
	 * and
	 *
	 *	0x02 for CHS
	 */
#if 0
	movb	$0x42, %ah
	/* ebios_ext2 - 1 points to 0x42 that can be changed to 0x02 */
#else
	movb	$0x02, %ah
	/* ebios_ext2 - 1 points to 0x02 that can be changed to 0x42 */
#endif
ebios_ext2:

	//andb	2(%bp), %ah

	movw	%sp, %si	/* DS:SI points to disk address packet */
	movb	0x24(%bp), %dl	/* drive number */
	pushw	%es
	pushw	%ds
	int	$0x13
	popw	%ds
	popw	%bx
	jc	disk_error_ext2
	leaw	0x20(%bx), %bx
	movw	%bx, %es
	popaw			/* remove parameter block from stack */
	popal
	incl 	%eax		/* next sector, here ZF=0 */
	loop	2b
	ret

	//. = . - (. - readDisk_ext2)/74

//msg_DiskReadError_ext2:
//
//	.ascii	"disk error\0"

msg_No_grldr_ext2:

	.ascii	"No "

filename_ext2:
	.ascii	"grldr\0"

	. = Entry_ext2 + 0x1ee

filename_end_ext2:

	.word (filename_ext2 - Entry_ext2)+(filename_end_ext2 - filename_ext2 - 1)*2048

	. = Entry_ext2 + 0x1f0

disk_error_ext2:

	movw	$(msg_DiskReadError_ext2 - Entry_ext2 + 0x7c00), %si

boot_error_ext2:

/* prints string DS:SI (modifies AX BX SI) */

//print_ext2:
1:
	lodsb	(%si), %al	/* get token */
	//xorw	%bx, %bx	/* video page 0 */
	movb	$0x0e, %ah	/* print it */
	int	$0x10		/* via TTY mode */
	cmpb	$0, %al		/* end of string? */
	jne	1b		/* until done */
#if 1

	/* The caller will change this to
	 *	ljmp	$0x9400, $(try_next_partition - _start1)
	 */

1:	jmp	1b

#else
	/* boot failed, try to hand over the control to supervisor */
	ldsw	(1f + 3 - Entry_ext2)(%bp), %si
	lodsl
	cmpl	$0x9400b8fa, %eax
1:	jnz	1b	/* no supervisor, hang up. */
	ljmp	$0x9400, $(try_next_partition - _start1)

	//. = . - (. - disk_error_ext2) / 30
#endif

	. = Entry_ext2 + 0x1fe

	.word	0xAA55

	. = _start1 + 0xA00

#define INSIDE_GRLDR

//#include "ntfsbs.S"
//-----------------begin of "ntfsbs.S"-----------------------
/*
 *  GRUB Utilities --  Utilities for GRUB Legacy, GRUB2 and GRUB for DOS
 *  Copyright (C) 2007 Bean (bean123@126.com)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* NTFS boot sector for loading GRLDR , written by bean
 *
 * This file can be compiled as standalone boot sector, or it can be embeded in
 * GRLDR.MBR at 0xA00 , right after the ext2 boot sector
 *
 * To compile the standalone ntfsbs.bin:
 *     gcc -c -o ntfsbs.o ntfsbs.S
 *     gcc -nostdlib -Wl,-N -Wl,-Ttext -Wl,7C00 -o ntfsbs_exec ntfsbs.o
 *     objcopy -O binary ntfsbs_exec ntfsbs.bin
 *
 * To install the standalone ntfsbs.bin:
 *     grubinst --restore=ntfsbs.bin DEVICE_OR_FILE
 *
 * Where DEVICE_OR_FILE specify a NTFS partition
 *
 * Limitations:
 *  1. Don't support >1K MFT record size, >4K INDEX record size
 *  2. Don't support encrypted file
 *  3. Don't support >4K non-resident attribute list and $BITMAP
 *
 */

#ifndef INSIDE_GRLDR

	.text

	.code16
#endif

#define AT_STANDARD_INFORMATION	0x10
#define AT_ATTRIBUTE_LIST	0x20
#define AT_FILENAME		0x30
#define AT_OBJECT_ID		0x40
#define AT_SECURITY_DESCRIPTOR	0x50
#define AT_VOLUME_NAME		0x60
#define AT_VOLUME_INFORMATION	0x70
#define AT_DATA			0x80
#define AT_INDEX_ROOT		0x90
#define AT_INDEX_ALLOCATION	0xA0
#define AT_BITMAP		0xB0
#define AT_SYMLINK		0xC0
#define AT_EA_INFORMATION	0xD0
#define AT_EA			0xE0

#define MAX_MFT_SIZE	1		// 1<<(1+9) = 1024
#define MAX_IDX_SIZE	3		// 1<<(3+9) = 4096

#define LOADSEG_NT	0x2000

#define MMFT_BASE	0x2000
#define MMFT_EMFT	(MMFT_BASE +1024)
#define MMFT_EBUF	(MMFT_BASE + 2048)

#define CMFT_BASE	(MMFT_BASE + 6144)
#define CMFT_EMFT	(CMFT_BASE + 1024)
#define CMFT_EBUF	(CMFT_BASE + 2048)

#define INDX_BASE	(CMFT_BASE + 6144)

#define SBUF_BASE	(INDX_BASE + 4096)

#define NTFS_Large_Structure_Error_Code	1
#define NTFS_Corrupt_Error_Code		2
#define NTFS_Run_Overflow_Error_Code	3
#define NTFS_No_Data_Error_Code		4
#define NTFS_Decompress_Error_Code	5

#define NT_FG_COMP	1
#define NT_FG_MMFT	2
#define NT_FG_ALST	4
#define NT_FG_GPOS	8

#define nt_boot_drive	-2(%bp)
#define nt_blocksize	-4(%bp)
#define nt_spc		-5(%bp)
#define nt_mft_size	-6(%bp)
#define nt_idx_size	-7(%bp)
#define nt_mft_start	-12(%bp)
#define nt_remain_len	-16(%bp)
//#define nt_file_count	-18(%bp)

#define nt_flag		(%di)
#define nt_attr_cur	2(%di)
#define nt_attr_nxt	4(%di)
#define nt_attr_end	6(%di)
#define nt_curr_vcn	8(%di)
#define nt_curr_lcn	0x10(%di)
#define nt_attr_ofs	0x14(%di)
#define nt_target_vcn	0x18(%di)
#define nt_read_count	0x1C(%di)
#define nt_vcn_offset	0x20(%di)

#define nt_emft_buf	1024(%di)
#define nt_edat_buf	2048(%di)

	.arch	i586

Entry_nt:
	jmp	1f

	. = Entry_nt + 0x02

	.byte	0x90	/* for CHS. Another possible value is 0x0e for LBA */

	.ascii	"NTFS    "

	.word	0	/* 0B - Bytes per sector */
	.byte	0	/* 0D - Sectors per cluster */
	.word	0	/* 0E - reserved sectors, unused */
	.byte	0	/* 10 - number of FATs, unused */
	.word	0	/* 11 - Max dir entries for FAT12/FAT16, unused */
	.word	0	/* 13 - total sectors for FAT12/FAT16, unused */
	.byte	0xF8	/* 15 - Media descriptor */
	.word	0	/* 16 - sectors per FAT for FAT12/FAT16, unused */
	.word	255	/* 18 - Sectors per track */
	.word	63	/* 1A - Number of heads */
nt_part_ofs:
	.long	0	/* 1C - hidden sectors */
	.long	0	/* 20 - total sectors for FAT32, unused */
	.long	0x800080
			/* 24 - Usually 80 00 80 00, A value of 80 00 00 00 has
			 * been seen on a USB thumb drive which is formatted
			 * with NTFS under Windows XP. Note this is removable
			 * media and is not partitioned, the drive as a whole
			 * is NTFS formatted.
		 	 */
	.long	0,0	/* 28 - Number of sectors in the volume */
	.long	0,0	/* 30 - LCN of VCN 0 of the $MFT */
	.long	0,0	/* 38 - LCN of VCN 0 of the $MFTMirr */
	.long	0	/* 40 - Clusters per MFT Record */
	.long	4	/* 44 - Clusters per Index Record */
	.long	0,0	/* 48 - Volume serial number */
	.long	0	/* 50 - Checksum, usually 0 */

1:

	. = Entry_nt + 0x54

	cli
	cld

	. = Entry_nt + 0x56

	/* the byte at offset 0x57 stores the real partition number for read.
	 * the format program or the caller should set it to a correct value.
	 * For floppies, it should be 0xff, which stands for whole drive.
	 */

	movb	$0xff, %dh	/* boot partition number */

	xorw	%ax, %ax
	movw	$0x7c00, %bp

	movw	%ax, %ss	/* stack and BP-relative moves up, too */
	leaw	-0x20(%bp), %sp
	sti

	movw	%dx, nt_boot_drive

	pushw	%ax		/* AX=0 */

	/* Test if your BIOS support LBA mode */
	movb	$0x41, %ah
	movw	$0x55AA, %bx
	int	$0x13

	popw	%ds		/* DS=0 */

	jc	1f		/* No EBIOS */
	cmpw	$0xAA55, %bx
	jne	1f		/* No EBIOS */
	testb	$1, %cl
	jz	1f		/* No EBIOS */
	/* EBIOS supported */
	movb	$0x42, (ebios_nt - 1 - Entry_nt)(%bp)
1:

	pushw	%ss		/* SS=0 */
	popw	%es		/* ES=0 */

	cmpl	$0x42555247, (nt_sector_mark - Entry_nt)(%bp)
	jz	1f			// Must be called from GRLDR.MBR

	movw	$0x7E00, %bx
	movl	(nt_part_ofs - Entry_nt)(%bp), %eax
	incl	%eax
	call	readDisk_nt		// Load the second sector from disk
	call	readDisk_nt		// Load the third sector from disk
	call	readDisk_nt
1:

	xorl	%eax, %eax
	movw	0xb(%bp), %ax		// Bytes per sector (blocksize)
	movw	%ax, nt_blocksize

	call	convert_to_power_2
	movb	%cl, %bl
	movb	0xd(%bp), %al		// Sectors per cluster
	call	convert_to_power_2
	movb	%cl, %ch
	addb	%bl, %ch
	subb	$9, %ch			// 1<<ch = sectors per cluster
	movb	%ch, nt_spc
	movb	0x44(%bp), %al 		// Index record size (high bits of eax is 0)
	call	convert_size

	cmpb	$MAX_IDX_SIZE, %cl
	jbe	1f

NTFS_Large_Structure_Error:
	movb	$NTFS_Large_Structure_Error_Code, %al
	jmp	NTFS_Error

1:
	movb	%cl, nt_idx_size

	movb	0x40(%bp), %al 		// MFT record size
	call	convert_size

	cmpb	$MAX_MFT_SIZE, %cl
	jnz	NTFS_Large_Structure_Error

	movb	%cl, nt_mft_size

	movl	0x30(%bp), %eax
	//movl	0x34(%bp), %edx

	movb	%ch, %cl		// ch still contains nt_spc

	//shldl	%cl, %eax, %edx
	//orl	%edx, %edx
	//jnz	NTFS_Large_Structure_Error

	shll	%cl, %eax
	addl	(nt_part_ofs - Entry_nt)(%bp), %eax
	movl	%eax, nt_mft_start

	movw	$1, %dx
	movb	nt_mft_size, %cl
	shlw	%cl, %dx
	movw	%dx, %cx

	movw	$MMFT_BASE, %bx
	pushw	%bx
1:
	call	readDisk_nt
	loop	1b

	popw	%bx
	cmpw	$0x4946, (%bx)		// "FI"
	jnz	NTFS_Corrupt_Error

	// dx should still contain the number of sectors in the MFT record
	movw	%dx, %cx
	call	ntfs_fixup

	movw	%bx, %di
	movb	$AT_DATA, %al		// find $DATA

	call	ntfs_locate_attr
	jc	NTFS_Corrupt_Error

	movw	$CMFT_BASE, %bx
	xorl	%eax, %eax
	movb	$0x5, %al
	call	ntfs_read_mft
	movw	%bx, %di

	jmp	ntfs_search

// Convert the size of MFT and IDX block
// Input:
//     eax: size
//     ch: spc
// Output:
//     cl: convert value
convert_size:
	orb	%al, %al
	js	1f
	movb	%ch, %cl
	jmp	2f			// Jump to 2 in convert_to_power_2
1:
	negb	%al
	subb	$9, %al
	movb	%al, %cl
	ret

// Convert number to a power of 2
// Input:
//     eax
// Output:
//     cl: 1<<cl = eax
//     eax: 0

convert_to_power_2:
	xorb	%cl, %cl
2:
	incb	%cl
	shrl	$1, %eax
	jnc	2b
	decb	%cl
	ret

// Fixup the "FILE" and "INDX" record
// Input:
//     DS:BX - data buffer
//     CX - buffer length in sectors
//

ntfs_fixup:
	push	%bx
	push	%di
	movw	%bx, %di

	movw	6(%bx), %ax		// Size of Update Sequence
	decw	%ax
	movw	%ax, %bx

	mulw	nt_blocksize
	shlw	$9, %cx
	cmpw	%ax, %cx
	jnz	NTFS_Corrupt_Error	// blocksize * count != size

	movw	%bx, %cx		// cx = count

	movw	%di, %bx
	addw	4(%bx), %bx		// Offset to the update sequence
	movw	(%bx), %ax		// Update Sequence Number
	subw	$2, %di

1:
	addw	nt_blocksize, %di
	addw	$2, %bx
	cmpw	(%di), %ax
	jnz	NTFS_Corrupt_Error
	movw	(%bx), %dx
	movw	%dx, (%di)
	loop	1b

	popw	%di
	popw	%bx
	ret

NTFS_Corrupt_Error:
	movb	$NTFS_Corrupt_Error_Code, %al
	jmp	NTFS_Error

/* Read a sector from disk, using LBA or CHS
 * input:	EAX - 32-bit DOS sector number
 *		ES:BX - destination buffer
 *		(will be filled with 1 sector of data)
 * output:	ES:BX points one byte after the last byte read.
 *		EAX - next sector
 */

readDisk_nt:

	pushal
	xorl	%edx, %edx	/* EDX:EAX = LBA */
	pushl	%edx		/* hi 32bit of sector number */
	pushl	%eax		/* lo 32bit of sector number */
	pushw	%es		/* buffer segment */
	pushw	%bx		/* buffer offset */
	pushw	$1		/* 1 sector to read */
	pushw	$16		/* size of this parameter block */

	xorl	%ecx, %ecx
	pushl	0x18(%bp)	/* lo:sectors per track, hi:number of heads */
	popw	%cx		/* ECX = sectors per track */
	divl	%ecx		/* residue is in EDX */
				/* quotient is in EAX */
	incw	%dx		/* sector number in DL */
	popw	%cx		/* ECX = number of heads */
	pushw	%dx		/* push sector number into stack */
	xorw	%dx, %dx	/* EDX:EAX = cylinder * TotalHeads + head */
	divl	%ecx		/* residue is in EDX, head number */
				/* quotient is in EAX, cylinder number */
	xchgb	%dl, %dh	/* head number should be in DH */
				/* DL = 0 */
	popw	%cx		/* pop sector number from stack */
	xchgb	%al, %ch	/* lo 8bit cylinder should be in CH */
				/* AL = 0 */
	shlb	$6, %ah		/* hi 2bit cylinder ... */
	orb	%ah, %cl	/* ... should be in CL */

	movw	$0x201, %ax	/* read 1 sector */
ebios_nt: /* ebios_nt - 1 points to 0x02 that can be changed to 0x42 */

//	cmpb	$0x0e, 2(%bp)	/* force LBA? */
//	jnz	1f		/* no, continue */
//	movb	$0x42, %ah	/* yes, use extended disk read */
//1:
	movw	%sp, %si	/* DS:SI points to disk address packet */
	movb	nt_boot_drive, %dl	/* hard disk drive number */

	int	$0x13

	popaw			/* remove parameter block from stack */
	popal
	jc	disk_error_nt	/* disk read error, jc 1f if caller handles */
	incl 	%eax		/* next sector */
	addw	0x0b(%bp), %bx	/* bytes per sector */
	jnc	1f		/* 64K bound check */
	pushw	%dx
	movw	%es, %dx
	addb	$0x10, %dh	/* add 1000h to ES */
				/* here, carry is cleared */
	movw	%dx, %es
	popw	%dx
1:
	/* carry stored on disk read error */
	ret


msg_DiskReadError_nt:

	.ascii	"0\0"

NTFS_Error:
	addb	%al, (msg_DiskReadError_nt - Entry_nt)(%bp)
	jmp	disk_error_nt

msg_NTFS_Not_Found_Error:
	.ascii "No "

nt_boot_image:
	.ascii "grldr\0"

// Kernel load address, located at 0x1E8
	. = Entry_nt + 0x1e8

nt_boot_image_end:

nt_loadseg_off:
	.word	0
	.word	LOADSEG_NT

// Boot image offset and length, located at 0x1EE
// Lower 11 bit is offset, higher 5 bit is length
	. = Entry_nt + 0x1ec

nt_boot_image_ofs:
	.word (nt_boot_image - Entry_nt)+(nt_boot_image_end - nt_boot_image-1)*2048

	. = Entry_nt + 0x1ee

disk_error_nt:

	movw	$(msg_DiskReadError_nt - Entry_nt + 0x7c00), %si

boot_error_nt:

/* prints string DS:SI (modifies AX BX SI) */

//print_32:
1:
	lodsb	(%si), %al	/* get token */
	//xorw	%bx, %bx	/* video page 0 */
	movb	$0x0e, %ah	/* print it */
	int	$0x10		/* via TTY mode */
	cmpb	$0, %al		/* end of string? */
	jne	1b		/* until done */

	/* The caller will change this to
	 *	ljmp	$0x9400, $(try_next_partition - _start1)
	 */

1:	jmp	1b

	. = Entry_nt + 0x1fc

	.word	0, 0xAA55

// Here starts sector #2

// Input:
//     DI - current mft
ntfs_search:
	//movw	$0, nt_file_count
	call	ntfs_init_attr
	movb	$AT_INDEX_ROOT, %al

1:
	call	ntfs_find_attr
	jc	NTFS_Not_Found_Error

	cmpl	$0x180400,  8(%si)	// resident
					// namelen = 4
					// name offset = 0x18
	jnz	1b
	//cmpl	$0x490024, 0x18(%si)	// "$I"
	//jnz	1b
	//cmpl	$0x300033, 0x1C(%si)
	//jnz	1b			// "30"
	//testw	$0xC001, 12(%si)	// not compressed, encrypted or sparse
	//jnz	1b

	addw	0x14(%si), %si		// jump to attribute
	cmpb	$0x30, (%si)
	jnz	1b			// test if it index filenames

	addw	$0x10, %si		// skip the index root
	addw	(%si), %si

	call	ntfs_find_grldr
	jnc	ntfs_final

	call	ntfs_init_attr
	movb	$AT_BITMAP, %al
1:
	call	ntfs_find_attr
	jc	NTFS_Not_Found_Error
	movw	9(%si), %bx
	cmpb	$4, %bl
	jnz	1b
	//shrw	$4, %bx
	//cmpl	$0x490024, (%bx, %si)	// "$I"
	//jnz	1b
	cmpb	$0, 8(%si)
	jnz	1f
	pushw	0x10(%si)
	addw	0x14(%si), %si
	pushw	%si
	jmp	2f
1:
	pushw	0x30(%si)
	xorl	%edx, %edx
	movl	0x28(%si), %ecx
	cmpw	$4096, %cx
	ja	NTFS_Not_Found_Error
	shrl	$9, %ecx
	movw	$SBUF_BASE, %bx
	pushw	%bx
	call	ntfs_read_data
2:

	movb	$AT_INDEX_ALLOCATION, %al

1:
	call	ntfs_locate_attr
	jc	NTFS_Not_Found_Error

	cmpl	$0x400401, 8(%si)	// non-resident
					// namelen = 4
					// name offset = 0x40
	jnz	1b
	//cmpl	$0x490024, 0x40(%si)	// "$I"
	//jnz	1b
	//cmpl	$0x300033, 0x44(%si)
	//jnz	1b			// "30"
	//testw	$0xC001, 12(%si)	// not compressed, encrypted or sparse
	//jnz	1b

	movb	nt_idx_size, %cl
	xorl	%ebx, %ebx
	movb	$1, %bl
	shll	%cl, %ebx		// ebx - index size
	xorl	%edx, %edx		// edx - index offset


	popw	%si
	popw	%cx

1:
	pushw	%cx
	lodsb	(%si), %al

	movw	$8, %cx
2:
	pushw	%cx
	pushw	%ax
	testb	$1, %al
	jz	3f
	pushw	%si
	pushl	%edx
	pushl	%ebx

	movl	%ebx, %ecx
	movw	$INDX_BASE, %bx
	call	ntfs_read_attr
	jc	NTFS_Not_Found_Error
	cmpw	$0x4E49, (%bx)		// "IN"
	jnz	NTFS_Not_Found_Error
	call	ntfs_fixup
	movw	%bx, %si
	addw	$0x18, %si
	addw	(%si), %si

	call	ntfs_find_grldr
	jnc	ntfs_final_0

	popl	%ebx
	popl	%edx
	popw	%si

3:
	addl	%ebx, %edx

	popw	%ax
	shrb	$1, %al
	popw	%cx
	loop	2b

	popw	%cx
	loop	1b

	//pushw	nt_file_count
	//call	hex_out

NTFS_Not_Found_Error:
	leaw	(msg_NTFS_Not_Found_Error - Entry_nt)(%bp), %si
	jmp	boot_error_nt

ntfs_final_0:
	//addw	$16, %sp

// Input:
//     DI - current mft
//     SI - index entry
ntfs_final:
	cmpw	$0, 4(%si)
	jnz	NTFS_Large_Structure_Error

	movl	(%si), %eax
	movw	%di, %bx
	call	ntfs_read_mft

	movb	$AT_DATA, %al
	call	ntfs_locate_attr
	jc	NTFS_No_Data_Error

	cmpb	$1, 8(%si)		// non-resident / resident
	jz	1f

	movw	0x10(%si), %cx		// Resident
	lesw	(nt_loadseg_off - Entry_nt)(%bp), %di
	addw	0x14(%si), %si
	rep	movsb	(%si), %es:(%di)
	jmp	2f

1:

	xorl	%edx, %edx
	movl	0x28(%si), %ecx		// Use allocate size instead of real size
	shrl	$9, %ecx

	lesw	(nt_loadseg_off - Entry_nt)(%bp), %bx
	call	ntfs_read_data


2:

	//movb	$1, (do_pause - Entry_nt)(%bp)
	//call	pause

	movw	nt_boot_drive, %dx
	ljmp	*(nt_loadseg_off - Entry_nt)(%bp)

NTFS_No_Data_Error:
	movb	$NTFS_No_Data_Error_Code, %al
	jmp	NTFS_Error

// Try to find GRLDR in the index
// Input:
//     DS:SI - points to index entry
// Output:
//     CF - status

ntfs_find_grldr:
	movw	%si, %bx
	testb	$2, 0xC(%bx)
	jz	1f
	stc
	ret
1:
	//incw	nt_file_count

	xorb	%ch, %ch

	pushw	%si
	leaw	(nt_boot_image - Entry_nt)(%bp), %si
	addw	$0x52, %bx		// The value at 0xA(%bx) is wrong sometimes (0x4C)
	movb	-2(%bx), %cl
1:
	lodsb	(%si), %al
	movb	(%bx), %ah
	cmpb	$'A', %ah
	jb	2f
	cmpb	$'Z', %ah
	ja	2f
	addb	$('a'-'A'), %ah		// Convert to lowercase
2:

	cmpb	%ah, %al
	jnz	3f			// Not match

	incw	%bx
	incw	%bx
	loop	1b

	cmpb	$0,(%si)
	jnz	3f

	popw	%si
	clc
	ret				// Match found

3:

	popw	%si
	addw	8(%si), %si

	jmp	ntfs_find_grldr

// Locate an attribute
// Input:
//     DI - pointer to buffer
//     AL - attribute
ntfs_locate_attr:
	call	ntfs_init_attr
	call	ntfs_find_attr
	jc	1f
2:
	testb	$NT_FG_ALST, nt_flag
	jnz	2f
	call	ntfs_find_attr
	jnc	2b
	call	ntfs_init_attr
	call	ntfs_find_attr
2:
	clc
1:
	ret

// Prepare to find attribute
// Input:
//     DI - pointer to buffer
ntfs_init_attr:
	pushw	%ax
	xorw	%ax, %ax
	movw	%ax, nt_flag
	movw	%ax, nt_attr_end
	movw	nt_attr_ofs, %ax
	addw	%di, %ax
	movw	%ax, nt_attr_nxt
	popw	%ax
	cmpw	$MMFT_BASE, %di
	jnz	1f
	orb	$NT_FG_MMFT, nt_flag
1:
	ret

// Find an attribute
// Input:
//     DI - pointer to buffer
//     AL - attribute
// Output:
//     SI - current item
//     CF - status
ntfs_find_attr:
	movw	nt_attr_nxt, %bx
	testb	$NT_FG_ALST, nt_flag
	jnz	6f
1:
	movw	%bx, %si
	cmpb	$0xFF, (%si)
	jz	3f

	cmpb	$AT_ATTRIBUTE_LIST, (%si)
	jnz	2f
	movw	%si, nt_attr_end
2:
	addw	4(%bx), %bx
	cmpb	%al, (%si)
	jnz	1b
	movw	%bx, nt_attr_nxt
	movw	%si, nt_attr_cur
2:
	ret
3:
	cmpw	$1, nt_attr_end
	jb	2b
	movw	nt_attr_end, %si
	cmpb	$0, 8(%si)
	jnz	4f
	movw	%si, %bx
	addw	0x14(%bx), %bx
	addw	4(%si), %si
	jmp	5f
4:
	movl	0x28(%si), %ecx
	shrl	$9, %ecx
	cmpw	$8, %cx
	ja	NTFS_Corrupt_Error
	leaw	nt_edat_buf, %bx
	pushw	%ax
	xorl	%edx, %edx
	call	ntfs_read_data
	popw	%ax
	jc	2b
	movw	0x30(%si), %si
	addw	%bx, %si
5:
	movw	%si, nt_attr_end
	orb	$NT_FG_ALST, nt_flag
	testb	$NT_FG_MMFT, nt_flag
	jz	6f
	call	ntfs_fix_mmft
6:
	movw	%bx, %si
	cmpw	nt_attr_end, %bx
	jb	1f
7:
	stc
	ret
1:
	addw	4(%bx), %bx
	cmpb	%al, (%si)
	jnz	6b

	pushw	%ax
	pushw	%es
	pushw	%ds
	popw	%es
	movw	%si, nt_attr_cur
	movw	%bx, nt_attr_nxt
	movl	0x10(%si), %eax
	leaw	nt_emft_buf, %bx
	testb	$NT_FG_MMFT, nt_flag
	jnz	2f
	call	ntfs_read_mft
	jmp	3f
2:
	pushw	%bx
	call	readDisk_nt
	movl	0x14(%si), %eax
	call	readDisk_nt
	popw	%bx
	cmpw	$0x4946, (%bx)			// "FI"
	jnz	NTFS_Corrupt_Error
	movw	$2, %cx
	call	ntfs_fixup
3:
	popw	%es
	popw	%ax
	addw	0x14(%bx), %bx
4:
	cmpb	$0xFF, (%bx)
	jz	7b
	cmpb	%al, (%bx)
	jz	5f
	addw	4(%bx), %bx
	jmp	4b
5:
	movw	%bx, %si
	ret

// Fix $MFT
// Input:
//     DI - pointer to buffer
//     BX - attr cur
ntfs_fix_mmft:
	pushw	%ax
	orb	$NT_FG_GPOS, nt_flag

1:
	cmpw	nt_attr_end, %bx
	jae	NTFS_Corrupt_Error
	cmpb	%al, (%bx)
	jz	2f
	addw	4(%bx), %bx
	jmp	1b
2:

	movw	%bx, nt_attr_cur

	movl	nt_mft_start, %eax
	movl	%eax, 0x10(%bx)
	incl	%eax
	movl	%eax, 0x14(%bx)
1:
	addw	4(%bx), %bx

	cmpw	nt_attr_end, %bx
	jae	2f
	cmpb	$AT_DATA, (%bx)
	jnz	2f

	movl	0x10(%bx), %edx
	movb	nt_mft_size, %cl
	shll	%cl, %edx

	call	ntfs_read_attr

	orl	%eax, %eax
	jz	NTFS_Corrupt_Error
	movl	%eax, 0x10(%bx)
	movl	%edx, 0x14(%bx)
	jmp	1b
2:
	movw	nt_attr_cur, %bx
	andb	$(~NT_FG_GPOS), nt_flag
	popw	%ax

	ret

// Read MFT record
// Input:
//     DS:BX - buffer
//     EAX - mft number
ntfs_read_mft:
	pushw	%di
	movw	$MMFT_BASE, %di
	movb	nt_mft_size, %cl
	shll	%cl, %eax
	movl	%eax, %edx
	movl	$1, %eax
	shll	%cl, %eax
	movl	%eax, %ecx
	call	ntfs_read_attr
	jc	NTFS_Corrupt_Error
	cmpw	$0x4946, (%bx)			// "FI"
	jnz	NTFS_Corrupt_Error
	call	ntfs_fixup
	popw	%di
	ret

// Read attribute
// Input:
//     DI - pointer to buffer
//     ES:BX - buffer
//     EDX - start sector
//     ECX - sector count
// Output:
//     CF - status
ntfs_read_attr:
	pushw	nt_attr_cur
	pushl	%edx
	pushl	%ecx
	pushw	%bx

	movw	nt_attr_cur, %si
	movb	(%si), %al

	testb	$NT_FG_ALST, nt_flag
	jz	2f
	movw	%si, %bx
	movb	nt_spc, %cl
	shrl	%cl, %edx

1:
	cmpw	nt_attr_end, %bx
	jae	2f
	cmpb	%al, (%bx)
	jnz	2f
	cmpl	%edx, 8(%bx)
	ja	2f
	movw	%bx, %si
	addw	4(%bx), %bx
	jmp	1b
2:

	movw	%si, nt_attr_nxt
	call	ntfs_find_attr

	popw	%bx
	popl	%ecx
	popl	%edx
	jc	1f
	call	ntfs_read_data
	clc
1:
	popw	nt_attr_cur
	ret

// Read data
// Input:
//     DI: pointer to buffer
//     SI: current item
//     ES:BX: buffer
//     EDX: start sector
//     ECX: sector count
ntfs_read_data:
	pushw	%cx
	pushw	%bx
	testb	$1, 8(%si)
	jz	NTFS_Corrupt_Error
	movb	0xC(%si), %al
	andb	$1, %al
	orb	%al, nt_flag

	movl	%ecx, nt_read_count
	movb	nt_spc, %cl

	movl	%edx, %eax
	shrl	%cl, %eax
	movl	%eax, nt_target_vcn
	shll	%cl, %eax
	subl	%eax, %edx
	movl	%edx, nt_vcn_offset

	xorw	%dx, %dx		// edx - next VCN
	movl	%edx, nt_curr_lcn

	movl	0x10(%si), %edx

	addw	0x20(%si), %si
1:
	call	ntfs_runlist_read_block

	cmpl	nt_target_vcn, %edx
	jbe	1b
1:
	movb	nt_spc, %cl

	orl	%eax, %eax		// sparse
	jz	2f

	movl	nt_target_vcn, %eax
	subl	nt_curr_vcn, %eax
	addl	nt_curr_lcn, %eax

	shll	%cl, %eax
	addl	nt_vcn_offset, %eax

	testb	$NT_FG_GPOS, nt_flag
	jz	3f
	pushl	%eax
	incl	%eax
	subl	nt_curr_vcn, %edx
	addl	nt_curr_lcn, %edx
	shll	%cl, %edx
	cmpl	%eax, %edx
	jnz	4f
	pushw	%cx
	call	ntfs_runlist_read_block
	popw	%cx
	movl	nt_curr_lcn, %eax
	shll	%cl, %eax
4:
	movl	%eax, %edx
	popl	%eax
	addl	(nt_part_ofs - Entry_nt)(%bp), %edx
3:

	addl	(nt_part_ofs - Entry_nt)(%bp), %eax

2:
	testb	$NT_FG_GPOS, nt_flag
	jnz	1f

	pushl	%ebx
	movl	%edx, %ebx
	subl	nt_target_vcn, %ebx
	shll	%cl, %ebx
	movl	%ebx, %ecx
	popl	%ebx

	subl	nt_vcn_offset, %ecx
	movl	$0, nt_vcn_offset
	cmpl	nt_read_count, %ecx
	jbe	2f
	movl	nt_read_count, %ecx
2:

	pushl	%ecx

	orl	%eax, %eax
	jnz	3f
	call	ntfs_sparse_block
	jmp	4f

3:
	call	readDisk_nt
	loop	3b

4:
	popl	%ecx
	subl	%ecx, nt_read_count
	jbe	1f

	movl	%edx, nt_target_vcn
	call	ntfs_runlist_read_block
	jmp	1b

1:
	popw	%bx
	popw	%cx
	ret

// Read run list data
// Input:
//     CL = number of bytes
// Output:
//     EAX = read bytes
//     SI points to the next unhandled byte

ntfs_runlist_read_data:
	pushw	%cx
	orb	%cl, %cl
	jnz	1f
	popw	%cx
	xorl	%eax, %eax
	ret
1:
	lodsb	(%si), %al
	rorl	$8, %eax
	decb	%cl
	jnz	1b

	popw	%cx
	negb	%cl
	add	$4, %cl
	shlb	$3, %cl
	ret

NTFS_Run_Overflow_Error:
	movb	$NTFS_Run_Overflow_Error_Code, %al
	jmp	NTFS_Error

// Read run list block
// Output:
//     EDX = Next VCN
//     SI points to the next unhandled byte

ntfs_runlist_read_block:
	lodsb	(%si), %al
	movb	%al, %cl
	movb	%cl, %ch
	andb	$0xF, %cl		// cl - Size of length field
	jz	1f
	shrb	$0x4, %ch		// ch - Size of offset field

	call	ntfs_runlist_read_data
	shrl	%cl, %eax

	movl	%edx, nt_curr_vcn
	addl	%eax, %edx

	movb	%ch, %cl
	call	ntfs_runlist_read_data
	sarl	%cl, %eax

	addl	%eax, nt_curr_lcn

	ret

1:
	testb	$NT_FG_ALST, nt_flag
	jz	NTFS_Run_Overflow_Error

	pushl	%edx
	pushw	%bx
	movw	nt_attr_cur, %si
	movb	(%si), %al
	call	ntfs_find_attr
	jc	NTFS_Run_Overflow_Error
	cmpb	$0, 8(%si)
	jz	NTFS_Run_Overflow_Error
	movl	$0, nt_curr_lcn
	popw	%bx
	popl	%edx
	addw	0x20(%si), %si
	jmp	ntfs_runlist_read_block

// Convert seg:ofs to linear address
// Input:
//     On stack: seg:ofs
// Output:
//     eax:
seg_to_lin:
	pushw	%bp
	movw	%sp, %bp
	xorl	%eax, %eax
	xchgw	6(%bp), %ax
	shll	$4, %eax
	addl	4(%bp), %eax
	popw	%bp
	ret	$4

// Convert linear address to seg:ofs
// Input:
//     on stack: linear address
// Output:
//     On stack: seg:ofs
lin_to_seg:
	pushw	%bp
	movw	%sp, %bp
	shll	$12, 4(%bp)
	shrw	$12, 4(%bp)
	popw	%bp
	ret

fix_segs:
	pushw	%ds
	pushw	%si
	call	seg_to_lin
	pushl	%eax
	call	lin_to_seg
	popw	%si
	popw	%ds

fix_es_di:
	pushw	%es
	pushw	%di
	call	seg_to_lin
	pushl	%eax
	call	lin_to_seg
	popw	%di
	popw	%es
	ret

// Handle sparse block
//     DI: points to buffer
//     ES:BX: points to buffer
//     ECX: number of sectors
//     EDX: next VCN

ntfs_sparse_block:
	pushw	%di
	pushl	%edx

	shll	$9, %ecx		// ecx - totel number of bytes

	testb	$1, nt_flag		// Not compressed
	jz	2f

	xorl	%edx, %edx
	movb	nt_target_vcn, %dl
	andb	$0xF, %dl
	jz	2f

	movw	%bx, %di

	pushw	%cx

	movb	nt_spc, %cl
	addb	$9, %cl
	shll	%cl, %edx		// edx: offset from the start of cluster

	push	%es
	push	%di
	call	seg_to_lin
	subl	%edx, %eax		// eax: linear address

	movl	$16, nt_remain_len
	shll	%cl, nt_remain_len

	popw	%cx

	addl	%edx, %ecx
	subl	nt_remain_len, %ecx

	pushl	%ecx
	call	ntfs_decomp_block
	popl	%ecx

	addl	nt_remain_len, %ecx

	jecxz	1f

	movw	%di, %bx

2:
	movw	%bx, %di
	movl	%ecx, %edx
	xorl	%eax, %eax
	movl	%eax, %ecx
	call	fix_es_di

3:
	movw	$0x8000, %cx
	cmpl	%edx, %ecx
	jbe	4f
	movw	%dx, %cx
4:
	pushw	%cx
	shrw	$2, %cx

	rep	stosl	%eax, %es:(%di)
	call	fix_es_di
	popw	%cx
	subl	%ecx, %edx
	jnz	3b

1:
	movw	%di, %bx

	popl	%edx
	popw	%di

	ret

// Decompress block
// Input:
//     eax: linear address at the beginning of the compressed block
// Output:
//     ES:DI: points to the end of the block
ntfs_decomp_block:
	pushw	%ds
	pushw	%si

	pushl	%eax
	call	lin_to_seg
	popw	%si
	popw	%ds
	movl	nt_remain_len, %edx
	addl	%edx, %eax
	pushl	%eax
	call	lin_to_seg
	popw	%di
	popw	%es

	pushw	%es
	pushw	%di
	pushw	%ds
	pushw	%si

	xorl	%ecx, %ecx

1:
	movw	$0x8000, %cx
	cmpl	%edx, %ecx
	jbe	2f
	movw	%dx, %cx
2:
	pushw	%cx
	shrw	$2, %cx
	rep	movsl	(%si), %es:(%di)
	call	fix_segs
	popw	%cx
	subl	%ecx, %edx
	jnz	1b

	popw	%di
	popw	%es
	popw	%si
	popw	%ds

1:
	xorl	%edx, %edx			// edx - copied bytes

	lodsw	(%si), %ax
	testb	$0x80, %ah
	jnz	2f
	movw	$0x800, %cx
	rep	movsw	(%si), %es:(%di)
	movw	$0x1000, %dx
	jmp	7f				// The block is not compressed

2:
	movw	%ax, %cx
	andw	$0xFFF, %cx
	incw	%cx				// ecx = block length
	addw	%si, %cx			// cx: end marker
	xorb	%bh, %bh

3:
	cmpw	$0x1000, %dx
	ja	NTFS_Decompress_Error

	orb	%bh, %bh
	jnz	4f
	lodsb	(%si), %al
	movb	%al, %bl			// bl: tag, bh: count
	movb	$8, %bh
4:

	testb	$1, %bl
	jz	5f

	movw	%dx, %ax
	decw	%ax

	pushw	%cx
	pushw	%bx

	movb	$12, %cl
6:
	cmpw	$0x10, %ax
	jb	6f
	shrw	$1, %ax
	decb	%cl
	jmp	6b
6:

	lodsw	(%si), %ax
	movw	%ax, %bx
	shrw	%cl, %bx			// bx: delta

	pushw	%dx
	movw	$1, %dx
	shlw	%cl, %dx
	decw	%dx
	andw	%dx, %ax
	popw	%dx

	addw	$3, %ax
	movw	%ax, %cx			// cx: length
	negw	%bx
	decw	%bx

6:
	movb	%es:(%bx, %di), %al
	stosb	%al, %es:(%di)
	incw	%dx
	loop	6b

	popw	%bx
	popw	%cx
	jmp	4f

5:
	movsb	(%si), %es:(%di)
	incw	%dx
4:
	shrb	$1, %bl
	decb	%bh

	cmpw	%cx, %si
	jb	3b

7:
	call	fix_segs

	subl	%edx, nt_remain_len	// End of block
	jz	1f

	cmpw	$0x1000, %dx
	je	1b

1:

	popw	%si
	popw	%ds
	ret

NTFS_Decompress_Error:
	pushw	%ss
	popw	%ds
	movb	$NTFS_Decompress_Error_Code, %al
	jmp	NTFS_Error

/*
do_pause:
	.byte	0

pause:
	cmpb	$0, (do_pause - Entry_nt)(%bp)
	jnz	1f
	ret
1:
	xorw	%bp, %bp
1:
	jmp	1b
*/

/*
hex_out:
	pushw	%bp
	movw	%sp, %bp
	pushaw
	movb	$0xE, %ah
 	movw	$7, %bx
	movw	$4, %cx
	movw	4(%bp), %dx
1:
	rol	$4, %dx
	movb	%dl, %al
	andb	$0xF, %al
	cmpb	$10, %al
	jb	2f
	subb	$('0'-'A'+10), %al
2:
	addb	$'0', %al
	int	$0x10
	loop	1b
	movb	$' ', %al
	int	$0x10
	popaw
	popw	%bp
	ret	$2
*/

	. = Entry_nt + 0x7fc

nt_sector_mark:
	.long	0x42555247		// "GRUB"
//----------------- end  of "ntfsbs.S"-----------------------

	. = _start1 + 0x1200

	.arch	i586, jumps

#ifdef DEBUG

	. = Entry_ext2 + 0x201

debug_print:

	pushfl
	pushal
	movl	%eax, %ebp
	call	2f
#if 0
	popal
	pushal
	movl	%ebx, %ebp
	call	2f
	popal
	pushal
	movl	%ecx, %ebp
	call	2f
	popal
	pushal
	movl	%edx, %ebp
	call	2f
	popal
	pushal
	movl	%esi, %ebp
	call	2f
	popal
	pushal
	movl	%edi, %ebp
	call	2f
	popal
	popfl
	pushfl
	pushal
	pushfl
	popl	%ebp		/* flags */
	call	2f
	movw	%ds, %bp
	shll	$16, %ebp
	movw	%es, %bp
	call	2f
	movw	$0x0e0d, %ax	/* print CR */
	int	$0x10		/* via TTY mode */
	movw	$0x0e0a, %ax	/* print LF */
	int	$0x10		/* via TTY mode */
#endif
	popal
	popfl
	ret
2:
	movw	$7, %cx
1:
	xorw	%bx, %bx	/* video page 0 */
	movl	%ebp, %eax
	shrl	%cl, %eax
	shrl	%cl, %eax
	shrl	%cl, %eax
	shrl	%cl, %eax
	andb	$0x0f, %al
	addb	$0x30, %al
	movb	$0x0e, %ah	/* print char in AL */
	int	$0x10		/* via TTY mode */

	decw	%cx
	testw	%cx, %cx
	jns	1b

	movw	$0x0e20, %ax	/* print space */
	int	$0x10		/* via TTY mode */
	ret
#endif

#if 1
	/* restore GRLDR_CS */

	/* this code is executed at 0000:MONITOR, which must be a 16-byte
	 * aligned address. The address 0000:MONITOR should be designed in
	 * a way that could avoid memory confliction with volume boot records
	 * (currently FAT12/16/32/NTFS/EXT2/3 are built in).
	 */

	/* CS=code */

	.align 16

restore_GRLDR_CS:
2:
	call	1f
1:
	popw	%bx		# instruction pointer of 1b
	movw	%cs, %ax
	shrw	$4, %bx
	addw	%ax, %bx	# BX=segment value of this code
	pushw	%bx
	pushw	$(1f - 2b)
	lret
1:
	/* modify gdt base */
	xorl	%eax, %eax
	movw	%bx, %ax
	shll	$4, %eax
	addl	$(gdt -2b), %eax
	movl	%eax, %cs:(gdt - 2b + 2)
	
	movw	$GRLDR_CS, %bx
	movw	%bx, %es
	movw	%ds, %bx	# save old DS to BX

	cli
	lgdt	%cs:(gdt - 2b)
	movl	%cr0, %eax
	orb	$1, %al
	movl	%eax, %cr0

	movw	$8, %si
	movw	%si, %ds

	xorl	%esi, %esi
	xorl	%edi, %edi
	movl	$(0x9000 / 4), %ecx

	cld
	repz movsl

	movw	$16, %si
	movw	%si, %ds

	andb	$0xfe, %al
	movl	%eax, %cr0

	movw	%bx, %ds	# restore DS from BX

	ljmp	$GRLDR_CS, $(try_next_partition - _start1)

#endif

# Descriptor tables
#
# NOTE: The intel manual says gdt should be sixteen bytes aligned for
# efficiency reasons.  However, there are machines which are known not
# to boot with misaligned GDTs, so alter this at your peril!  If you alter
# GDT_ENTRY_BOOT_CS (in asm/segment.h) remember to leave at least two
# empty GDT entries (one for NULL and one reserved).
#
# NOTE:	On some CPUs, the GDT must be 8 byte aligned.  This is
# true for the Voyager Quad CPU card which will not boot without
# This directive.  16 byte aligment is recommended by intel.
#
	.align 16
gdt:
	/* this is the default null entry in GDT */
	.word	gdt_end - gdt - 1		# gdt limit
	.long	(GRLDR_CS * 16 + gdt - _start1)	# linear address of gdt
	.word	0				# pad 2 bytes

	/* real mode data segment base=0x200000 */
	.word	0xFFFF, 0
	.byte	0x20, 0x92, 0, 0

	/* real mode data segment base=0 */
	.word	0xFFFF, 0
	.byte	0, 0x92, 0, 0

gdt_end:

helper_start:

	/* helper function begins here
	 * before the call:
	 *	CF=1		: indicates an invalid or corrupt entry
	 *	CF=0		: indicates a valid entry
	 *
	 * on return:
	 * 	CF=1		: means "below", try next entry
	 *	CF=0,ZF=1	: means "equal", helper did nothing, so we need
	 *			  a further try to boot via NT bootsector
	 *	CF=0,ZF=0	: means "above", helper succeeded, boot it now
	 */

	sti

	/* DS=SS=0x9400 */
	pushw	%cs
	popw	%ds

	pushw	$FS_BOOT
	popw	%es

	/* ES=FS_BOOT */

	/* Format of partition information blocks.
	 *
	 * Offset   Length in bytes	Field
	 *  00h		1		Set to 80h if this partition is active.
	 *  01h		1		Partition's starting head.
	 *  02h		2		Partition's starting sector and track.
	 *  04h(SI)	1		Partition's ID number.
	 *  05h		1		Partition's ending head.
	 *  06h		2		Partition's ending sector and track.
	 *  08h		4		Starting LBA.
	 *  0Ch		4		Partition's length in sectors.
	 */

	pushw	%ds		/* DS=0x9400 */
	pushw	%es		/* ES=FS_BOOT */
	pushal
	pushfw

#if (defined(GRLDR_MBR)) || (defined(GRLDR_INSTALL))
	testb	$0x0C, %cs:0x02
	jz	1f

	pushw	%es		/* ES=FS_BOOT */
	/* set the DUCE indicator */
	xorw	%ax, %ax
	movw	%ax, %es
	movw	$0x5FC, %di
	movl	$0x45435544, %eax
	movb	%cs:0x02, %al
	andb	$0x0C, %al
	orb	$0x40, %al
	stosl
	popw	%es
1:
	testb	$0x80, %cs:0x02	/* boot previous MBR first? */
	jnz	2f		/* no, continue to find GRLDR */

	/* yes, call the routine for booting the previous MBR.
	 * it will not return on success.
	 * on failure, it will return here
	 */

	/* before we call the routine, we will check if the user want to
	 * skip this step and continue to find the GRLDR
	 */
	/* if timeout==0, don't display the message */
  	cmpb    $0, %cs:0x03
  	je      1f
	/* Press ... to start GRUB, any other key to boot previous MBR ... */
	movw	$(press_hot_key_pre - _start1), %si
	call	print_message
	movw	$(press_hot_key_name - _start1), %si
	call	print_message
	movw	$(press_hot_key_sub - _start1), %si
	call	print_message
1:
	call	sleep_5_seconds
	jc	3f		/* desired hot-key pressed */
	call	boot_prev_mbr	//Error_modify
	jmp	3f
2:
	/* before we find GRLDR, give the user a chance to boot_prev_mbr. */
	/* if timeout==0, don't display the message */
  	cmpb    $0, %cs:0x03
  	je      1f
	/* Press ... to boot previous MBR, any other key to start GRUB ... */
	movw	$(press_hot_key_pre - _start1), %si
	call	print_message
	movw	$(press_hot_key_name - _start1), %si
	call	print_message
	movw	$(press_hot_key_sub1 - _start1), %si
	call	print_message
1:
	call	sleep_5_seconds
	jnc	3f
	/* desired hot-key pressed */
	andb	$0x7F, %cs:0x02
	call	boot_prev_mbr	//Error_modify
3:
	/* modify code at 2b to be "jmp 4f" */
	movw	$(0xEB | ((4f - 2b - 2) << 8)), (2b - _start1)
	orb	$0x80, %cs:0x02
4:
#endif
	popfw
	popal
	popw	%es
	popw	%ds

	pushw	%ds		/* DS=0x9400 */
	pushw	%es		/* ES=FS_BOOT */
	pushal
	pushfw

	//cmpb	$0x0e, 0x00	/* EBIOS previously checked OK? */
	//jbe	1f		/* yes, skip the check */
	movb	$0x02, 0x00	/* initialise this byte to 0x02 */
	movb	$0x41, %ah	/* EBIOS check existence */
	movw	$0x55aa, %bx
//	int	$0x13
	call	int13
	jc	1f		/* No EBIOS */
	cmpw	$0xaa55, %bx
	jnz	1f		/* No EBIOS */
	testb	$1, %cl
	jz	1f		/* No EBIOS */
	movb	$0x42, 0x00	/* LBA supported, save 0x42 to 9400:0000 */
1:
	popfw
	popal
	popw	%es
	popw	%ds

	pushw	%ds		/* DS=0x9400 */
	pushw	%es		/* ES=FS_BOOT */
	pushal
	pushfw

	pushaw
	cmpw	$0x1c2, %si
	jne	1f

	/* initialize partition number and partition entries end */
	movw	$0xffff, 0x1bc		/* hd partition number */
	movw	$0x01fe, 0x1ba		/* partition entries end */
1:
	pushw	%dx

	pushw	%ds
	pushw	%es
	call	geometry_tune
	popw	%es
	popw	%ds

	testb	%dl, %dl
	jns	1f			/* floppy, use normal CHS mode */

	cmpw	$0x1f2, %si		/* is it a primary partition? */
	ja	2f			/* no, it is an extended partition */
	movl	4(%si), %eax
	movl	%eax, 8(%si)		/* parent part_start saved here */
	xorl	%eax, %eax
	movl	%eax, 4(%si)		/* current part_start(0) saved here */
2:
	//movl	-4(%si), %eax
	//cmpl	$0xfffffe00, %eax	/* check the starting CHS */
	//jb	1f			/* use normal CHS mode */

	cmpw	$0xFFFF, 0x08		/* geometry determined? */
	jne	3f			/* yes, skip */

	/* get CHS total number of sectors */
	pushw	%es
	pushw	%ds
	movb	$8, %ah		/* read drive parameters changes DX,ES,DI,BX */
	//movb	$0x80, %dl	/* BIOS drive number is in DL */
	int	$0x13
	popw	%ds
	popw	%es
	jc	3f		/* failed */
	testb	$63, %cl
	jz	3f		/* failed */

	movb	%dh, 0x09	/* Hmax */
	andb	$63, %cl	/* sectors per track */
	movb	%cl, 0x08	/* Smax */

3:

	/* check the partition's starting LBA */
	movl	4(%si), %ebx
	addl	8(%si), %ebx	/* EBX=start_LBA */

	testl	%ebx, %ebx
	je	1f

	cmpb	$0x42, 0x00	/* EBIOS present? */
	je	3f		/* yes, skip the CHS mode int13 fix */

	/******************************************************************/
	/* read the boot sectors once again using CHS translated from LBA */
	/******************************************************************/

	movw	$0, %bp
	movw	0x08, %ax
	movw	%ax, %dx
	movb	$0, %dh
	movw	%dx, 0x18	/* sectors per track in 0x18(%bp) */
	movb	%ah, %dl
	incw	%dx
	movw	%dx, 0x1a	/* number of heads in 0x1a(%bp) */

	popw	%ax		/* AX=orig DX which holds drive number DL */
	pushw	%ax

	movb	%al, 0x24	/* drive number in 0x24(%bp) */

	pushw	(jc_code_begin - _start1)	/* save original code */

	/* modify jc_code_begin in readDisk_12_16 */
	movw	$(0xEB | ((jc_code_end - jc_code_begin - 2) << 8)), (jc_code_begin - _start1)

	pushw	%es
	pushl	%ebx

	pushl	%ebx
	popw	%ax
	popw	%dx		/* DX:AX=LBA */

	xorw	%bx, %bx	/* buffer in ES:BX=FS_BOOT:0 */
	movw	$63, %cx	/* number of sectors to read in CX */

	call	readDisk_12_16	/* CX=0, AX,DX,ES changed */

	popl	%ebx
	popw	%es

	popw	(jc_code_begin - _start1)	/* restore original code */

	/*******************************************************/
	/* load partition boot track to FS_BOOT using LBA mode */
	/*******************************************************/

	cmpb	$0x42, 0x00	/* EBIOS present? */
	jne	1f		/* no, skip the LBA mode int13 call */
3:
	popw	%ax		/* AX=orig DX which holds drive number DL */
	pushw	%ax
	xorw	%dx, %dx
	pushw	%dx		/* DX=0, higher 4 bytes of starting LBA */
	pushw	%dx
//	pushl	%edx		/* EDX=0, higher 4 bytes of starting LBA */
	pushl	%ebx		/* lower 4 bytes of starting LBA */
	pushw	%es		/* ES=FS_BOOT */
	pushw	%dx		/* DX=0, ES:0 is the buffer */
	//pushl	$0x003f0010	/* transfer 63 sectors */
	pushw	$0x3f		/* transfer 63 sectors */
	pushw	$0x10		/* size of disk address packet */
	xchgw	%ax, %dx	/* restore drive number DL from AL */
	movb	$0x42, %ah	/* extended read */
	movw	%sp, %si	/* DS:SI points to disk address packet */
//	int	$0x13		/* ignore the read failure */
	call	int13
	popaw			/* adjust the stack */
	jc	1f
	popw	%dx
	popaw

	//popw	%ax		/* discard flags in the stack */
	popfw
	clc

	pushfw			/* push new flags with CF=0 */
	pushaw
	pushw	%dx
1:
	popw	%dx
	popaw

	popfw
	popal
	popw	%es
	popw	%ds

	pushw	%ds		/* DS=0x9400 */
	pushw	%es		/* ES=FS_BOOT */
	pushal
	pushfw

	pushw	%si

	pushfw
	pushw	%es
//---------------------------------------------------------
	/* print "Try (hd0,n): " or "Try (fd0): "*/
	pushw	%ds
	popw	%es		/* ES=DS=CS=0x9400 */

	cld			/* for stosb */
	xorw	%ax, %ax
	testb	%dl, %dl
	jns	1f		/* floppy */
	/* hard drive */
#if 0
	movw	%si, %ax
	subw	$0x1c2, %ax
	shrw	$4, %ax
	cmpw	$0x1fe, %si	/* is in MBR? */
	jb	1f		/* yes */
	/* no, it is an entry in an extended partition */
	movb	$0xFC, (add_sub_si + 2 - _start1)	/* addw	$-4, %si */
	incw	0x1bc		/* logical partition number */
	movb	0x1bc, %al
#else
	incw	0x1bc		/* logical partition number */
	movw	0x1bc, %ax
	cmpb	$4, %al
	jb	1f
	movb	$0xFC, (add_sub_si + 2 - _start1)	/* addw	$-4, %si */
#endif
1:
	/* AL=partition number, AH=0 */
	pushw	%ax

	movw	$(partition_message - _start1 + 7), %di	/* drive type */
	movb	%dl, %al
	shrb	$7, %al		/* drive type: floppy=0, harddrive=1 */
	shlb	$1, %al
	addw	$0x6466, %ax	/* "fd" or "hd" */
	stosw
	movb	%dl, %al
	andb	$0x7f, %al	/* drive number */
	aam		/* convert binary to decimal, AH=high, AL=low */
	testb	%ah, %ah
	jz	1f
	addb	$0x30, %ah
	movb	%ah, (%di)
	incw	%di
1:
	addb	$0x30, %al
	stosb

	popw	%ax

	testb	%dl, %dl
	jns	2f		/* floppy */
	/* this is a hard drive, the partition number is in AL */
	movb	$0x2c, (%di)	/* "," */
	incw	%di
	aam		/* convert binary to decimal, AH=high, AL=low */
	testb	%ah, %ah
	jz	1f
	addb	$0x30, %ah
	movb	%ah, (%di)
	incw	%di
1:
	addb	$0x30, %al
	stosb
2:
	movl	$0x00203a29, (%di)	/* "): \0" */

	movw	$(partition_message - _start1), %si
	call	print_message	/* CS:SI points to message string */
//---------------------------------------------------------
	popw	%es
	popfw
	//stc
	jc	invalid_or_null		/* invalid or null entry */

	xorw	%si, %si
	pushw	%es
	popw	%ds

	/* DS=ES=FS_BOOT */

	/* First, check for ext2 filesystem */

	cmpw	$0xEF53, 0x438		/* Magic signature */
	jnz	1f
	xorl	%eax, %eax
	cmpl	%eax, 0x400		/* s_inodes_count */
	jz	1f
	cmpl	%eax, 0x404		/* s_blocks_count */
	jz	1f
//	cmpw	%ax, 0x458		/* s_inode_size, usually 0x80 */
//	jz	1f
	cmpl	%eax, 0x420		/* s_blocks_per_group */
	jz	1f
	cmpl	%eax, 0x428		/* s_inodes_per_group */
	jz	1f
	movl	0x414, %eax		/* s_first_data_block */
	movw	%ax, %bx		/* BX=1 for 1K block, 0 otherwise */
	shrl	$1, %eax		/* must be 0 */
	jnz	1f
	movl	0x418, %ecx		/* s_log_block_size */
	cmpl	$4, %ecx		/* max size of block is 16K */
	ja	1f
	negw	%cx			/* CF=0 for 1K block, CF=1 otherwise */
	adcw	%ax, %bx		/* EAX=0 */
	decw	%bx
	jnz	1f

	/* BX = 0 */
	/* EAX= 0 */

	movw	$0x80, %ax		/* EXT2_GOOD_OLD_INODE_SIZE */
	movw	%ax, %cs:0x826		/* inode size */
	movl	0x44C, %ecx		/* ECX=s_rev_level */
	jecxz	3f			/* EXT2_GOOD_OLD_REV */
	movw	0x458, %ax		/* AX=s_inode_size */
	testw	%ax, %ax
	jz	1f			/* invalid inode size */
	pushw	%ax
	pushw	%dx
	movb	0x418, %cl		/* s_log_block_size */
	addb	$10, %cl
	xorw	%dx, %dx		/* DX=0 */
	incw	%dx			/* DX=1 */
	shlw	%cl, %dx		/* DX=block size in bytes */
	xchgw	%ax, %cx		/* CX=s_inode_size */
	xchgw	%ax, %dx		/* AX=block size in bytes */
	xorw	%dx, %dx		/* DX:AX=block size in bytes */
	divw	%cx			/* quo=AX, rem=DX */
	testw	%dx, %dx
	popw	%dx
	popw	%ax
	jnz	1f			/* invalid inode size */
	movw	%ax, %cs:0x826		/* inode size */
3:
	/* BX = 0 */

	/* super block is sane */

	//pushw	%cs
	//popw	%ds
	///* DS=SS=0x9400 */
	///* ES=FS_BOOT */
	cld
	movw	$0x800, %si
	xorw	%di, %di
	movw	$0x0200, %cx	/* yes, we need 2 sectors if enable debug */

	repz cs movsw		/* CS segment override prefix(=0x2E) */

	/* modify the boot partition number */

	/* the boot partition number is at offset 0x25 for ext2 */

	testb	%dl, %dl
	jns	3f			/* no modification for floppy */
	movw	$0x25, %di
	movw	%cs:0x1bc, %ax		/* partition number */
	stosb
3:
	/* fix for ext2 partition: hidden_sectors, offset 0x1c */
	popw	%si			/* DI points to old entry in MBR */
	pushw	%si
	xorl	%eax, %eax		/* let hidden_sectors=0 for floppy */
	testb	%dl, %dl
	jns	3f			/* floppy */
	movl	%cs:4(%si), %eax
	addl	%cs:8(%si), %eax
3:
	/* BX = 0 */

	movl	%eax, %es:0x1c(%bx)	/* adjust hidden_sectors for EXT2 */

	/* fix for ext2 partition: EBIOS indicator, offset 0x02 */

	movb	%cs:0x00(%bx), %al
	movb	%al, %es:0x02(%bx)

	/* fix for ext2 partition: sectors per block, offset 0x0d */
	/* fix for ext2 partition: bytes per block, offset 0x0e */
	/* fix for ext2 partition: dwords per block(dpb), offset 0x14 */
	/* fix for ext2 partition: square of dpb, offset 0x10 */

	movb	%es:0x418, %cl		/* s_log_block_size */
	//incw	%cx
	movl	$2, %eax
	shlw	%cl, %ax
	movb	%al, %es:0x0d(%bx)
	shlw	$9, %ax			/* block size is word wide */
	movw	%ax, %es:0x0e(%bx)
	shrw	$2, %ax
	movl	%eax, %es:0x14(%bx)
	addb	$8, %cl
	shll	%cl, %eax
	movl	%eax, %es:0x10(%bx)


	/* fix for ext2 partition: sectors per track, offset 0x18 */
	/* fix for ext2 partition: number of heads, offset 0x1a */
	movw	%cs:0x08(%bx), %ax	/* BX=0 */
	cmpw	$0xFFFF, %ax
	jz	3f
	movb	%al, %es:0x18(%bx)
	shrw	$8, %ax
	incw	%ax
	movw	%ax, %es:0x1a(%bx)
3:

#if 0
	testb	%dl, %dl
	jns	3f			/* floppy */
	popw	%di			/* DI points to old entry in MBR */
	pushw	%di
	movw	%cs:1(%di), %ax
	andb	$63, %ah
	movb	%ah, %es:0x18
	xorb	%ah, %ah
	incw	%ax
	movw	%ax, %es:0x1a
3:
#endif
	
	/* fix for ext2 partition: s_inodes_per_group, offset 0x28 */
	movl	%es:0x428, %eax		/* s_inodes_per_group */
	movl	%eax, %es:0x28(%bx)

	/* fix for ext2 partition: block number for group descriptors, offset 0x2c */
        /* At which block the group descriptors begin? */
	movl	%es:0x414, %eax		/* s_first_data_block */
	incw	%ax
	movl	%eax, %es:0x2c(%bx)

	/* fix for ext2 partition: on error go back to supervisor, offset 0x01fc */
	movw	$0x01fc, %si
	movw	%si, %di
	lodsw
	cmpw	$0xFEEB, %ax /* EB FE is jmp back to itself(infinite loop) */
	jnz	3f
	decw	%ax		/* AL=0xEA, ljmp */
	stosb
	//movw	$(try_next_partition - _start1), %ax
	movw	$MONITOR, %ax
	stosw
	//movw	%cs, %ax	/* AX=0x9400 */
	xorw	%ax, %ax
	stosw			/* the last byte 0x00 is in the next sector! */
//	addw	$0x0f, %di
//	movw	$(restore_GRLDR_CS - _start1), %si
//	movw	$((gdt_end - restore_GRLDR_CS) / 4), %cx
//	.byte	0x2e		/* %cs: prefix */
//	repz movsl
3:

	movw	$(EXT2_message - _start1), %si
	call	print_message	/* CS:SI points to message string */

	clc
	jmp	move_entries_and_return

1:
	#; It is not EXT2. Check for FAT12/16/32/NTFS.

	/* DS=ES=FS_BOOT */

	cmpw	$0x200, 0x0b(%si)	/* bytes per sector */
	jne	1f			/* not a normal BPB */
	movb	0x0d(%si), %al		/* sectors per cluster */
	testb	%al, %al
	jz	1f			/* invalid if = 0 */
	movb	%al, %cl
	movw	$128, %ax
	divb	%cl			/* quo=AL, rem=AH */
	testb	%ah, %ah
	jnz	1f			/* invalid if not 2^n */
	movw	0x18(%si), %ax		/* sectors per track */
	testw	%ax, %ax
	jz	1f			/* invalid if = 0 */
	cmpw	$63, %ax
	ja	1f			/* invalid if > 63 */
	movw	0x1a(%si), %ax		/* number of heads */
	decw	%ax			/* Max head number, should be a byte */
	testb	%ah, %ah		/* should be 0 */
	jnz	1f			/* invalid if number of heads > 256 */
	cmpb	$0xf0, 0x15(%si)	/* media descriptor */
	jb	1f

	cmpb	$0x42, %cs:0x00		/* EBIOS present? */
	jne	3f
	//movb	$0x41, %ah		/* EBIOS check existence */
	//movw	$0x55aa, %bx
	//int	$0x13
	//jc	3f			/* No EBIOS */
	//cmpw	$0xaa55, %bx
	//jnz	3f			/* No EBIOS */
	//testb	$1, %cl
	//jz	3f			/* No EBIOS */
	movb	$0x0e, 0x02(%si)	/* force LBA */
3:
	cld
	movw	$0x0600, %bx		/* FAT12/FAT16 */
	movw	$0x003c, %cx		/* FAT12/FAT16 */

	movb	0x10(%si), %al		/* number of FATs(NTFS:0, FAT:1,2) */
	cmpb	$2, %al
	ja	1f			/* abnormal FAT */
	movw	0x11(%si), %ax		/* max root entries */
	testw	%ax, %ax
	jnz	2f			/* FAT12/FAT16 */

	/* FAT32 or NTFS */
	movw	0x13(%si), %ax		/* total sectors(small) */
	testw	%ax, %ax
	jnz	1f			/* invalid FAT32 BPB */
	movw	0x16(%si), %ax		/* sectors per FAT(small) */
	testw	%ax, %ax
	jnz	1f			/* invalid FAT32 BPB */
	movb	0x10(%si), %al		/* number of FATs(NTFS:0, FAT:1,2) */
	testb	%al, %al
	jz	8f

	/* FAT32 */
	movl	0x20(%si), %eax		/* FAT32 total sectors */
	testl	%eax, %eax
	jz	1f
	movl	0x24(%si), %eax		/* FAT32 sectors per FAT */
	testl	%eax, %eax
	jz	1f
	movw	$0x0400, %bx		/* FAT32 */
	movw	$0x0058, %cx		/* FAT32 */
	movw	$(FAT32_message - _start1), %si	/* SI changed!! */
	jmp	7f
8:
	/* NTFS */
	movl	0x20(%si), %eax		/* FAT32 total sectors */
	testl	%eax, %eax
	jnz	1f
	//movw	0x11(%si), %ax		/* max root entries */
	//testw	%ax, %ax
	//jnz	1f
	movw	0x0e(%si), %ax		/* reserved sectors */
	testw	%ax, %ax
	jnz	1f

	/* fix for ntfs partition: sectors per track, offset 0x18 */
	/* fix for ntfs partition: number of heads, offset 0x1a */
	movw	%cs:0x08(%si), %ax	/* SI=0 */
	cmpw	$0xFFFF, %ax
	jz	3f
	movb	%al, 0x18(%si)		/* DS=ES */
	shrw	$8, %ax
	incw	%ax
	movw	%ax, 0x1a(%si)
3:
	/* BUG fix for extended NTFS partition */
	popw	%si			/* SI points to old entry in MBR */
	pushw	%si
	xorl	%eax, %eax		/* let hidden_sectors=0 for floppy */
	testb	%dl, %dl
	jns	3f			/* floppy */
	movl	%cs:4(%si), %eax
	addl	%cs:8(%si), %eax
3:
	movl	%eax, 0x1c		/* adjust hidden_sectors for NTFS */

	movb	%dl, 0x24		/* adjust drive number for NTFS */

#if 1
	// Load NTFS using internal boot sector at 0xA00

	movw	$(NTFS5_message - _start1), %si
	call	print_message	/* CS:SI points to message string */

	movw	$0xA00, %bx
	movw	$0x52, %cx

	pushw	%cs
	popw	%ds

	/* DS=SS=0x9400 */
	/* ES=FS_BOOT */

	movw	%bx, %si
	xorw	%di, %di
	lodsw
	stosw
	addw	%cx, %si
	addw	%cx, %di
	movw	$0x800, %cx
	subw	%di, %cx

	repz movsb

	/* modify the boot partition number */
	movb	%es:1, %al
	addb	$5, %al			/* AL is less than 0x80 */
	cbw				/* AH=0 */
	xchgw	%ax, %di		/* move AX to DI */
	movb	$0xff, %al		/* partition=whole drive for floppy */
	testb	%dl, %dl
	jns	3f			/* no modification for floppy */
	movb	0x1bc, %al		/* partition number */
3:
	stosb

	/* fix for NTFS partition: on error go back to supervisor, offset 0x01fa */

	movw	$0x01fa, %di
	movw	%es:(%di), %ax
	cmpw	$0xFEEB, %ax /* EB FE is jmp back to itself(infinite loop) */
	jnz	3f
	decw	%ax		/* AL=0xEA, ljmp */
	stosb
	//movw	$(try_next_partition - _start1), %ax
	movw	$MONITOR, %ax
	stosw

	//movw	%cs, %ax	/* AX=0x9400 */
	xorw	%ax, %ax
	stosw			/* DI=0x01ff */
3:
	clc
	jmp	move_entries_and_return

#else

	/* modify the boot partition number */
	movb	$0xB6, %al		/* 0xB6="MOV DH,imm8" */
	movb	%cs:0x1bc, %ah
	testb	%dl, %dl
	js	3f
	movb	$0xff, %ah /* partition number for floppy is whole drive */
3:
	/* before the call:
	 *		AH= partition number
	 *		AL= 0xB6	; 0xB6 is opcode of "MOV DH,imm8"
	 *		DL= drive number
	 *
	 * on return:	CF=0 if there is NTFS boot record;
	 *		CF=1 otherwise. 
	 *		CF of flags_orig on the stack will set if CF=1
	 */

	call	modify_NTFS_boot_record
	//jnc	move_entries_and_return
	//movw	$(NTFS5_message - _start1), %si
	////jmp	4f
	//call	print_message	/* CS:SI points to message string */
	//stc
	jmp	move_entries_and_return

#endif

2:
	/* FAT12/FAT16 */
	xorw	%si, %si
	movb	0x10(%si), %al		/* number of FATs(NTFS:0, FAT:1,2) */
	testb	%al, %al
	jz	1f
	movw	0x16(%si), %ax		/* sectors per FAT(small) */
	testw	%ax, %ax
	jz	1f
	movw	$(FAT16_message - _start1), %si
	cmpw	$12, %ax
	ja	7f
	movw	$(FAT12_message - _start1), %si
7:
	pushw	%bx
	pushw	%cx
	call	print_message	/* CS:SI points to message string */
	popw	%cx
	popw	%bx

	/* fix for fat 12/16/32: sectors per track, offset 0x18 */
	/* fix for fat 12/16/32: number of heads, offset 0x1a */
	xorw	%si, %si
	movw	%cs:0x08(%si), %ax	/* SI=0 */
	cmpw	$0xFFFF, %ax
	jz	3f
	movb	%al, 0x18(%si)		/* DS=ES */
	shrw	$8, %ax
	incw	%ax
	movw	%ax, 0x1a(%si)
3:
	/* BUG fix for extended FAT12/16/32 partition */
	popw	%di			/* DI points to old entry in MBR */
	pushw	%di
	xorl	%eax, %eax		/* let hidden_sectors=0 for floppy */
	testb	%dl, %dl
	jns	3f			/* floppy */
	movl	%cs:4(%di), %eax
	addl	%cs:8(%di), %eax
3:
	movl	%eax, 0x1c		/* adjust hidden_sectors for FAT */

	pushw	%cs
	popw	%ds
	/* DS=SS=0x9400 */
	/* ES=FS_BOOT */
	movw	%bx, %si
	xorw	%di, %di
	lodsw
	stosw
	addw	%cx, %si
	addw	%cx, %di
	movw	$0x0200, %cx
	subw	%di, %cx
	repz movsb
	/* modify the boot partition number */
	movb	%es:1, %al
	addb	$5, %al			/* AL is less than 0x80 */
	cbw				/* AH=0 */
	xchgw	%ax, %di		/* move AX to DI */
	movb	$0xff, %al		/* partition=whole drive for floppy */
	testb	%dl, %dl
	jns	3f			/* no modification for floppy */
	movb	0x1bc, %al		/* partition number */
3:
	stosb

	/* fix for FAT12/16/32 partition: on error go back to supervisor, offset 0x01fa */
	//pushw	%es
	//popw	%ds
	movw	$0x01fa, %di
	//movw	%di, %si
	//lodsw
	movw	%es:(%di), %ax
	cmpw	$0xFEEB, %ax /* EB FE is jmp back to itself(infinite loop) */
	jnz	3f
	decw	%ax		/* AL=0xEA, ljmp */
	stosb
	//movw	$(try_next_partition - _start1), %ax
	movw	$MONITOR, %ax
	stosw
	//movw	%cs, %ax	/* AX=0x9400 */
	xorw	%ax, %ax
	stosw			/* DI=0x01ff */
3:

	clc
	jmp	move_entries_and_return
1:
	#; It is not FAT12/16/32/NTFS. Check for extended partition.

	/* DS=ES=FS_BOOT */

	pushw	%cs
	popw	%es

	/* ES=SS=0x9400 */
	/* DS=FS_BOOT */

	popw	%si
	pushw	%si
	cmpb	$0x05, %es:(%si)	/* extended */
	je	1f
	cmpb	$0x0f, %es:(%si)	/* Win95 extended (LBA) */
	je	1f
	cmpb	$0x15, %es:(%si)	/* hidden extended */
	je	1f
	cmpb	$0x1f, %es:(%si)	/* hidden win95 extended (LBA) */
	je	1f
	cmpb	$0x85, %es:(%si)	/* Linux extended */
	je	1f
	movw	$(non_MS_message - _start1), %si
4:
	call	print_message	/* CS:SI points to message string */
	stc
	jmp	move_entries_and_return
1:
	/* extended partition entry */
	cmpw	$0x1fe, %si
	jb	1f
	decw	%es:0x1bc	/* count the partitions in extended zone */
1:
	movw	$(extended_message - _start1), %si
	call	print_message	/* CS:SI points to message string */
	movw	$0x1be, %si
	movw	$4, %cx
5:
	//xorl	%eax, %eax
	//cmpl	%eax, (%si)
	//jnz	2f
	movl	(%si), %eax
	cmpw	2(%si), %ax	/* Is EAX high word equal to AX? */
	jnz	2f
	cmpb	%al, %ah	/* Is AL=AH? */
	jnz	2f

	/* now all 4 bytes in EAX are equal to each other. */
	cmpl	%eax, 4(%si)
	jnz	2f
	cmpl	%eax, 8(%si)
	jnz	2f
	cmpl	%eax, 12(%si)
	jz	3f	/* entry with 16 dups of a byte means empty entry */
2:
	movb	(%si), %al
	shlb	$1, %al
	jnz	1f
	//jnz	3f		/* invalid entry is treated as empty entry */
	movb	2(%si), %al
	and	$63, %al	/* starting sector number */
	jz	1f
	//jz	3f		/* invalid entry is treated as empty entry */
	movb	6(%si), %al
	and	$63, %al	/* ending sector number */
	jz	1f
	//jz	3f		/* invalid entry is treated as empty entry */
	movl	8(%si), %eax	/* starting LBA */
	testl	%eax, %eax
	jz	1f
	//jz	3f		/* invalid entry is treated as empty entry */
	movl	12(%si), %eax	/* total number of sectors in partition */
	testl	%eax, %eax
	jz	1f
3:
	addw	$16, %si
	loop	5b
	cmpw	$0xaa55, (%si)
	jnz	1f

	movw	$0x1be, %si
	movw	$4, %cx
	popw	%bx	/* the old SI points to extended partition ID in MBR */
	pushw	%bx
5:
#if 1
	//xorl	%eax, %eax
	//cmpl	%eax, (%si)
	//jnz	2f
	movl	(%si), %eax
	cmpw	2(%si), %ax	/* Is EAX high word equal to AX? */
	jnz	2f
	cmpb	%al, %ah	/* Is AL=AH? */
	jnz	2f

	/* now all 4 bytes in EAX are equal to each other. */
	cmpl	%eax, 4(%si)
	jnz	2f
	cmpl	%eax, 8(%si)
	jnz	2f
	cmpl	%eax, 12(%si)
	jz	3f	/* entry with 16 dups of a byte means empty entry */
2:
	/* now it is an acceptable entry */
	movw	%es:0x1ba, %di	/* partition entries end */
	/* ensure our stack not to be overwritten by the partition entries */
	cmpw	$0x83f0, %di
	ja	3f		/* try next */
	/* ensure our code not to be overwritten by the partition entries */
	cmpw	$0x3fe, %di
	jne	6f
	/* more entries stores at 0x9be00-0x9c3ff */
	movw	$0x7e00, %di
	movw	%di, %es:0x1ba
6:
	addw	$16, %es:0x1ba	/* increment partition entries end */

	lodsl
	stosl
	lodsl
	stosl

	xchgw	%ax, %dx	/* save AL(the partition ID)to DL */

	lodsl
        xchgl	%eax, %edx	/* restore AL from DL(the partition ID), */
				/* and save EAX to EDX */
	cmpb	$0x05, %al
	je	6f
	cmpb	$0x0f, %al
	je	6f
	cmpb	$0x15, %al
	je	6f
	cmpb	$0x1f, %al
	je	6f
	cmpb	$0x85, %al
	je	6f
	/* normal partition, copied to 0x941fe-0x943fb */
	addl	%es:4(%bx), %edx	/* current partition start */
6:
	/* extended partition, copied to 0x941fe-0x943fb */
        xchgl	%eax, %edx	/* restore or update EAX from EDX */
	stosl
	lodsl				/* adjust SI only */
	movl	%es:8(%bx), %eax	/* parent partition start ... */
	stosl				/* ... stored here */
	jmp	2f
3:
	addw	$16, %si
#endif
	//. = 5b + 0x7c
2:
	loop	5b

	/* extended partition is not a normal one, so set carry to try next */
	stc
	jmp	move_entries_and_return

invalid_or_null:
1:
	movw	$(invalid_message - _start1), %si
	call	print_message	/* CS:SI points to message string */
	stc

move_entries_and_return:
	popw	%si
	pushfw
	pushw	%cs
	popw	%ds
	pushw	%cs
	popw	%es
	pushw	%si
	cmpw	$0x202, %si
	jne	1f
	/* move entries backward 1 entry */
	movw	$0x1fe, %di
	movw	$0x20e, %si
	movw	$0xf8, %cx		/* 0x1f0 bytes = 0xf8 words */
	cld				/* move upward */
	repz movsw
	movw	$0x3ee, %di
	movw	$0x7e00, %si
	movw	$0x8, %cx		/* 0x10 bytes = 0x8 words */
	cld				/* move upward */
	repz movsw
	movw	$0x7e00, %di
	movw	$0x7e10, %si
	movw	$0x2f8, %cx		/* 0x5f0 bytes = 0x2f8 words */
	cld				/* move upward */
	repz movsw
	cmpw	$0x7e10, 0x1ba
	jne	2f
	movw	$0x40e, 0x1ba
2:
	subw	$0x10, 0x1ba
	
1:
	popw	%si
	movw	$0x1fe, %ax
	movw	$(add_sub_si + 5 - _start1), %di
	movw	%ax, (%di)		/* 0x1fe */
	cmpw	$0x31b2, %si		/* floppy? */
	je	4f			/* yes */
	incw	(%di)			/* 0x1ff */
	cmpw	%ax, 0x1ba		/* AX=0x1fe */
	jne	1f
	decw	(%di)			/* 0x1fe */
	//cmpw	$0x31b2, %si		/* floppy? */
	//je	4f	// 1f		/* yes */
	cmpw	$0x1f2, %si
	ja	2f			/* logical partition */
	jb	1f			/* primary partition 0, 1, 2 */
	/* primary partition 3 */
	cmpw	$0x0003, 0x1bc		/* are there any logical partitions? */
	ja	1f			/* yes */
2:
inc_hard_drive:

	/* all partitions on the drive have been checked, try next drive.
	 *
	 * the current stack is:
	 *
	 * SP + 38	: DS
	 * SP + 36	: ES
	 * SP + 32	: EAX
	 * SP + 28	: ECX
	 * SP + 24	: EDX
	 * SP + 20	: EBX
	 * SP + 16	: ESP_temp
	 * SP + 12	: EBP
	 * SP +  8	: ESI
	 * SP +  4	: EDI
	 * SP +  2	: flags_orig
	 * SP		: flags
	 *
	 */

	/* get total hard drives */
	xorw	%ax, %ax
	movw	%ax, %ds
	movb	0x475, %dh
	pushw	%cs
	popw	%ds
//	cmpb	$16, %dh
//	jnb	2f
//	movb	$16, %dh
//2:
	orb	$0x80, %dh	/* CF=0, DH=Max harddrive number + 1 */
	//xchgw	%ax, %cx	/* CL=Max harddrive number + 1, CH=0 */
	movw	%sp, %bp
	movb	24(%bp), %dl	/* BIOS drive number is in DL */
2:
	jnc	3f
	call	print_message	/* CS:SI points to message string */
	movw	$(drive_number_string - _start1), %si
	movb	%dl, %al
	andb	$0x7f, %al
	aam			/* AH=high decimal, AL=low decimal */
	addw	$0x3030, %ax
	xchgb	%al, %ah
	movw	%ax, 32(%si)
	call	print_message	/* CS:SI points to message string */
3:
	incw	%dx		/* !!!! Next drive !!!! */
	cmpb	%dh, %dl
	jnb	2f		/* all drives checked, try floppy finally */

	pushw	%bx
	movb	$8, %ah		/* read drive parameters changes DX,ES,DI,BX */
	call	int13
	jc	3f		/* try next hard drive */
	andb	$63, %cl	/* CL=sectors per track, CF cleared */
	stc
	jz	3f		/* try next hard drive */
	popw	%bx
	movb	%dl, %ch	/* DL saved at BP high byte in the stack */
	pushw	%cx		/* push new BX onto stack */
	movw	$0x201, %ax	/* read 1 sector */
	movw	$0x7e00, %bx	/* read MBR to 9400:7e00 */
	movw	$1, %cx
	xorb	%dh, %dh
	call	int13
	sti
3:
	popw	%bx		/* BL=sectors per track, BH=DL */

	movw	$(Error_while_reading_string - _start1), %si
	jc	2b		/* read failure, try next hard drive */

	/* on seccessful return, should be: ah=0 for OK, al=1 for 1 sector */
	//decw	%ax		/* some BIOSes return incorrect AL */
	testb	%ah, %ah
	stc
	jnz	2b

	/* The new partition table might be empty or invalid.
	 * Move the new partition table onto the old one while checking
	 */

	//movb	%dl, %bh	/* DL saved at BP high byte in the stack */

	movw	$0x7fbe, %si
	movw	$0x01be, %di

3:
	cmpw	$0x1fe, %di
	jnb	3f

	xorl	%ecx, %ecx

	lodsl
	stosl
	orl	%eax, %ecx
	lodsl
	stosl
	orl	%eax, %ecx
	lodsl
	stosl
	orl	%eax, %ecx
	lodsl
	stosl
	orl	%eax, %ecx
	jecxz	3b		/* null entry, check next */

	//lodsw
	//stosw
	movb	-16(%si), %al
	shlb	$1, %al
	stc
	xchgw	%ax, %si	/* save SI to AX */
	movw	$(partition_boot_indicator_string - _start1), %si
	jnz	2b
	xchgw	%ax, %si	/* restore SI from AX */
	//lodsw
	//stosw
	movb	-14(%si), %al
	andb	$63, %al
	stc
	xchgw	%ax, %si	/* save SI to AX */
	movw	$(partition_sectors_per_track_string - _start1), %si
	jz	2b
	xchgw	%ax, %si	/* restore SI from AX */
	//lodsw
	//stosw
	//lodsw
	//stosw
	movb	-10(%si), %al
	andb	$63, %al
	stc
	xchgw	%ax, %si	/* save SI to AX */
	movw	$(partition_sectors_per_track_string - _start1), %si
	jz	2b
	xchgw	%ax, %si	/* restore SI from AX */
	//lodsl
	//stosl
	movl	-8(%si), %eax
	testl	%eax, %eax
	stc
	xchgw	%ax, %si	/* save SI to AX */
	movw	$(partition_start_sector_string - _start1), %si
	jz	2b
	xchgw	%ax, %si	/* restore SI from AX */

	//lodsl
	//stosl
	movl	-4(%si), %eax
	testl	%eax, %eax
	stc
	xchgw	%ax, %si	/* save SI to AX */
	movw	$(partition_end_sector_string - _start1), %si
	jz	2b
	xchgw	%ax, %si	/* restore SI from AX */

	jmp	3b
3:
	cmpw	$0xAA55, (%si)
	stc
	xchgw	%ax, %si	/* save SI to AX */
	movw	$(no_boot_signature_string - _start1), %si
	jnz	2b
	xchgw	%ax, %si	/* restore SI from AX */
	//lodsw
	//stosw			/* store boot signature */

	/* Now the partition table is OK */

	movw	%bx, 12(%bp)	/* adjust BP in the stack */

	movw	$0x1b2, 8(%bp)	/* adjust SI in the stack */

	/* temp change the code:	call	self_modify_once
	 *
	 * "call self_modify_once" at add_sub_si is:
	 *
	 *	.byte	0xE8
	 *	.word	(self_modify_once - add_sub_si - 3)
	 *
	 */
	movb	$0xE8, (add_sub_si - _start1)
	movw	$(self_modify_once - add_sub_si - 3), (add_sub_si + 1 - _start1)

	/* initialize partition number and partition entries end */
	movw	$0xffff, 0x1bc		/* hd partition number */
	movw	$0x01fe, 0x1ba		/* partition entries end */

	jmp	1f
2:
	/* get here if all drives have been checked */
#if 0
	movw	$0x202, 8(%bp)	/* adjust SI in the stack */

	/* restore the original code:	addw	$-4, %si */
	movw	$0xC683, (add_sub_si  - _start1)	/* 0x83, 0xC6 */
	movb	$0xFC, (add_sub_si + 2 - _start1)	/* 0xFC */
#endif
4:
	//--------------------------------------------------------------------
	/* change the code:	jmp	Error_modify
	 *
	 * "jmp Error_modify" at Error_or_prev_MBR:
	 *
	 *	.byte	0xE9
	 *	.word	(Error_modify - Error_or_prev_MBR - 3)
	 *
	 */
	movb	$0xE9, (Error_or_prev_MBR - _start1)
	movw	$(Error_modify - Error_or_prev_MBR - 3), (Error_or_prev_MBR + 1 - _start1)
	//--------------------------------------------------------------------

	//--------------------------------------------------------------------
	/* floppy search disabled ? */
#if 0
	testb	$1, 0x02		/* test bit0 of the third byte */
	jz	1f			/* zero means floppy search enabled */
	/* 0x1fd or below means disable floppy search */
	decw	(add_sub_si + 5 - _start1)
#else
	movb	0x02, %al
	andb	$0x01, %al
	subb	%al, (add_sub_si + 5 - _start1)
#endif
	//--------------------------------------------------------------------

1:
#if 0
	popfw
	lahf			/* Load Flags into AH Register. */
				/* AH = SF:ZF:xx:AF:xx:PF:xx:CF */
				/* CF will be moved to ZF */
	movb	%ah, %al
	andb	$1, %al		/* CF=0 */
	shlb	$6, %al		/* move CF to ZF */
	popfw
	lahf			/* Load Flags into AH Register. */
				/* AH = SF:ZF:xx:AF:xx:PF:xx:CF */
	andb	$0xbf, %ah	/* 0xbf= binary 1011 1111. It clears ZF */
	orb	%al, %ah
#else
	popw	%ax		/* AX=Flags */
	popfw			/* Flags_orig */
	lahf			/* Load Flags_orig into AH Register. */
				/* AH = SF:ZF:xx:AF:xx:PF:xx:CF */
	shlb	$2, %ah
	rorw	$2, %ax		/* move CF of Flags to ZF of Flags_orig */
#endif

	sahf			/* update flags */
				/* current CF is the CF of Flags_orig */
				/* current ZF is the CF of Flags */
	jc	1f		/* CF=1 means failed in loading bootsector */
	popal			/* get drive number DL */
	pushal
	pushfw
	cmpb	$0xff, %cs:0x06
	jz	2f
	movb	%cs:0x1bc, %dh
	testb	%dl, %dl
	js	3f
	movb	$0xff, %dh	/* partition # for floppy is "whole drive" */
3:
	cmpw	%cs:0x06, %dx
	jz	2f
	popfw
	stc
	pushfw
2:
	popfw
1:
	popal
	popw	%es
	popw	%ds
	ret

self_modify_once:
	/* when we get here, SI should be 0x1b2, and BP high holds DL */ 
	addw	$12, %si	/* 0x83, 0xC6, 0x0C */
	movw	%bp, %ax
	movb	%ah, %dl

	/* note: DS=0x9400 */

	/* restore the original code:	addw	$12, %si */
	movw	$0xC683, (add_sub_si  - _start1)	/* 0x83, 0xC6 */
	movb	$0x0C, (add_sub_si + 2 - _start1)	/* 0x0C */
	ret

Error_modify:
	cmpb	$0xff, %cs:0x06	/* preferred drive? */
	jz	1f		/* not active. Turn to the final step. */

	/* preferred drive is already handled, so de-activate it now. */
	movb	$0xff, %cs:0x06

	/* we will do the second pass, from drive 0x80. */
	movb	$0x7f, %dl	/* this will become 0x80 after inc. */

	/* pass "error" to PUSHF, simulating a load failure, in order
	 * to try the first entry after return from the helper function.
	 */

	stc

	pushw	$(helper_call + 3 - _start1)	/* return address */
	pushw	%cs		/* 0x9400, it is for DS. */
	pushw	$FS_BOOT	/* 0x0d00, it is for ES. */
	pushal
	//pushl	%eax
	//pushl	%ecx
	//pushl	%edx
	//pushl	%ebx
	//pushl	%esp
	//pushl	%ebp
	//pushl	%esi
	//pushl	%edi
	pushfw			/* CF=1 */
	pushfw

	pushw	%cs
	popw	%es		/* ES=0x9400 */

	/* redo from start: DL will be 0x80 after inc. */
	jmp	inc_hard_drive
1:
boot_prev_mbr:

	/* prepare to boot the previous MBR */

	/* at this moment DS=0x9400, ES=$FS_BOOT or ES=0x9400 */
	xorw	%ax, %ax
	//pushw	%ax	/* AX=0, for the segment of 0000:7c00 */
	movw	%ax, %es	/* ES=0x0000 */
	movw	%ax, %ds	/* DS=0x0000 */
//	pushw	%ds
//	pushw	%es
	movw	$0x0202, %ax	/* read 2 sectors ... */
	movw	$0x7A00, %bx	/* ... to 0000:7A00 */
	//pushw	%bx	/* BX=0x7c00, for the offset of 0000:7c00 */
	movw	$0x0001, %cx	/* from the first sector ... */
	movw	$0x0080, %dx	/* ... of the first hard drive */
//	stc
//	int	$0x13
	call	int13
	sti
//	popw	%es
//	popw	%ds
	jc	1f
	testb	%ah, %ah
	jnz	1f
	cmpw	$0xAA55, 0x7dfe
	jne	1f
	cmpw	$0xAA55, 0x7bfe
	jne	1f

	/* has a valid partition table ? */
	movw	$0x7dbe, %si
3:
	cmpw	$0x7dfe, %si
	jnb	3f		/* partition table is OK */
	movw	$4, %cx

	movw	%si, %di
2:
	lodsl
	negl	%eax
	jc	2f
	loop	2b
	/* empty entry, check next */
	jmp	3b
2:
	/* non-empty entry */
	movw	%di, %si

	lodsw
	shlb	$1, %al
	jnz	2f
	lodsw
	andb	$63, %al
	jz	2f
	lodsw
	lodsw
	andb	$63, %al
	jz	2f
	lodsl
	negl	%eax
	jnc	2f
	lodsl
	negl	%eax
	jc	3b
2:
	stc		/* invalid partition table */
3:
	pushfw

	/* disable the boot of non-MBR bootsector ? */
	testb	$2, %cs:0x02		/* test bit1 of the third byte */
	jz	2f			/* zero means non-MBR enabled */
	popfw
	jc	1f	/* invalid partition table, print "Error" */	

	/* the partition table is valid */
	pushfw

2:
	/* the check passed, and the boot is permitted */
	popfw

	jc	2f	/* invalid partition table */

	/* use partition table in MBR instead */

	/* copy 72 bytes at 0000:7bb8 to 0000:7db8 */

	movw	$0x7bb8, %si
	movw	$0x7db8, %di
	movw	$36, %cx
	cld
	repz movsw

2:
	testb	$0x80, %cs:0x02		/* test bit 7 of the third byte */
	jz	2f			/* zero means boot prev-MBR first */

	/* Cannot find GRLDR. Press space bar to hold the screen, any other key to boot previous MBR ... */
	movw	$(Cannot_find_GRLDR_string - _start1), %si
	call	print_message	/* CS:SI points to message string */
	movw	$(nt_boot_image - _start1), %si
	call	print_message	/* CS:SI points to message string */
	movw	$(press_space_bar_string - _start1), %si
	movw	$0x3920, %cs:0x04	/* reset hot-key to space bar */
	movb	$15, %cs:0x03		/* reset time out to 15 seconds */
	call	print_message	/* CS:SI points to message string */
	movw	$(prev_MBR_string - _start1), %si
	call	print_message	/* CS:SI points to message string */
3:
	call	sleep_5_seconds		/* the hot-key is SPACE */
	/* if hot-key is pressed, wait forever until another key is pressed. */
	movb	$0xff, %cs:0x03
	jc	3b		/* desired hot-key is pressed */
2:
	/* boot the previous MBR */

	/* clear the DUCE indicator */
	movl	$0, 0x5FC	/* DS=ES=0 */

	//movb	$0x80, %dl
	ljmp	$0, $0x7c00
1:
	/* no previous MBR, print "Error" */
	///* Note the 0000:7C00 is on the stack */
	//popw	%ax	/* AX=0x0000 */
	//popw	%ax	/* AX=0x7C00 */

	testb	$0x80, %cs:0x02	/* are we called prior to the GRLDR search? */
	jnz	1f		/* no, it is a failure at last */
	/* yes, so return to the caller */
	/* Invalid previous MBR. Press any key to start GRUB ... */
	movw	$(continue_string - _start1), %si
	call	print_message	/* CS:SI points to message string */
	call	sleep_5_seconds
	ret
1:
	movw	$(message_string_helper - _start1), %si
	call	print_message	/* CS:SI points to message string */
	movw	$(nt_boot_image - _start1), %si
	call	print_message	/* CS:SI points to message string */
	movw	$(ctrl_alt_del_string - _start1), %si
	call	print_message	/* CS:SI points to message string */
1:	jmp	1b	/* hang */

sleep_5_seconds:
	/* sleep 5 seconds */

	/* sleep forever if %cs:0x03 is 0xff */

	/* calculate the timeout ticks */

	pushw	%ds
	pushl	%esi
	pushl	%edx

	movl	$0xffffffff, %edx
	movzbl	%cs:0x03, %eax
	cmpb	$0xff, %al
	je	1f
	movl	$18, %edx	/* 18.2 ticks per second. We simply use 18. */
	mulw	%dx		/* EDX=0, EAX=ticks */
	xchgw	%ax, %dx	/* EAX=0, EDX=ticks */
1:
	xorw	%ax, %ax
	movw	%ax, %ds
	movl	0x46c, %eax	/* initial tick */
	movl	%eax, %ecx	/* ECX=initial tick */
	testl	%edx, %edx
	js	1f
	addl	%edx, %eax	/* EAX=timeout tick */
	pushl	%eax

	movzbl	%cs:0x03, %eax
	orl	%eax, %eax
	jz	3f

	movw	$(hot_key_timeout_pre - _start1), %si
	pushl	%eax
	call	print_message
	popl	%eax

	movw	$(hot_key_timeout_num - _start1), %si
	call	print_decimal
3:
	movl	%ecx, %esi
	addl	$18, %esi

	popl	%eax
	jmp	3f
1:
	movl	%edx, %eax	/* EAX=0xffffffff */
	movl	%edx, %esi
3:
	movl	0x46c, %ebx	/* EBX=current tick */
	cmpl	%ecx, %ebx
	jnb	2f

	/* current tick is less than initial tick, this means the ticks have
	 * overflowed to the next day, and EBX is rather small. */
	xorl	%ecx, %ecx
	movl	%edx, %eax
	movl	$18, %esi
2:
	/* check if there is any key press. */
	pushl	%eax
	movb	$1, %ah
	int	$0x16
	pushw	%ax
	pushfw

	movb	$0x11, %ah
	int	$0x16
	jnz	1f
	popfw
	jnz	2f

	/* no, there is no key press. */

	popw	%ax
	popl	%eax

	cmpl	%esi, %ebx
	jb	4f
	pushl	%esi
	pushl	%eax
	pushl	%edx


	subl	%esi, %eax
	xorl	%edx, %edx
	movl	$18, %esi
	divl	%esi

	movw	$(hot_key_timeout_num - _start1), %si
	pushl	%ebx
	call	print_decimal
	popl	%ebx

	popl	%edx
	popl	%eax
	popl	%esi
	addl	$18, %esi
4:
	cmpl	%eax, %ebx	/* timeout? */
	jbe	3b		/* no, continue to wait */

	/* timeout reached, CF=0, no key pressed. */
	popl	%edx
	popl	%esi
	popw	%ds
	ret
1:
	popfw
2:
	/* yes, there is a key press. */
#if 0
	/* clear the keyboard buffer */
	movb	$1, %ah
	int	$0x16
	jz	1f	/* no keys, end */
	movb	$0, %ah
	int	$0x16	/* discard the key */
	jmp	1b
1:
#endif

	/* check if it is the desired key. */

	xorw	%cs:0x04, %ax	/* CF=0 */
	popw	%ax
	je	1f
	xorw	%cs:0x04, %ax	/* CF=0 */
	jne	2f		/* not desired, return CF=0 */

	/* remove the desired key from the keyboard buffer. */

	movb	$0, %ah
	int	$0x16	/* discard the key */
	jmp	3f
1:
	/* remove the desired key from the keyboard buffer. */

	movb	$0x10, %ah
	int	$0x16	/* discard the key */
3:
	stc	/* CF=1, the desired key pressed */
2:
	popl	%eax
	popl	%edx
	popl	%esi
	popw	%ds
	ret

out_decimal:
	/*
	 * input: EAX = number, CS:SI = buffer
	 */

	pushl	%edx
	pushl	%ecx
	pushw	%bx

	movl	$10, %ecx
	movw	%si, %bx

1:
	xorl	%edx, %edx
	divl	%ecx
	addb	$'0', %dl
	movb	%dl, %cs:(%si)
	incw	%si
	orl	%eax, %eax
	jnz	1b

	pushw	%si

1:
	decw	%si
	cmpw	%bx, %si
	jbe	1f
	movb	%cs:(%si), %al
	xchgb	%al, %cs:(%bx)
	movb	%al, %cs:(%si)
	incw	%bx
	jmp	1b
1:

	popw	%si

	popw	%bx
	popl	%ecx
	popl	%edx
	ret

print_decimal:
	pushw	%si
	call	out_decimal

1:
	cmpb	$'\b', %cs:(%si)
	jz	2f
	movb	$' ', %cs:(%si)
	incw	%si
	jmp	1b
2:
	popw	%si
	call	print_message
	ret

#if 0
modify_NTFS_boot_record:

	/* before the call:
	 *		AH= partition number
	 *		AL= 0xB6	; 0xB6 is opcode of "MOV DH,imm8"
	 *		DL= drive number
	 *
	 * on return:	CF=0 if there is NTFS boot record;
	 *		CF=1 otherwise. 
	 *		CF of flags_orig on the stack will set if CF=1
	 */

	/* 
	 *
	 * the current stack is:
	 *
	 * SP + 40	: DS
	 * SP + 38	: ES
	 * SP + 34	: EAX
	 * SP + 30	: ECX
	 * SP + 26	: EDX
	 * SP + 22	: EBX
	 * SP + 18	: ESP_temp
	 * SP + 14	: EBP
	 * SP + 10	: ESI
	 * SP +  6	: EDI
	 * SP +  4	: flags_orig
	 * SP +  2	: SI		; SI points to old entry in MBR
	 * SP		: return_IP
	 *
	 */

	/* DS=ES=FS_BOOT */

	/* change NTLDR to GRLDR */

	/* check GR or NT or anything else */

	pushw	%ax

	movw	$0x200, %si
	lodsw
	cmpw	$5, %ax
	jne	1f		/* failure */
	lodsw
	testb	%ah, %ah	/* high byte of unicode ASCII should be 0 */
	jne	1f		/* failure */

	/* 'N' should be a capital letter */

	cmpb	$0x41, %al	/* Less than 'A' */
	jb	1f		/* failure */
	cmpb	$0x5A, %al	/* Greater than 'Z'*/
	ja	1f		/* failure */

	xchgw	%ax, %cx	/* save AX to CX. CL='N' */

	lodsw
	testb	%ah, %ah	/* high byte of unicode ASCII should be 0 */
	jne	1f		/* failure */

	/* 'T' should be a capital letter */

	cmpb	$0x41, %al	/* Less than 'A' */
	jb	1f		/* failure */
	cmpb	$0x5A, %al	/* Greater than 'Z'*/
	ja	1f		/* failure */

	movb	%al, %ch	/* save AL to CH. CH='T' */

	lodsw
	cmpw	$0x4C, %ax	/* 'L' */
	jne	1f		/* failure */
	lodsw
	cmpw	$0x44, %ax	/* 'D' */
	jne	1f		/* failure */
	lodsw
	cmpw	$0x52, %ax	/* 'R' */
	jne	1f		/* failure */
	lodsw
	cmpw	$0x04, %ax	/* length of "$I30" */
	jne	1f		/* failure */
	lodsw
	cmpw	$0x24, %ax	/* '$' */
	jne	1f		/* failure */
	lodsw
	cmpw	$0x49, %ax	/* 'I' */
	jne	1f		/* failure */
	lodsw
	cmpw	$0x33, %ax	/* '3' */
	jne	1f		/* failure */
	lodsw
	cmpw	$0x30, %ax	/* '0' */
	jne	1f		/* failure */

	
	/* assume it is NT bootsector. first, find "NTLDR". CX holds "NT" */
	movw	$0x0100, %di
	movb	%cl, %al	/* AL="N" */
	movb	$1, %ah		/* AH=Carry for SAHF below */
	movl	$0x52444c00, %ebx	/* "LDR" */
	movb	%ch, %bl		/* 'T' */
	movw	$0x00fa, %cx

	/* now AL holds 'N' and BL holds 'T' */

	//cld			/* already upward */
3:
	repnz scasb		/* find "N" */
	jcxz	4f		/* gets the end, exit */
	cmpl	%ebx, (%di)	/* is it "NTLDR"? */
	jnz	3b		/* no, continue to find */

	/* "NTLDR" is found, so we believe it is NT boot sector. */

	movw	$0x5247, -1(%di)	/* change "NT" to "GR" */

	/* CF=0 for now */

	lahf			/* Load Flags into AH */
				/* AH = SF:ZF:xx:AF:xx:PF:xx:CF */
				/* AH = binary xxxxxxx0 */
	jmp	3b
4:
	sahf		/* Store AH into flags SF ZF xx AF xx PF xx CF */

	/* CF=0 means "NTLDR" is found, CF=1 means "NTLDR" is not found. */

	jc	1f		/* failure */

	movl	$0x00520047, 0x202	/* change to "G R L D R" */

	/* check NT 4.0 */

	movw	$0x406, %si
	movl	(%si), %ebx		/* NT 4.0 */
	cmpl	$0x03E8B800, %ebx	/* MOV AX, 03E8 */
	jnz	3f

	movl	0x84, %ebx
	cmpl	$0x680007E8, %ebx	/* call 008e; push (0D00) */
	jnz	3f

//	movw	0x154, %bx		/* CR LF at end of "A disk read error occurred." */
//	cmpw	$0x0A0D, %bx		/* CR LF */
//	jnz	3f
//	movw	0x180, %bx		/* CR LF at end of "A kernel file is missing from the disk." */
//	cmpw	$0x0A0D, %bx		/* CR LF */
//	jnz	3f
//	movw	0x1A8, %bx		/* CR LF at end of "A kernel file is too discontiguous." */
//	cmpw	$0x0A0D, %bx		/* CR LF */
//	jnz	3f
//	movw	0x1F8, %bx		/* CR LF at end of "NTLDR is compressed." */
//	cmpw	$0x0A0D, %bx		/* CR LF */
//	jnz	3f

	movl	0xE8, %ebx
	cmpl	$0x13CD80B2, %ebx	/* "B2 80"="mov DL, 80", "CD 13"="int 13" */
	jnz	3f

	popw	%ax
	movw	%ax, 4(%si)

	movl	$0x68909090, %ebx	/* nop;nop;nop;push (0D00) */
	movl	%ebx, 0x84

//	/* change CRLF in NTFS error messages to spaces */
//	movw	$0x2020, %bx		/* change CRLF to 2 spaces */
//	movw	%bx, 0x154
//	movw	%bx, 0x180
//	movw	%bx, 0x1A8
//	movw	%bx, 0x1F8

	movb	%dl, 0xE9		/* modify drive number */

	/* modify NTFS boot record */
	movb	$0xea, %al	/* ljmp, hand over the control to supervisor */
	movb	%al, 0x122
	//movw	$(try_next_partition - _start1), %ax	/* offset for ljmp */
	movw	$MONITOR, %ax	/* offset for ljmp */
	movw	%ax, 0x123
	//movw	%cs, %ax	/* AX=0x9400, segment for ljmp */
	xorw	%ax, %ax
	movw	%ax, 0x125

	movw	$(NTFS4_message - _start1), %si
	call	print_message	/* CS:SI points to message string */
	clc
	ret
3:
	/* check NT 5.0 */

	movw	$0x44b, %si
	movl	(%si), %ebx		/* NT 5.0 */
	cmpl	$0x03E8B800, %ebx	/* MOV AX, 03E8 */
	jz	2f

	movw	$0x479, %si
	movl	(%si), %ebx		/* NT 5.1 SP2 */
	cmpl	$0x03E8B800, %ebx	/* MOV AX, 03E8 */
	jnz	1f
2:
	movl	0x71, %ebx
	cmpl	$0x680053E8, %ebx	/* call 00C7; push (0D00) */
	jnz	1f

	//movw	0x183, %bx		/* CR LF at begin of "A disk read error occurred." */
	movb	0x1F8, %bl
	movb	$1, %bh
	movw	(%bx), %bx
	cmpw	$0x0A0D, %bx		/* CR LF */
	jnz	1f
	//movw	0x1A0, %bx		/* CR LF at begin of "NTLDR is missing." */
	movb	0x1F9, %bl
	movb	$1, %bh
	movw	(%bx), %bx
	cmpw	$0x0A0D, %bx		/* CR LF */
	jnz	1f
	//movw	0x1B3, %bx		/* CR LF at begin of "NTLDR is compressed." */
	movb	0x1FA, %bl
	movb	$1, %bh
	movw	(%bx), %bx
	cmpw	$0x0A0D, %bx		/* CR LF */
	jnz	1f

	popw	%ax
	movw	%ax, 4(%si)

	movl	$0x68909090, %ebx	/* nop;nop;nop;push (0D00) */
	movl	%ebx, 0x71

	/* change CRLF in NTFS error messages to spaces */
	movw	$0x2020, %ax
	movb	0x1F8, %bl
	movb	$1, %bh
	movw	%ax, (%bx)	// 0x183
	movb	0x1F9, %bl
	movb	$1, %bh
	movw	%ax, (%bx)	// 0x1A0
	movb	0x1FA, %bl
	movb	$1, %bh
	movw	%ax, (%bx)	// 0x1B3

	/* modify NTFS boot record */
	movb	$0xEA, %al	/* ljmp, hand over the control to supervisor */
	movb	%al, 0x167
	//movw	$(try_next_partition - _start1), %ax	/* offset for ljmp */
	movw	$MONITOR, %ax	/* offset for ljmp */
	movw	%ax, 0x168
	//movw	%cs, %ax	/* AX=0x9400, segment for ljmp */
	xorw	%ax, %ax
	movw	%ax, 0x16A

	cmpw	$0x44b, %si
	jne	2f
	movw	$(NTFS5_message - _start1), %si
	jmp	3f
2:
	movw	$(NTFS5p_message - _start1), %si
3:
	call	print_message	/* CS:SI points to message string */
	clc
	ret
1:
	/* NTFS boot record not found. */

	movw	$(NTFS_no_boot_record_message - _start1), %si
	call	print_message	/* CS:SI points to message string */

	popw	%ax
	popl	%eax			/* return_IP and SI */
	popfw
	stc
	pushfw
	pushl	%eax			/* return_IP and SI */
	ret
#endif

//#if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL))
move_helper:
	
	/* called only once and only when the boot loader loaded this code */
	pushw	%si
	pushw	%bx
	pushl	%eax

	movw	$0x0003, %ax	/* set display mode: 80*25 color text */
	int	$0x10

	movw	$0x200, %si	/* move from the 2nd sector */
	movw	%si, %di
	movw	$0x3E00, %cx	/* move 0x3E=62 sectors */
	cld
	repz movsw

	popl	%eax
	popw	%bx
	popw	%si
	ret
//#endif

#if (defined(GRLDR_MBR)) || (defined(GRLDR_INSTALL))
set_floppy_preferred:
	movw	$0xff00, 0x06	/* preferred drive=0, partition=0xff */
	andb	$0xFE, 0x02	/* bit0=0, enable GRLDR search on floppy */
	ret

filesystem_boot:
	/* The partition boot record successfully modified, just boot it */

	/*
	 * The boot might fail, but we want to take back the control.
	 * So we save the registers now.
	 */
	pushw	%ds
	pushw	%es
	pushal

	/* DS=CS=GRLDR_CS, ES=FS_BOOT */

	/* save GRLDR_CS */

	movw	%es, %bx	# save old ES to BX

	cli
	lgdt	gdt - _start1
	movl	%cr0, %eax
	orb	$1, %al
	movl	%eax, %cr0

	movw	$8, %si
	movw	%si, %es

	xorl	%esi, %esi
	xorl	%edi, %edi
	movl	$(0x9000 / 4), %ecx

	cld
	repz movsl

	movw	$16, %si
	movw	%si, %es

	andb	$0xfe, %al
	movl	%eax, %cr0

	movw	%bx, %es	# restore ES from BX

	/* move FS_BOOT:0000 to 0:7c00 */
#if 0
	/* for single sector boot record */
	movw	$0x0200, %cx	/* move 2 sectors, the old FS_BOOT:0000 will keep untouched.  */
#else
	/* for 4-sector NTFS boot record */
	movw	$0x0400, %cx	/* move 4 sectors, the old FS_BOOT:0000 will keep untouched.  */
#endif
	xorw	%si, %si
	pushw	%si	/* SI=0, for the segment of 0000:7c00 */
	movw	$0x7c00, %di
	pushw	%di	/* DI=0x7c00, for the offset of 0000:7c00 */
	pushw	%es	/* ES=FS_BOOT */
	popw	%ds	/* DS=FS_BOOT */
	pushw	%si	/* SI=0 */
	popw	%es	/* ES=0 */
	cld
	repz movsw

	movw	$MONITOR, %di
	movw	$(restore_GRLDR_CS - _start1), %si
	movw	$((gdt_end - restore_GRLDR_CS) / 4), %cx
	cld
	repz cs movsl		/* CS segment override prefix(=0x2E) */

	pushw	%es	/* ES=0 */
	popw	%ds	/* DS=0 */
	sti
	lret	//ljmp	$0, $0x7c00
#endif

geometry_tune:

	/* on call:
	 *		CS=DS=SS
	 *		ES=FS_BOOT
	 *
	 * on return:
	 *		CL	max sector number
	 *		DH	max head number
	 *		DS	changed
	 *		ES	changed
	 *		AX	changed
	 *		BX	changed
	 *		CX	changed
	 *   byte at CS:[08]	updated on success(Smax)
	 *   byte at CS:[09]	updated on success(Hmax)
	 */


	testb	%dl, %dl		/* floppy? */
	jns	1f			/* yes, continue to tune. */

	cmpw	$0x1c2, %si		/* first run on this hard drive? */
	jne	2f			/* no, avoid tuning again. */
1:
	movw	$0xffff, 0x08

#if (defined(GRLDR_MBR)) || (defined(GRLDR_INSTALL))
	cmpb	$0x42, 0x00	/* EBIOS present? */
	je	2f		/* yes, skip the tune */

	#; real geometry tune follows. if you wish to disable the tune,
	#; just do a 'ret' here.
	//ret		#; disable the tune

	testb	$0x08, 0x02	/* chs-no-tune */
	jnz	2f

	/* check if the code is loaded completely */

	cmpl	$0xAA555247, (grldr_signature - _start1) /* "GR" 0x55 0xAA */
	je	real_geometry_tune

#endif
2:
	ret

press_space_bar_string:
	.ascii	"\r\nPress space bar\0"

press_hot_key_pre:
	.ascii "\r\nPress \0"

press_hot_key_sub:
	.ascii	" to start GRUB, any other key to boot previous MBR ...\0"
press_hot_key_sub1:
	.ascii	" to boot previous MBR, any other key to start GRUB ...\0"

hot_key_timeout_pre:
	.ascii "\r\nTimeout : \0"

hot_key_timeout_num:
	.ascii "   \b\b\b\0"

continue_string:
	.ascii	"\r\nInvalid previous MBR. Press any key to start GRUB ...\0"

Cannot_find_GRLDR_string:
	.ascii	"\r\nCannot find \0"

prev_MBR_string:
	.ascii	" to hold the screen, any other key to boot previous MBR ...\0"

Error_while_reading_string:
	.ascii	"\r\nError while reading MBR of \0"

drive_number_string:
	.ascii	" in partition table of drive (hd0 ) \0"

partition_boot_indicator_string:
	.ascii	"\r\nInvalid boot indicator\0"

partition_sectors_per_track_string:
	.ascii	"\r\nInvalid sectors_per_track\0"

partition_start_sector_string:
	.ascii	"\r\nInvalid start_sector\0"

partition_end_sector_string:
	.ascii	"\r\nInvalid end_sector\0"

no_boot_signature_string:
	.ascii	"\r\nNo boot signature\0"

message_string_helper:
	.ascii	"\r\nError: Cannot find \0"
ctrl_alt_del_string:
	.ascii	" in all drives. Press Ctrl+Alt+Del to restart.\0"

partition_message:
	.ascii	"\r\nTry (hd0,0 ) : \0"

EXT2_message:
	.ascii	"EXT2: \0"

#if 0
NTFS4_message:
	.ascii	"NTFS4: \0"
NTFS5p_message:
	.ascii	"NTFS5p: \0"
#endif

NTFS5_message:
	.ascii	"NTFS5: \0"
FAT32_message:
	.ascii	"FAT32: \0"
FAT16_message:
	.ascii	"FAT16: \0"
FAT12_message:
	.ascii	"FAT12: \0"
non_MS_message:
	.ascii	"non-MS: skip \0"
extended_message:
	.ascii	"Extended: \0"
invalid_message:
	.ascii	"invalid or null \0"

#if (defined(GRLDR_MBR)) || (defined(GRLDR_INSTALL))

	//. = . + (0x3e8 - ((. - _start1) % 0x200)) % 0x200
	. = _start1 + 0x1FE8

press_hot_key_name:

	/* hot key name, the address is (grldr_signature - 16) */

	.ascii  "hot-key\0"

	/* grldr.mbr is larger than 8K. */

	######################################################################
	# The installer should setup the long integer at offset 0x1FFC as
	# the unique disk signature. The same signature value must be also
	# placed into the disk_serial_number_structure in the first sector(the
	# MBR sector). You can easily locate the structure through the pointer
	# at offset 0x1FF8.
	#
	# For GRLDR.MBR the default disk serial number is the grldr.mbr
	# signature(0x93cb4d05). You should change it according to the
	# date-time value just at the time when you run the installer.
	######################################################################

	. = _start1 + 0x1FF8

	/* point to disk serial number in the first sector, i.e., the MBR
	 * sector. The program never access this pointer. It can be used by
	 * an external program to easily locate the disk serial number at MBR.
	 */

	.word	disk_serial_number_structure - _start1 + 5

	. = _start1 + 0x1FFA

	/* version of grldr.mbr. It has a copy at (grldr_signature - 2) */

	.word   4

	. = _start1 + 0x1FFC

	.long	0x93cb4d05		/* date-time for disk serial number */

	. = _start1 + 0x2000

real_geometry_tune:

//////////////////////////////////////////////////////////////////////////////

	/* initialize passed-in values */

	pushaw

	xorl	%eax, %eax
	movl	%eax, %cs:(Sectors_passed_in - _start1)
	movl	%eax, %cs:(Heads_passed_in - _start1)

	movb	$8, %ah		/* read drive parameters changes DX,ES,DI,BX */
	//movb	$0x80, %dl	/* BIOS drive number is in DL */
	int	$0x13
	jc	1f		/* failed */
	testb	$63, %cl
	jz	1f		/* failed */

	andb	$63, %cl	/* sectors per track */

	movb	%cl, %cs:(Sectors_passed_in - _start1)
	incb	%dh
	movb	%dh, %cs:(Heads_passed_in - _start1)	/* 0 for 256 */
1:
	popaw

	/* print BIOS geometry */

	movzbw	%cs:(Sectors_passed_in - _start1), %cx
	pushw	%cx
	movzbw	%cs:(Heads_passed_in - _start1), %cx
	pushw	%cx
	movzbw	%dl, %cx
	pushw	%cx
	pushw	$(BIOS_geom_string - _start1)
	call	realmode_printf
	addw	$8, %sp

//////////////////////////////////////////////////////////////////////////////

	/* find Max sector by reading each sector on the first track. */

	/* try the passed-in value first */
	movw	%cs:(Sectors_passed_in - _start1), %cx	/* cylinder=0 */
	call	check_sector_readable
	jnc	1f
	xorw	%cx, %cx
1:
	incw	%cx
	call	check_sector_readable
	jc	1f
	cmpw	$63, %cx
	jb	1b
	jmp	2f
1:
	/* Max Sector = CX - 1 */
	decw	%cx
	cmpb	$2, %cl
	jnb	2f
	movb	$1, %ah		/* failure */
	ret

check_sector_readable:
	movw	$0x5000, %ax
	movw	%ax, %es
	movw	%ax, %ds
	xorw	%bx, %bx
	movw	$0x201, %ax	/* read 1 sector */
	movb	$0, %dh		/* head=0 */
	pushaw
	movw	%bx, %si
	movw	%bx, %di
	int	$0x13
	popaw
	ret

2:
	/* CX=Max Sector */
	movw	%cx, %cs:(Smax_tuned - _start1)

//////////////////////////////////////////////////////////////////////////////
#if 0
	/* check if we can read sectors across the track boundary */

	/* first, read a track plus one sector */

	movb	$0, %cs:(cross_track - _start1)

	movw	$1, %cx		/* sector 1, cylinder 0 */
	movb	$0, %dh		/* head 0 */
	movb	%cs:(Smax_tuned - _start1), %al	/* sectors to read */
	incw	%ax		/* read 1 more sector */
	movb	$2, %ah		/* READ */
	movw	$0x5000, %bx
	movw	%bx, %es
	movw	%bx, %ds
	xorw	%bx, %bx
	pushaw
	movw	%bx, %si
	movw	%bx, %di
	int	$0x13
	popaw
	jc	1f		/* cross-track read is not supported */

	/* read again normally, only the track */

	movb	%cs:(Smax_tuned - _start1), %al	/* sectors to read */
	movb	$2, %ah		/* READ */
	movw	$0x5800, %bx
	movw	%bx, %es
	movw	%bx, %ds
	xorw	%bx, %bx
	pushaw
	movw	%bx, %si
	movw	%bx, %di
	int	$0x13
	popaw
	jc	2b		/* failure */

	/* compare the two tracks */

	pushw	%cx
	pushw	%si
	pushw	%di
	movw	%cs:(Smax_tuned - _start1), %cx	/* sectors */
	shlw	$7, %cx				/* dwords */
	movw	$0x5000, %ax
	movw	%ax, %ds
	movw	$0x5800, %ax
	movw	%ax, %es
	xorw	%si, %si
	xorw	%di, %di
	cld
	repz cmpsl
	popw	%di
	popw	%si
	popw	%cx
	jne	1f		/* cross-track read is not supported */
	movb	$1, %cs:(cross_track - _start1)
1:
#endif
//////////////////////////////////////////////////////////////////////////////
#if 0
	/* find Max head by reading sector 1 on each track of cylinder 0. */

	movb	$0xFF, %dh	/* head=Max possible */
1:
	movw	$0x5000, %ax
	movw	%ax, %es
	movw	%ax, %ds
	xorw	%bx, %bx
	movw	$0x201, %ax	/* read 1 sector */
	movw	$1, %cx		/* cylinder=0, sector=1 */
	pushaw
	movw	%bx, %si
	movw	%bx, %di
	int	$0x13
	popaw
	jnc	1f		/* found Max head */
	decb	%dh
	cmpb	$0xFF, %dh
	jne	1b
	movb	$1, %ah		/* failure */
	ret
1:

	/* DH=Max head */
	movb	%dh, %cs:(Hmax_tuned - _start1)
#endif
//////////////////////////////////////////////////////////////////////////////

	/* tune Hmax */

	/* First, try the passed-in value */
	movb	%cs:(Heads_passed_in - _start1), %dh
	testb	%dh, %dh
	jz	1f		/* the passed-in heads = 0x100 */

	call	tune_heads
	jb	2f		/* failure */
	ja	4f		/* success */
1:
	movb	$1, %dh		/* Hmax: 1 - 255 */
1:
	call	tune_heads
	jb	2f		/* failure */
	ja	4f		/* success */

#if 0
	cmpb	%cs:(Hmax_tuned - _start1), %dh
	ja	2f		/* this should not happen */
	je	5f
#endif

	incb	%dh		/* Next Hmax */
	jnz	1b

	/* Hmax=0xFF */
4:
	/* got Hmax=DH-1 */

	decb	%dh
	movb	%dh, %cs:(Hmax_tuned - _start1)
5:
	/* Hmax is tuned ok. */

	cmpb	$0xFF, %dh
	jne	1f
	/* consider Hmax=0xFF as a failure! Use the passed-in value. */
	movb	%cs:(Heads_passed_in - _start1), %dh
	testb	%dh, %dh
	jnz	4f
	decb	%dh
4:
	decb	%dh
	movb	%dh, %cs:(Hmax_tuned - _start1)
1:
//////////////////////////////////////////////////////////////////////////////

	/* tune Smax */

	/* First, try the passed-in value */
	movb	%cs:(Sectors_passed_in - _start1), %cl
//	cmpb	$8, %cl
//	jb	1f
//	cmpb	$0, %cs:(cross_track - _start1)
//	jnz	4f
//	cmpb	%cs:(Smax_tuned - _start1), %cl
//	jnb	1f
4:
	call	tune_sectors
	jb	2f		/* failure */
	ja	4f		/* success */
1:
	movb	$8, %cl		/* Smax: 8 - 63 */
1:
	call	tune_sectors
	jb	2f		/* failure */
	ja	4f		/* success */

	incw	%cx		/* Next Smax */
	cmpb	%cs:(Smax_tuned - _start1), %cl
	jb	1b

4:
	/* got Smax=CL */

	movb	%cl, %cs:(Smax_tuned - _start1)

	/* Smax is tuned ok. */

//////////////////////////////////////////////////////////////////////////////

	/* print tuned geometry */

	movzbw	%cs:(Smax_tuned - _start1), %cx
	pushw	%cx
	movzbw	%cs:(Hmax_tuned - _start1), %cx
	incw	%cx
	pushw	%cx
	movzbw	%dl, %cx
	pushw	%cx
	pushw	$(TUNE_geom_string - _start1)
	call	realmode_printf
	addw	$8, %sp

	/* return with success */

	movw	%cs:(Smax_tuned - _start1), %cx
	movb	%cs:(Hmax_tuned - _start1), %dh
	movb	%cl, %cs:0x08	/* Smax */
	movb	%dh, %cs:0x09	/* Hmax */
	movb	$0, %ah		/* success */
	ret
2:
	movb	$1, %ah		/* failure */
	ret

//////////////////////////////////////////////////////////////////////////////

tune_heads:

	/* input:	DH = MaxHead + 1 */

	movb	$0, %ch		/* cylinder: 0 - 4 */
2:
	/* read ending track of this cylinder */

	movb	$1, %cl		/* sector 1=the leading sector */
	movb	$2, %ah		/* READ */
	movb	%cs:(Smax_tuned - _start1), %al	/* sectors to read */
	movw	$0x5000, %bx
	movw	%bx, %es
	movw	%bx, %ds
	xorw	%bx, %bx
	pushaw
	movw	%bx, %si
	movw	%bx, %di
	int	$0x13
	popaw
	//jc	2f		/* failure */
	incb	%ch
	jc	4f		/* considered OK */
	decb	%ch

	/* read beginning track of this cylinder */

	movb	$1, %cl		/* sector 1=the leading sector */
	movb	$2, %ah		/* READ */
	movb	%cs:(Smax_tuned - _start1), %al	/* sectors to read */
	movw	$0x5000, %bx
	movw	%bx, %es
	movw	%bx, %ds
	xorw	%bx, %bx
	pushaw
	movw	%bx, %si
	movw	%bx, %di
	movb	$0, %dh
	int	$0x13
	popaw
	jc	2f		/* failure */

	incb	%ch		/* next cylinder */

	/* compare the two tracks */
	call	cmp_track
	je	4f		/* ok, try next cylinder */

	/* read beginning track of the next cylinder */

	movb	$1, %cl		/* sector 1=the leading sector */
	movb	$2, %ah		/* READ */
	movb	%cs:(Smax_tuned - _start1), %al	/* sectors to read */
	movw	$0x5800, %bx
	movw	%bx, %es
	movw	%bx, %ds
	xorw	%bx, %bx
	pushaw
	movw	%bx, %si
	movw	%bx, %di
	movb	$0, %dh
	int	$0x13
	popaw
	jc	2f		/* failure */

	/* compare the two tracks */
	call	cmp_track
	jne	3f		/* Next Hmax */
4:
	cmpb	$5, %ch		/* cylinder: 0 - 4 */
	jb	2b		/* Next cylinder */

	/* all passed, DH-1 is the final Hmax */
	cmpb	$0, %dh
	je	2f		/* failure */
	ret			/* Flag: above */
3:
	cmpb	%dh, %dh	/* Flag: equal */
	ret
2:
	stc			/* Flag: below */
	ret

cmp_track:
	pushw	%cx
	pushw	%si
	pushw	%di
	movw	%cs:(Smax_tuned - _start1), %cx	/* sectors */
	shlw	$7, %cx				/* dwords */
	movw	$0x5000, %ax
	movw	%ax, %ds
	movw	$0x5800, %ax
	movw	%ax, %es
	xorw	%si, %si
	xorw	%di, %di
	cld
	repz cmpsl
	popw	%di
	popw	%si
	popw	%cx
	ret

//////////////////////////////////////////////////////////////////////////////

tune_sectors:

	/* input:	CL = MaxSector */

	movw	$16, %cs:(Smax_count - _start1)

	movb	$0, %ch		/* cylinder: 0 - 6 */
	movb	$0, %dh		/* head: 0 - Hmax */
6:
	/* read ending sector of this track. */

	movw	$0x202, %ax	/* read 2 sectors */
	movw	$0x5000, %bx
	movw	%bx, %es
	movw	%bx, %ds
	xorw	%bx, %bx
	pushaw
	movw	%bx, %si
	movw	%bx, %di
	int	$0x13
	popaw
	//jc	2f		/* failure */
	pushfw			/* save CF */

	/* read beginning sector of the next track. */

	cmpb	%cs:(Hmax_tuned - _start1), %dh
	jb	3f		/* Next track */
	movb	$0xFF, %dh	/* head 0 of ... */
	incb	%ch		/* ... the next cylinder. */
3:
	incb	%dh		/* next track */

	popfw			/* restore CF */
	jc	4f		/* considered OK */

	pushw	%cx		/* save CL */

	movb	$1, %cl		/* sector 1=the leading sector */

	movw	$0x201, %ax	/* read 1 sector */
	movw	$0x5800, %bx
	movw	%bx, %es
	movw	%bx, %ds
	xorw	%bx, %bx
	pushaw
	movw	%bx, %si
	movw	%bx, %di
	int	$0x13
	popaw
	popw	%cx		/* restore CL */
	jc	2f		/* failure */

	/* compare the two sectors */

	pushw	%cx
	pushw	%si
	pushw	%di
	movw	$0x80, %cx	/* 1 sector == 0x80 dwords */
	movw	$0x5020, %ax
	movw	%ax, %ds
	movw	$0x5800, %ax
	movw	%ax, %es
	xorw	%si, %si
	xorw	%di, %di
	cld
	repz cmpsl
	popw	%di
	popw	%si
	popw	%cx
	jne	3f		/* Next Smax */
4:
	decw	%cs:(Smax_count - _start1)
	jz	6f

	//cmpb	%cs:(Hmax_tuned - _start1), %dh
	//jb	6b		/* Next track */
	//movb	$0, %dh		/* head 0 of ... */
	//incb	%ch		/* ... the next cylinder. */
	cmpb	$7, %ch		/* any cylinder remains to check? */
	jb	6b		/* yes, next track */
6:
	/* all passed, CL is the final Smax */
	cmpb	$1, %cl
	jbe	2f		/* failure */
	ret			/* Flag: above */
3:
	/* not Maximum sector number */
	cmpb	%cl, %cl	/* Flag: equal */
	ret
2:
	/* I/O error, sector tune failed */
	stc			/* Flag: below */
	ret

#;============================================================================

/* void realmode_printf(const char *format, ...)
 *
 * input:	format is offset in CS segment
 * 
 * Usage example:
 * 
 * 		pushw	IntegerN
 *		 ... ... ... ...
 * 		pushw	Integer2
 * 		pushw	Integer1
 * 		pushw	$format_string - _start1
 *		call	realmode_printf
 * 		addw	$(2*(N+1)), %sp
 * 
 * where int13_handle should be the base of the CS segment,
 * and format_string like this:
 *
 * format_string:
 *		 .string "Int1=%x, Int2=%x, ..., IntN=%x\r\n"
 *
 * Currently only %d, %x and %X are implemented.
 */

realmode_printf:
	pushaw
	movw	%sp, %bp
	# bp+18:	format
	# bp+20:	variables
	addw	$18, %bp
	movw	(%bp), %si		# points to format string
	addw	$2, %bp			# (%bp) is the first variable
1:
	cs lodsb
	testb	%al, %al
	jz	1f
	cmpb	$'%', %al
	jne	2f

	#; %d, %x, %X

	cs lodsb
	testb	%al, %al
	jz	1f
	cmpb	$'d', %al
	movw	$10, %bx		# base 10
	jz	4f
	cmpb	$'x', %al
	jz	3f
	cmpb	$'X', %al
	jne	1b			# unkown directive, continue
3:
	/* print hexa number */
	movw	$16, %bx		# base 16
4:
	/* print decimal or hexa number */
	pushl	%edi

	xorl	%edi, %edi
	xorw	%cx, %cx		# count the digits
	movw	(%bp), %ax
5:
	xorw	%dx, %dx
	divw	%bx			# AX=quo, DX=rem
	movw	%dx, %di
	rorl	$4, %edi
	incw	%cx
	testw	%ax, %ax		# end?
	jnz	5b

	/* print the digits in EDI */
	xorw	%bx, %bx	/* video page 0 */
5:
	roll	$4, %edi
	movw	%di, %ax		# get digit in AL
	andb	$0x0f, %al
	cmpb	$9, %al
	jbe	6f
	addb	$7, %al			# A, B, C, D, E, F
6:
	addb	$0x30, %al
	movb	$0x0e, %ah	/* print it */
	int	$0x10		/* via TTY mode */
	loop	5b

	popl	%edi

	addw	$2, %bp			# (%bp) is the next variable
	jmp	1b			# continue
2:
	/* print char in AL */
	xorw	%bx, %bx	/* video page 0 */
	movb	$0x0e, %ah	/* print it */
	int	$0x10		/* via TTY mode */
	jmp	1b			# continue
1:
	popaw
	ret

#;============================================================================
//////////////////////////////////////////////////////////////////////////////

BIOS_geom_string:
	.string	"\r\nBIOS: Drive=0x%X, H=%d, S=%d\r\n"
TUNE_geom_string:
	.string	"TUNE: Drive=0x%X, H=%d, S=%d\r\n"

	.align	4

Sectors_passed_in:
	.long	0
Heads_passed_in:
	.long	0
Smax_tuned:
	.word	0
Hmax_tuned:
	.word	0
Smax_count:
	.word	0
cross_track:
	.byte	0

//////////////////////////////////////////////////////////////////////////////

#endif	/* end of real_geometry_tune */

#if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL))
	######################################################################
	# External modifiers may setup a long integer at offset 0x1FFC as the
	# unique disk signature. The same signature value must be also placed
	# into the disk_serial_number_structure in the first sector(the MBR
	# sector). You can easily locate the structure through the pointer at
	# offset 0x1FF8.
	#
	# For GRLDR the default disk serial number is the grldr signature
	# ("GR" 0x55 0xAA). Generally you needn't change it, though you are
	# allowed to change it through an external modifier.
	######################################################################

	. = _start1 + 0x1FF8

	/* point to disk serial number in the first sector, i.e., the MBR
	 * sector. The program never access this pointer. It can be used by
	 * an external program to easily locate the disk serial number at MBR.
	 */

	.word	disk_serial_number_structure - _start1 + 5

	. = _start1 + 0x1FFA
#else
	. = . + (0x3fa - ((. - _start1) % 0x200)) % 0x200
#endif

	/* version word of grldr.mbr, the address is (grldr_signature - 2) */

	.word   4

grldr_signature:
	.byte	0x47, 0x52, 0x55, 0xaa	/* signature for helper */

	.align	0x200

#if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL))

	/* pre_stage2 start at 0x2000 for grldr */

	. = _start1 + 0x2000

#endif

#if defined(GRLDR_MBR)
	/* if the size is less than 8192, let it be 8192 */
	. = . + (0x2000 - (. - _start1)) * (0x4000 / (. - _start1 + 0x2001))
#endif

pre_stage2_start:


