/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 1999,2000,2001,2002,2004 Free Software Foundation, Inc.
 *
 *  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.
 */


/*
 * Note: These functions defined in this file may be called from C.
 *       Be careful of that you must not modify some registers. Quote
 *       from gcc-2.95.2/gcc/config/i386/i386.h:

   1 for registers not available across function calls.
   These must include the FIXED_REGISTERS and also any
   registers that can be used without being saved.
   The latter must include the registers where values are returned
   and the register where structure-value addresses are passed.
   Aside from that, you can include as many other registers as you like.

  ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg
{  1, 1, 1, 0, 0, 0, 0, 1, 1,  1,  1,  1,  1,  1,  1,  1,  1 }
 */

#define ASM_FILE

#include "filesys.h"
#include "shared.h"

#ifdef STAGE1_5
# define	ABS(x)	((x) - EXT_C(main) + 0x2200)
#else
# define	ABS(x)	((x) - EXT_C(main) + 0x8200)
#endif

	.file	"asm.S"

	.text

	/* Tell GAS to generate 16-bit instructions so that this code works
	   in real mode. */
	.code16

#ifndef STAGE1_5
	/*
	 * In stage2, do not link start.S with the rest of the source
	 * files directly, so define the start symbols here just to
	 * force ld quiet. These are not referred anyway.
	 */
	.globl	start, _start
start:
_start:
#endif /* ! STAGE1_5 */

ENTRY(main)
	/*
	 *  Guarantee that "main" is loaded at 0x0:0x8200 in stage2 and
	 *  at 0x0:0x2200 in stage1.5.
	 */
	ljmp $0, $ABS(codestart)

	. = EXT_C(main) + 0x5

	/* control byte: pxe, DUCE, tune
	 * bit 0 = 1: disable pxe
	 * bit 1 = 1: disable keyboard intervention in boot process
	 * bit 2 = 1: disable the "unconditional command-line entrance" feature
	 * bit 3 = 1: disable geometry tune
	 */

	.byte	0

	/*
	 *  Compatibility version number
	 *
	 *  These MUST be at byte offset 6 and 7 of the executable
	 *  DO NOT MOVE !!!
	 */
	. = EXT_C(main) + 0x6
	.byte	COMPAT_VERSION_MAJOR, COMPAT_VERSION_MINOR

	/*
	 *  This is a special data area 8 bytes from the beginning.
	 */

	. = EXT_C(main) + 0x8

VARIABLE(install_partition)
	.long	0xFFFFFF
/* This variable is here only because of a historical reason.  */
VARIABLE(saved_entryno)
#if defined(STAGE1_5) /* || ! defined(PRESET_MENU_STRING) */
	.long	0
#else

/* Note: GRUB for DOS uses this for the commandline preset_menu.
 * A preset_menu can be embedded in the commandline of GRUB.EXE.
 * This new preset_menu overrides the built-in preset_menu.
 * If the variable is not touched, and the first byte at config_file is 0,
 * then the new menu at 0x0800 will work.
 * If the variable here is cleared to 0, or the first byte at config_file is
 * not 0, then the built-in preset_menu will work.
 *
 * Do NOT change this variable to other value than 0.
 */

	.long	EXT_C(preset_menu)
#endif
VARIABLE(stage2_id)
	.byte	STAGE2_ID
VARIABLE(force_lba)
	.byte	0
VARIABLE(version_string)
	.string VERSION
VARIABLE(config_file)
#ifndef STAGE1_5
	.string "/boot/grub/menu.lst"
#else   /* STAGE1_5 */
	.long	0xffffffff
	.string "/boot/grub/stage2"
#endif  /* STAGE1_5 */

	/*
	 *  Leave some breathing room for the config file name.
	 */

	. = EXT_C(main) + 0x6C  #; bss starting address
#ifndef STAGE1_5
#if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL)
	//.word	(__bss_start - main) & 0x0F, (__bss_start - main) >> 4
	.long	__bss_start
#elif defined(HAVE_USCORE_EDATA_SYMBOL)
	//.word	(_edata - main) & 0x0F, (_edata - main) >> 4
	.long	_edata
#elif defined(HAVE_EDATA_SYMBOL)
	//.word	(edata - main) & 0x0F, (edata - main) >> 4
	.long	edata
#else
#error no bss starting address
#endif
#endif

	. = EXT_C(main) + 0x70

/* the real mode code continues... */
codestart:
	jmp	real_codestart

	/* internal variables follow */

	. = EXT_C(main) + 0x80

VARIABLE(boot_drive)
#ifdef SUPPORT_DISKLESS
	.long	NETWORK_DRIVE
#else
	.long	0
#endif

//IP4 pxe_yip, pxe_sip, pxe_gip;
VARIABLE(pxe_yip)
	.long	0
VARIABLE(pxe_sip)
	.long	0
VARIABLE(pxe_gip)
	.long	0

	. = EXT_C(main) + 0x90

VARIABLE(filesize)
	.long	0, 0
VARIABLE(saved_mem_upper)
	.long	0
VARIABLE(saved_partition)
	.long	0
VARIABLE(saved_drive)
	.long	0
VARIABLE(no_decompression)
	.long	0

	. = EXT_C(main) + 0xC0

real_codestart:
	cli		/* we're not safe here! */
	cld

	/* set up %ds, %ss, and %es */
	xorw	%ax, %ax
	movw	%ax, %ds
	movw	%ax, %es
	movw	%ax, %ss
	movl	$STACKOFF, %esp

	sti		/* added 2008-08-04 */

#ifndef STAGE1_5
	movb	0x0410, %al
	movb	%al, ABS(EXT_C(floppies_orig))
	movb	0x0475, %al
	movb	%al, ABS(EXT_C(harddrives_orig))

	//movb	$((int13_handler_end - int13_handler + 0x3ff) / 0x400), ABS(int13_handler)

	movl	0x54, %eax
	movl	%eax, ABS(EXT_C(ROM_int15))
	movl	0x4C, %eax
	movl	%eax, ABS(ROM_int13)
	movl	%eax, ABS(ROM_int13_dup)
	cmpl	$0xC0000000, %eax
	jnb	1f
	cmpl	$0x5A000000, %eax	// cmpl	$0x9A000000, %eax
	jb	1f
	andl	$0x3FFFFF, %eax
	cmpl	$0x100, %eax
	jnz	1f
	movw	0x413, %ax		/* Memory size in Kb */
	shlw	$6, %ax			/* Memory size in paragragh */
	cmpw	0x4E, %ax		/* 0000:004E=current int 13 segment */
	jne	1f			/* not hooked */

	movw	%ax, %ds		/* DS=current int13 code segment */

	/* check our int13 signature "$INT13SFGRUB4DOS" */
	cmpl	$0x544E4924, 0x103	/* $INT */
	jnz	2f
	cmpl	$0x46533331, 0x107	/* 13SF */
	jnz	2f
	cmpl	$0x42555247, 0x10B	/* GRUB */
	jnz	2f
	cmpl	$0x534F4434, 0x10F	/* 4DOS */
	jnz	2f

	movl	(ROM_int13 - int13_handler), %eax	/* 0x1C=ROM int 13 */
	cmpl	$0x5A000000, %eax	// cmpl	$0x9A000000, %eax
	jb	2f					/* not our handler */

	movl	(EXT_C(ROM_int15) - int13_handler), %eax	/* 0x0C=ROM int 15 */
	cmpl	$0x5A000000, %eax	// cmpl	$0x9A000000, %eax
	jb	2f					/* not our handler */

	/* restore old emu data, except the first byte of handler size. */
	movw	$(0x140 - 1), %cx
	movw	$1, %si			/* DS=current int13 code segment */
	movw	$ABS(int13_handler + 1), %di		/* ES=0 */
	repz movsb

	/* calculate the new max_cdrom_id. */
	movw	$2, %si
	lodsw				/* AL=atapi_dev_count, AH=min_cdrom_id */
	addb	%ah, %al
	decw	%ax			/* AL=max_cdrom_id */
	movb	%al, %cs:ABS(max_cdrom_id)		/* CS=0 */

	/* initialize bios_drive_map with hooked_drive_map */
	movw	$(DRIVE_MAP_SIZE * DRIVE_MAP_SLOT_SIZE / 2), %cx
	movw	$ABS(EXT_C(hooked_drive_map)), %si		/* CS=0 */
	movw	$ABS(EXT_C(bios_drive_map)), %di		/* ES=0 */
	cs repz movsw
	//xorw	%ax, %ax
	//movw	%ax, %ds			/* DS=0 */
	//jmp	3f

2:
	xorw	%ax, %ax
	movw	%ax, %ds			/* DS=0 */
1:
	//movl	ABS(EXT_C(ROM_int15)), %eax
	//cmpl	$0x5A000000, %eax	// cmpl	$0x9A000000, %eax
	//jnb	3f
	//movl	0x0054, %eax
	//movl	%eax, ABS(EXT_C(ROM_int15))
//3:
	/* check the BIOS type (currently only for Bochs) */
	movw	$0xF000, %ax
	movw	%ax, %es			/* ES=0xF000 */
	movw	$0xFF00, %di
	movw	$ABS(bochs_copygrght_string), %si
	movw	$0x22, %cx
	repz cmpsw
	setz	ABS(EXT_C(bios_id))	/* 1 for bochs, 0 for unknown. */
	xorw	%ax, %ax
	movw	%ax, %es			/* ES=0 */

#endif  /* STAGE1_5 */
//#ifndef SUPPORT_DISKLESS
//	/*
//	 * Save the sector number of the second sector (i.e. this sector)
//	 * in INSTALL_SECOND_SECTOR. See also "stage2/start.S".
//	 */
//	ADDR32	movl	%ebp, EXT_C(install_second_sector)
//#endif


	/* the active mouse will hang the machine */

#if 0
	cli
	pushal
	pushw	%ds
	pushw	%es
	pushw	%cs
	call	1f
	popw	%es
	popw	%ds
	popal
	jmp	2f
1:
	pushw	$0xADDA
	//ljmp	$0xF000, $0x98E1
	ljmp	$0xF000, $0xA5F2

2:
	ljmp	$0xF000, $0xFFF0
#endif

	pushw	%dx		/* DL=boot drive */

	/* qemu-0.8.0 could hang on mouse init here. */

#if 0
	/* reset mouse */
	movw	$0xC201, %ax
	int	$0x15
#endif

#if 0
	/* disable mouse */
	movw	$0xC200, %ax
	xorw	%bx, %bx	/* BH=0 means disable */
	int	$0x15
#endif

#if 0
	/* set mouse handler address */
	movw	$0xC207, %ax
	xorw	%bx, %bx	/* ES:BX=0000:0000 to cancel the handler */
	int	$0x15
#endif

#if 0
	/* disable monitor clock (Watch-Dog) */
	movw	$0xC300, %ax
	int	$0x15
#endif

#if 0
	/* restart all adaptors */
	movb	$0xFF, %al
	outb	%al, $0x96
	xorw	%cx, %cx
1:	loop	1b
	movb	$0xF0, %al
	outb	%al, $0x96
	xorw	%cx, %cx
1:	loop	1b
	movb	$0x00, %al
	outb	%al, $0x96
	xorw	%cx, %cx
1:	loop	1b

	/* initialize all adaptors */
	movw	$0xC000, %bx
2:
	movw	%bx, %ds
	xorw	%si, %si
	lodsw
	cmpw	$0xAA55, %ax
	jne	1f
	xorw	%ax, %ax
	lodsb			#; ROM size in sectors
	addw	$3, %ax
	andw	$0xFFFC, %ax
	subw	$4, %ax
	shlw	$5, %ax
	pushaw
	pushw	%bx
	pushw	$3
	movw	%sp, %bp
	lcall	*(%bp)
	popw	%bx
	popw	%bx
	popaw
1:
	addw	$0x80, %ax
	addw	%ax, %bx
	cmpw	$0xF000, %bx
	jb	2b

#endif

	xorw	%ax, %ax
	movw	%ax, %ds
	movw	%ax, %es

#if 0
	/* cancel alarm clock */
	movb	$0x07, %ah
	int	$0x1A
#endif

#if 0	/* commented out 2008-06-11 */
	/* clear VDS */
	andb	$0xD7, 0x47B
#endif

#ifndef STAGE1_5

	sti

	/* check the DUCE indicator */

	movl	0x5FC, %eax
	movl	%eax, %ecx
	testb	$0x0C, %al
	jz	1f
	orb	$0x0C, %al
	cmpl	$0x4543554C, %eax
	jne	1f
	andb	$0x0C, %cl	/* bit2=DUCE, bit3=chs-no-tune */
	orb	%cl, ABS(EXT_C(main)) + 5 /* at 0x8205 */
1:
	movw	$1, %bx		/* BL=1, BH=0 */

	testb	$0x02, ABS(EXT_C(main)) + 5	/* disable keyboard intervention? */
	jnz	4f			/* yes */

	movl	0x46c, %eax	/* initial tick */
	addl	$5, %eax	/* wait 0.27 seconds */
	pushl	%eax

2:
	/* checkkey 'c' */

	pushw	%bx
	movb	$0x01, %ah	/* checkkey */
	int	$0x16
	popw	%bx

	jz	1f		/* no keypress */

	/* getkey */

	pushw	%bx
	movb	$0x00, %ah	/* getkey */
	int	$0x16
	popw	%bx

	cmpw	$KEY_IC, %ax	/* insert char */
	jne	3f
	movb	$1, %bh		/* DEBUG_KEY pressed */
3:
	orb	$0x20, %al
	cmpb	$0x63, %al	/* is "C"? */
	jne	2b		/* no, get next key */

	/* "C" is pressed. */

	testb	$0x04, ABS(EXT_C(main)) + 5 /* at 0x8205, bit2=DUCE */
	jnz	2b

	/* Bypass all config files */
	movb	$0, %bl
	jmp	2b
1:

	popl	%eax
	pushl	%eax
	movl	0x46c, %ecx	/* current tick */
	cmpl	%eax, %ecx
	jnb	1f
	subl	$5, %eax
	cmpl	%eax, %ecx
	jnb	2b
	cmpl	$5, %ecx
	jb	2b
1:
	popl	%eax
4:
	movb	%bh, EXT_C(debug_boot)
#endif

	popw	%dx		/* DL=boot drive */

	//cli

//	pushw	%bx

#ifndef SUPPORT_DISKLESS
	/* save boot drive reference */
	ADDR32	movb	%dl, EXT_C(boot_drive)

//	testb	%bh, %bh	/* debug_boot? */
//	jz	1f
//	movw	$ABS(reset_disk_string),%si
//	call	print_message	/* will not change DX */
//1:
//	xorw	%ax, %ax
//	/* reset disk system (%ah = 0) */
//#ifdef STAGE1_5
//	int	$0x13
//#else
//	call	safe_int13
//#endif
//	movw	$ABS(reset_disk_failure_string),%si
//	jc	1f
//	movw	$ABS(reset_disk_success_string),%si
//1:
//	popw	%bx
//	pushw	%bx
//	testb	%bh, %bh	/* debug_boot? */
//	jz	1f
//	call	print_message	/* will not change DX */
//1:
#endif

//	popw	%bx

	xorw	%ax, %ax
	movw	%ax, %ds
	movw	%ax, %es

	testb	$0x02, ABS(EXT_C(main)) + 5	/* disable keyboard intervention? */
	jnz	4f			/* yes */

	pushw	%bx

	/* clear keyboard buffer */
2:
	movb	$0x01, %ah	/* checkkey */
	int	$0x16

	jz	1f		/* no keypress */

	movb	$0x00, %ah	/* getkey */
	int	$0x16
	jmp	2b
1:

	popw	%bx

4:

	/* transition to protected mode */
	DATA32	call EXT_C(real_to_prot)

	/* The ".code32" directive takes GAS out of 16-bit mode. */
	.code32

#ifndef STAGE1_5
	testb	%bl, %bl
	jnz	1f
	movl	$0, EXT_C(use_config_file)
1:

	/* before clearing the bss, we move preset_menu to 0x800 */

	movl	EXT_C(preset_menu), %eax

#if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL)
	cmpl	$__bss_start, %eax
#elif defined(HAVE_USCORE_EDATA_SYMBOL)
	cmpl	$_edata, %eax
#elif defined(HAVE_EDATA_SYMBOL)
	cmpl	$edata, %eax
#else
#error no bss starting address
#endif
	jnz	1f		/* use old bootp for diskless */

	xorl	%eax, %eax
	cmpb	%al, EXT_C(config_file)	/* AL == 0 */
	jnz	2f
	movl	EXT_C(saved_entryno), %ebx
	testl	%ebx, %ebx
	jnz	3f	/* use menu embedded in commnad-line of grub.exe */
2:
	/* use builtin preset_menu */

	/* set the starting address of the preset_menu */

#if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL)
	movl	$__bss_start, %esi
#elif defined(HAVE_USCORE_EDATA_SYMBOL)
	movl	$_edata, %esi
#elif defined(HAVE_EDATA_SYMBOL)
	movl	$edata, %esi
#else
#error no bss starting address
#endif

	cld
	addl	$16, %esi	/* skip 4 bytes of B0 02 1A CE */
				/* skip 4 bytes of reserved */
				/* skip 4 bytes of reserved */
				/* skip 4 bytes of zeroes */

	movl	$0x400, %ecx	/* move 4KB of the menu ... */
	movl	$0x800, %edi	/* ... to 0x800 */
	repz movsl

3:
	movl	$0x0800, EXT_C(preset_menu)	/* use new menu at 0x800 */
1:
#endif /* !STAGE1_5 */

	/* if force_cdrom_as_boot_device==0, we are running by configfile, so we do not clear bss */
	cmpl	$0, EXT_C(force_cdrom_as_boot_device)
	je	1f

	/* clean out the bss */

	/* set %edi to the bss starting address */
#if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL)
	movl	$__bss_start, %edi
#elif defined(HAVE_USCORE_EDATA_SYMBOL)
	movl	$_edata, %edi
#elif defined(HAVE_EDATA_SYMBOL)
	movl	$edata, %edi
#else
#error no bss starting address
#endif

	/* set %ecx to the bss end */
#if defined(HAVE_END_SYMBOL)
	movl	$end, %ecx
#elif defined(HAVE_USCORE_END_SYMBOL)
	movl	$_end, %ecx
#else
#error no bss ending address
#endif

	/* compute the bss length */
	subl	%edi, %ecx

	/* zero %al */
	xorb	%al, %al

	/* set the direction */
	cld

	/* clean out */
	rep
	stosb

1:
	/*
	 *  Call the start of main body of C code, which does some
	 *  of it's own initialization before transferring to "cmain".
	 */
	call EXT_C(init_bios_info)
	call EXT_C(cmain)


/*
 *  This call is special...  it never returns...  in fact it should simply
 *  hang at this point!
 */

ENTRY(stop)
	call	EXT_C(prot_to_real)

	/*
	 * This next part is sort of evil.  It takes advantage of the
	 * byte ordering on the x86 to work in either 16-bit or 32-bit
	 * mode, so think about it before changing it.
	 */

	/* No external program ever calls HARD_STOP. HARD_STOP is only called
	 * by the asm.S itself, and all calls are from real mode. So we
	 * could(and should) use .code16 here clearly.
	 */

	.code16

//ENTRY(hard_stop)
hard_stop:
	sti
	hlt
	//jmp EXT_C(hard_stop)
	jmp hard_stop

VARIABLE(configfile_opened)
	.long	0
#ifndef STAGE1_5

/* If preset_menu == __bss_start, the new menu at end of pre_stage2 will be used. */

VARIABLE(preset_menu)
#if defined(PRESET_MENU_STRING)
#if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL)
	//.word	(__bss_start - main) & 0x0F, (__bss_start - main) >> 4
	.long	__bss_start
#elif defined(HAVE_USCORE_EDATA_SYMBOL)
	//.word	(_edata - main) & 0x0F, (_edata - main) >> 4
	.long	_edata
#elif defined(HAVE_EDATA_SYMBOL)
	//.word	(edata - main) & 0x0F, (edata - main) >> 4
	.long	edata
#else /* ! HAVE_EDATA_SYMBOL */
#error no bss starting address
#endif /* ! HAVE_EDATA_SYMBOL */
#else  /* ! PRESET_MENU_STRING */
	.long	0
#endif /* PRESET_MENU_STRING */

VARIABLE(debug_boot)
	.long	0
#endif /* ! STAGE1_5 */
	.code16

	/* real mode print string */

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

print_message:
1:
	sti		/* for hardware interrupt or watchdog */
	cld
	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 */
	ret

reset_disk_string:
	.ascii	"Reseting the boot drive... \0"
reset_disk_success_string:
	.ascii	"Success.\r\n\0"
reset_disk_failure_string:
	.ascii	"Failure!\r\n\0"

bochs_copygrght_string:
	.ascii	"(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team.\0"
ENTRY(bios_id)
	.long	0	/* 1 for bochs, 0 for unknown. */

	.code32


#ifndef STAGE1_5

#ifdef FSYS_PXE
/* unsigned long pxe_scan(void)
 *
 * scan pxe runtime
 */
ENTRY(pxe_scan)
	pushl	%ebx

	call	EXT_C(prot_to_real)
	.code16

	sti		/* for hardware interrupt or watchdog */
	movw	$0x5650, %ax
	int	$0x1A
	cmpw	$0x564E, %ax
	jnz	1f
	cmpl	$0x4E455850, %es:(%bx)		// PXEN(V+)
	jnz	1f
	cmpw	$0x201, %es:6(%bx)		// API version
	jb	1f
	lesw	%es:0x28(%bx), %bx		// !PXE structure
	cmpl	$0x45585021, %es:(%bx)		// !PXE
	jnz	1f
	movw	%es, %cx
	jmp	2f
1:
	xorw	%bx, %bx
	xorw	%cx, %cx
2:

	DATA32  call EXT_C(real_to_prot)
	.code32

	xorl	%eax, %eax
	movw	%cx, %ax
	shll	$4, %eax
	andl	$0xFFFF, %ebx
	addl	%ebx, %eax
	jz	3f

	movl	0x10(%eax), %ebx
	movl	%ebx, EXT_C(pxe_entry)
	xorl	%ecx, %ecx
	movl	0x2A(%eax), %ebx
	cmpl	0x32(%eax), %ebx
	ja	1f
	movl	0x32(%eax), %ebx
	movw	0x36(%eax), %cx
	jmp	2f
1:
	movw	0x2E(%eax), %cx
2:
	addl	%ecx, %ebx
	addl	$1023, %ebx
	shrl	$10, %ebx
	movw	%bx, EXT_C(pxe_freemem)

3:
	popl	%ebx
	ret

/* int pxe_call(int func,void* data)
 *
 * PXE function call
 */
ENTRY(pxe_call)
	pushl	%ebp
	movl	%esp, %ebp
	pushl	%esi
	pushl	%edi
	pushl	%ebx

	movl	8(%ebp), %ecx
	movl	12(%ebp), %edx
	movl	%edx, %eax
	andl	$0xF, %eax
	shrl	$4, %edx
	shll	$16, %edx
	addl	%eax, %edx
	movl	EXT_C(pxe_entry), %ebx

	call	EXT_C(prot_to_real)
	.code16

	sti		/* for hardware interrupt or watchdog */
	pushl	%ebx
	pushl	%edx
	pushw	%cx
	movw	%sp, %bx
	lcall	*%ss:6(%bx)
	cld
	addw	$10, %sp
	movw	%ax, %cx

	DATA32  call EXT_C(real_to_prot)
	.code32

	movzwl	%cx, %eax

	popl	%ebx
	popl	%edi
	popl	%esi
	popl	%ebp
	ret

#if PXE_FAST_READ

/* int pxe_fast_read(void* data,int num)
 *
 * Read multiple packets
 */
ENTRY(pxe_fast_read)
	pushl	%ebp
	movl	%esp, %ebp
	pushl	%esi
	pushl	%edi
	pushl	%ebx

	movl	8(%ebp), %edx
	movl	12(%ebp), %esi	/* num */
	movl	EXT_C(pxe_blksize), %edi
	movl	%edx, %eax
	andl	$0xF, %eax
	shrl	$4, %edx
	shll	$16, %edx
	addl	%eax, %edx
	movl	EXT_C(pxe_entry), %ebx

	call	EXT_C(prot_to_real)
	.code16

	sti		/* for hardware interrupt or watchdog */
	movw	%si, %cx	/* num */
1:
	pushw	%cx		/* blocks to read */
	pushw	%di		/* block size */
	pushl	%ebx		/* pxe_entry */
	pushl	%edx		/* data pointer */
	pushw	$0x22		// PXENV_TFTP_READ
	movw	%sp, %bp
	lesw	2(%bp), %si	/* ES:SI=data pointer */

	movw	%di, %es:4(%si)	/* block size */
	lcall	*6(%bp)
	movw	%sp, %bp
	lesw	2(%bp), %si	/* ES:SI=data pointer */
	popw	%dx		// PXENV_TFTP_READ
	popl	%edx		/* data pointer */
	popl	%ebx		/* pxe_entry */
	popw	%di		/* block size */
	popw	%cx		/* blocks to read */
	cld

	cmpw	$0, %es:(%si)
	jnz	2f
	movw	%es:4(%si), %bp
//	cmpw	$512, %dx
//	jb	2f
//	cmpw	%di, %dx
//	ja	2f
//	je	3f
//	cmpl	EXT_C(filemax), %edx
//	jae	3f
//	movl	%edx, EXT_C(pxe_blksize)
//	movl	%edx, %edi
//3:
	addw	%bp, %es:6(%si)
	cmpw	%di, %bp
	jb	2f
	loop	1b

2:
	//addw	$10, %sp
	movw	%ax, %cx

	DATA32  call EXT_C(real_to_prot)
	.code32

	movzwl	%cx, %eax

	popl	%ebx
	popl	%edi
	popl	%esi
	popl	%ebp
	ret
#endif /* PXE_FAST_READ */
#endif /* FSYS_PXE */

/*
 * stop_floppy()
 *
 * Stops the floppy drive from spinning, so that other software is
 * jumped to with a known state.
 */
ENTRY(stop_floppy)
	pushal
	call	EXT_C(prot_to_real)
	.code16
	sti	#; added 2006-11-30
	xorb	%dl, %dl
#if defined(STAGE1_5) || 1
	int	$0x13
#else
	call	safe_int13
#endif
	DATA32  call EXT_C(real_to_prot)
	.code32
	popal
	ret

/*
 * grub_reboot()
 *
 * Reboot the system. At the moment, rely on BIOS.
 */
ENTRY(grub_reboot)
	call	EXT_C(prot_to_real)

	.code16

	/* cold boot */

	//sti		/* needn't enable interrupt here. comment it out */

	movw	$0x0472, %di
	movw	%ax, (%di)
	ljmp	$0xFFFF, $0x0000

	.code32

/*
 * grub_halt(int no_apm)
 *
 * Halt the system, using APM if possible. If NO_APM is true, don't use
 * APM even if it is available.
 */
ENTRY(grub_halt)
	/* get the argument */
	movl	4(%esp), %eax

	/* see if zero */
	testl	%eax, %eax
	jnz	EXT_C(stop)

	call	EXT_C(prot_to_real)

	.code16

	//sti		/* this is not needed here, so comment it out. */
	sti	#; added 2006-11-30

	/* detect APM */
	movw	$0x5300, %ax
	xorw	%bx, %bx
	int	$0x15
	//jc	EXT_C(hard_stop)
	jc	hard_stop
	/* don't check %bx for buggy BIOSes... */

	/* disconnect APM first */
	movw	$0x5304, %ax
	xorw	%bx, %bx
	int	$0x15

	/* connect APM */
	movw	$0x5301, %ax
	xorw	%bx, %bx
	int	$0x15
	//jc	EXT_C(hard_stop)
	jc	hard_stop

	/* set APM protocol level - 1.1 or bust. (this covers APM 1.2 also) */
	movw	$0x530E, %ax
	xorw	%bx, %bx
	movw	$0x0101, %cx
	int	$0x15
	//jc	EXT_C(hard_stop)
	jc	hard_stop

	/* set the power state to off */
	movw	$0x5307, %ax
	movw	$1, %bx
	movw	$3, %cx
	int	$0x15

	/* shouldn't reach here */
	//jmp	EXT_C(hard_stop)
	jmp	hard_stop


/*
 * int check_64bit (void)
 *
 * Checks whether 64-bit mode is supported
 *
 * Stolen from a patch originaly intended for syslinux
 * (http://syslinux.zytor.com/archives/2007-January/007832.html)
 *
 * Copyright (C) 2007 Byron Stanoszek <gandalf@winds.org>
 *
 * Adapted to AT&T syntax by Robert Millan <rmh@aybabtu.com>
 */

ENTRY(check_64bit)

	.code32

	pushl	%ebp
	pushl	%ebx
	pushl	%edx

	/* Check if this CPU supports the CPUID command */
	pushfl
	pushfl
	popl	%eax
	movl	%eax, %ebx
	xorl	$(1 << 21), %eax	// CPUID bit
	pushl	%eax
	popfl
	pushfl
	popl	%eax
	popfl				// Restore the original flags
	xorl	%ebx, %eax
	jz	is_32bit

	/* Now check for the 64-bit flag in the CPU features byte ($0000_0001, edx)
	This is bit 30 for Intel CPUs, and bit 29 for AMD CPUs */
	movl	$0x00000000, %eax	// Find last Intel cpuid #
	cpuid
	cmpl	$0x00000000, %eax
	je	test_amd
	movl	$0x00000001, %eax	// Read Intel CPU flags
	cpuid
	btl	$30, %edx		// 64-bit if bit 30 is set
	jc	is_64bit

test_amd:
	movl	$0x80000000, %eax	// Find last AMD cpuid #
	cpuid
	cmpl	$0x80000000, %eax
	jbe	is_32bit
	movl	$0x80000001, %eax	// Read AMD CPU flags
	cpuid
	btl	$29, %edx		// 64-bit if bit 29 is set
	jnc	is_32bit

is_64bit:
	movl	$1, %eax
	popl	%edx
	popl	%ebx
	popl	%ebp
	ret
is_32bit:
	xorl	%eax, %eax
	popl	%edx
	popl	%ebx
	popl	%ebp
	ret


/*
 * int tpm_init (void)
 *      return non-zero for success and zero for failure.
 */
ENTRY(tpm_init)
	pushl	%ebp
	pushl	%ebx
	pushl	%esi
	pushl	%edi

	call	EXT_C(prot_to_real)	/* enter real mode */

	.code16

	sti

	movw	$0xBB00, %ax
	int	$0x1A
	testl	%eax, %eax
	jnz	1f			/* failure */
	cmpl	$0x41504354, %ebx	/* "TCPA" */
	jnz	1f			/* failure */
	cmpw	$0x102, %cx		/* TCG BIOS version 1.2 */
	jb	1f			/* failure */
	movw	%ax, %es		/* ES=0 */
	movw	$0xBB07, %ax		/* eax hi word=0 */
	movl	$0x00000200, %ecx	/* buffer size to hash */
	movl	$0x00000008, %edx	/* PCR index for the hashed result */
	xorl	%esi, %esi		/* place 0 into the event field */
	movl	$0x00007C00, %edi	/* ES:DI point to data buffer to hash */
	int	$0x1A

	testl	%eax, %eax
1:
	setz	%dl

	DATA32	call	EXT_C(real_to_prot)
	.code32

	movzbl	%dl, %eax

	popl	%edi
	popl	%esi
	popl	%ebx
	popl	%ebp
	ret


/* Catch CPU exceptions 0 - 7
 *	0	Divide
 *	1	Debug
 *	2	NMI
 *	3	Break point
 *	4	Overflow
 *	5	Bound
 *	6	Invalid Instruction
 *	7	no coprocessor
 */

set_fault_recovery_handler:

	.code16

	pushfw
	pushw	%ds
	pushw	%es
	pushaw

	/* backup int 00 - 07 */

	xorw	%ax, %ax
	movw	%ax, %ds
	movw	%ax, %es
	xorw	%si, %si
	movw	$ABS(int_00_07_vectors), %di
	movw	$16, %cx
	cld
	repz movsw

	/* set to new vector */

	xorw	%ax, %ax
	movw	%ax, %ds
	movw	%ax, %es
	xorw	%si, %si
	movw	$ABS(int_00_07_vectors), %di

	pushl	%eax

	xorw	%di, %di
	movl	$ABS(fault_recovery_handler), %eax	/* 0000:fault_recovery_handler */
	movw	$8, %cx
	cld
	repz stosl

	popl	%eax

	popaw
	popw	%es
	popw	%ds
	popfw
	ret

unset_fault_recovery_handler:

	.code16

	pushfw
	pushw	%ds
	pushw	%es
	pushaw

	/* restore int 00 - 07 */

	xorw	%ax, %ax
	movw	%ax, %ds
	movw	%ax, %es
	xorw	%di, %di
	movw	$ABS(int_00_07_vectors), %si
	movw	$16, %cx
	cld
	repz movsw

	popaw
	popw	%es
	popw	%ds
	popfw
	ret

	.align	4

int_00_07_vectors:
	.space	32

original_registers:
	.space	48

safe_int13:

	.code16

	/* setup our fault recovery handler */

	call	set_fault_recovery_handler

	/* backup old registers. Note: CS=0 */

	movw	%ds, %cs:ABS(original_registers)
	movw	%es, %cs:ABS(original_registers) + 4
	movw	%ss, %cs:ABS(original_registers) + 8
	movl	%esp, %cs:ABS(original_registers) + 12
	movl	%eax, %cs:ABS(original_registers) + 16
	movl	%ebx, %cs:ABS(original_registers) + 20
	movl	%ecx, %cs:ABS(original_registers) + 24
	movl	%edx, %cs:ABS(original_registers) + 28
	movl	%esi, %cs:ABS(original_registers) + 32
	movl	%edi, %cs:ABS(original_registers) + 36
	movl	%ebp, %cs:ABS(original_registers) + 40

	pushw	%bp
	pushw	%ax
	movw	%sp, %bp
	movw	4(%bp), %ax
	movw	%ax, %cs:ABS(original_registers) + 44	#; return IP
	popw	%ax
	popw	%bp

	int	$0x13

	call	unset_fault_recovery_handler

	ret

fault_recovery_handler:

	.code16

	/* restore old registers. Note: CS=0 */

	movw	%cs:ABS(original_registers), %ds
	movw	%cs:ABS(original_registers) + 4, %es
	movw	%cs:ABS(original_registers) + 8, %ss
	movl	%cs:ABS(original_registers) + 12, %esp
	movl	%cs:ABS(original_registers) + 16, %eax
	movl	%cs:ABS(original_registers) + 20, %ebx
	movl	%cs:ABS(original_registers) + 24, %ecx
	movl	%cs:ABS(original_registers) + 28, %edx
	movl	%cs:ABS(original_registers) + 32, %esi
	movl	%cs:ABS(original_registers) + 36, %edi
	movl	%cs:ABS(original_registers) + 40, %ebp

	/* stack is available, so we can push and pop. */

	pushw	%bp
	pushw	%ax
	movw	%sp, %bp
	movw	%cs:ABS(original_registers) + 44, %ax	#; return IP
	movw	%ax, 4(%bp)
	popw	%ax
	popw	%bp

	pushl	$1		#; CF=1 indicating error
	popfl			#; CLD, CLI, and many more...

	call	unset_fault_recovery_handler

	ret

	/* never come here. */

	iret

	.code32

/*
 * set_int15_handler(void)
 *
 * Set up int15_handler.
 */
ENTRY(set_int15_handler)

	.code32

	pushl	%edi

	/* save the original int15 handler */
	movl	$0x54, %edi
#if 0
	movw	(%edi), %ax
	movw	%ax, ABS(int15_offset)
	movw	2(%edi), %ax
	movw	%ax, ABS(int15_segment)

	/* save the new int15 handler */
	movw	$ABS(int15_handler), %ax
	movw	%ax, (%edi)
	xorw	%ax, %ax
	movw	%ax, 2(%edi)
#else
	movl	(%edi), %eax
	movl	%eax, ABS(int15_offset)

	/* set the new int15 handler */
	movl	$ABS(int15_handler), %eax
	stosl
#endif

	popl	%edi
	ret


/*
 * unset_int15_handler(void)
 *
 * Restore the original int15 handler
 */
ENTRY(unset_int15_handler)

	.code32

	pushl	%edi

	/* check if int15_handler is set */
	movl	$0x54, %edi
#if 0
	movw	$ABS(int15_handler), %ax
	cmpw	%ax, (%edi)
	jne	1f
	xorw	%ax, %ax
	cmpw	%ax, 2(%edi)
	jne	1f

	/* restore the original */
	movw	ABS(int15_offset), %ax
	movw	%ax, (%edi)
	movw	ABS(int15_segment), %ax
	movw	%ax, 2(%edi)
#else
	movl	$ABS(int15_handler), %eax
	cmpl	%eax, (%edi)
	jne	1f

	/* restore the original */
	movl	ABS(int15_offset), %eax
	stosl
#endif

1:
	popl	%edi
	ret


/*
 * Translate a key code to another.
 *
 * Note: This implementation cannot handle more than one length
 * scancodes (such as Right Ctrl).
 */
	.code16
int15_handler:
	/* if non-carrier, ignore it */
	jnc	1f
	/* check if AH=4F */
	cmpb	$0x4F, %ah
	jne	1f

	/* E0 and E1 are special */
	cmpb	$0xE1, %al
	je	4f
	cmpb	$0xE0, %al
	/* this flag is actually the machine code (je or jmp) */
int15_skip_flag:
	je	4f

	pushw	%bp
	movw	%sp, %bp

	pushw	%bx
	pushw	%dx
	pushw	%ds
	pushw	%si

	/* save bits 0-6 of %al in %dl */
	movw	%ax, %dx
	andb	$0x7f, %dl
	/* save the highest bit in %bl */
	movb	%al, %bl
	xorb	%dl, %bl
	/* set %ds to 0 */
	xorw	%ax, %ax
	movw	%ax, %ds
	/* set %si to the key map */
	movw	$ABS(EXT_C(bios_key_map)), %si

	/* find the key code from the key map */
2:
	lodsw
	/* check if this is the end */
	testw	%ax, %ax
	jz	3f
	/* check if this matches the key code */
	cmpb	%al, %dl
	jne	2b
	/* if so, perform the mapping */
	movb	%ah, %dl
3:
	/* restore %ax */
	movw	%dx, %ax
	orb	%bl, %al
	/* make sure that CF is set */
	orw	$1, 6(%bp)
	/* restore other registers */
	popw	%si
	popw	%ds
	popw	%dx
	popw	%bx
	popw	%bp
	iret

4:
	/* tricky: jmp (0x74) <-> je (0xeb) */
	xorb	$(0x74 ^ 0xeb), ABS(int15_skip_flag)
1:
	/* just cascade to the original */
	/* ljmp */
	.byte	0xea
int15_offset:	.word	0
int15_segment:	.word	0

	.code32

	.align	4
ENTRY(bios_key_map)
	.space	(KEY_MAP_SIZE + 1) * 2


/*
 * set_int13_handler(map)
 *
 * Copy MAP to the drive map and set up int13_handler.
 */
ENTRY(set_int13_handler)

	.code32

	pushl	%ebp
	movl	%esp, %ebp

	pushl	%edi
	pushl	%esi

	/* copy MAP to the drive map */
	movl	$(DRIVE_MAP_SIZE * DRIVE_MAP_SLOT_SIZE / 4), %ecx
	movl	$ABS(EXT_C(hooked_drive_map)), %edi
	movl	8(%ebp), %esi
	cld
	repz movsl

//	Now initialized early at the beginning of this file
//
//	/* save the original int13 handler */
//	movl	$0x4c, %edi
//	movl	(%edi), %eax
//	movl	%eax, ABS(ROM_int13)

	/* decrease the lower memory size and set it to the BIOS memory */
	movl	$0x413, %edi
	movl	%edi, %esi

	lodsw
	/* KBytes that int13 handler occupies */
	subb	ABS(int13_handler), %al
	stosw

	/* compute the segment(high word) */
	shll	$(16 + 6), %eax

	/* the offset(low word) should be 0x100 */
	movw	$0x100, %ax

	/* save the new int13 handler */
	movl	$0x4c, %edi
	stosl

	/* EDI points to the destination int13 handler in the reserved area */
	movl	%eax, %edi		/* the int13 vector just saved */
	shrl	$12, %edi		/* get base address of segment */

	/* set ESI to the drive map */
	movl	$ABS(EXT_C(hooked_drive_map)), %esi
	movl	$(DRIVE_MAP_SIZE), %ecx
1:
	cmpb	$0xff, 1(%esi)		/* Is there a mapped memdrive? */
	jne	2f			/* No. Try next slot */
	testb	$0x40, 5(%esi)		/* Is To_DRIVE a CDROM? */
	jz	1f			/* No. Memdrive indeed. Hook int15 */
2:
	/* try next slot */
	addl	$DRIVE_MAP_SLOT_SIZE, %esi
	loop	1b
	jmp	2f			/* no memdrives, don't hook int15 */

1:
	/* save the new int15 handler */
	movw	$(int15_e820_handler - int13_handler), %ax	/* segment still in high word */
	movl	%eax, 0x54

2:
	/* copy int13_handler to the reserved area */
	movl	$ABS(int13_handler), %esi
	movl	$((int13_handler_end - int13_handler) / 4), %ecx
	cld
	repz movsl

	popl	%esi
	popl	%edi
	popl	%ebp
	ret

/* int
 * unset_int13_handler(check_status_only)
 *
 * Restore the original int13 handler
 *
 * Return 0 for success and non-zero for failure.
 */
ENTRY(unset_int13_handler)

	.code32

	pushl	%ebp
	movl	%esp, %ebp

	pushl	%edi

	/* check if int13_handler is set */
	movl	$0x413, %edi
	movw	(%edi), %ax
	cmpw	$640, %ax
	jae	1f		#; needn't unset
//	cmpw	$632, %ax
//	jb	1f
	shll	$(16 + 6), %eax

	/* the offset(low word) should be 0x100 */
	movw	$0x100, %ax

	cmpl	%eax, 0x4c
	jne	1f		#; not hooked, unset failure
	movl	%eax, %edi
	shrl	$12, %edi	/* segment base address */

	/* check our int13 signature "$INT13SFGRUB4DOS" */
	cmpl	$0x544E4924, 0x103(%edi)	/* $INT */
	jnz	1f
	cmpl	$0x46533331, 0x107(%edi)	/* 13SF */
	jnz	1f
	cmpl	$0x42555247, 0x10B(%edi)	/* GRUB */
	jnz	1f
	cmpl	$0x534F4434, 0x10F(%edi)	/* 4DOS */
	jnz	1f

	//cmpl	$0x9A000000, 0x1C(%edi)		/* old int 13 */
	cmpl	$0x5A000000, 0x1C(%edi)		/* old int 13 */
	jb	1f

	//cmpl	$0x9A000000, 0x0C(%edi)		/* old int 15 */
	cmpl	$0x5A000000, 0x0C(%edi)		/* old int 15 */
	jb	1f

	movl	ABS(ROM_int13), %eax
	cmpl	0x1C(%edi), %eax
	jnz	1f

	movl	ABS(EXT_C(ROM_int15)), %eax
	cmpl	0x0C(%edi), %eax
	jnz	1f

	xorl	%eax, %eax

	cmpl	%eax, 8(%ebp)
	jnz	1f

	/* increase the lower memory size */

	movzbw	(%edi), %ax
	addw	%ax, 0x413

	/* restore the original int15 handler */
	movl	ABS(EXT_C(ROM_int15)), %eax
	movl	%eax, 0x54

	/* restore the original int13 handler */
	movl	ABS(ROM_int13), %eax
	movl	%eax, 0x4c

	xorl	%eax, %eax			/* success */
1:
	/* return non-zero for failure */
	popl	%edi
	popl	%ebp
	ret


/*
 * Map a drive to another drive or a disk image file.
 */

	.code16

	.align	4

ENTRY(bios_drive_map)
	.space	(DRIVE_MAP_SIZE + 1) * DRIVE_MAP_SLOT_SIZE

	/* align it this way so that int13_handler can be used as a segment
	 * base address. The `cdrom' command requires this.
	 */
	.align	16

int13_handler:

	/* memory size in K that int13 handler uses. */

	.byte	((int13_handler_end - int13_handler + 0x3ff) / 0x400)

	/* 1-byte space reserved. */

	. = int13_handler + 0x2

ENTRY(atapi_dev_count)	.byte	0
ENTRY(min_cdrom_id)	.byte	0xE0

	. = int13_handler + 0x4

	/* Signature */
	.ascii	"G4DS"		# Please don't use this signature any longer.
				# This field might be used for other purposes.
				# Use signature at offset 0x103 instead.

	. = int13_handler + 0x08

	/* Version number */
	.word	1

	. = int13_handler + 0x0A

VARIABLE(floppies_orig)
	.byte	0			/* original value at 0040:0010 */

	. = int13_handler + 0x0B

VARIABLE(harddrives_orig)
	.byte	0			/* original value at 0040:0075 */

	. = int13_handler + 0x0C

VARIABLE(ROM_int15)
	.long	0			/* original int15 vector */

	. = int13_handler + 0x10

	/* 12-byte space reserved. */

	. = int13_handler + 0x1C

ROM_int13:	.long	0		/* original int13 vector */

	. = int13_handler + 0x20	/* drive map table begins at 0x20 */

ENTRY(hooked_drive_map)
	.space	(DRIVE_MAP_SIZE + 1) * DRIVE_MAP_SLOT_SIZE

	/* 8-byte space reserved. */

	. = int13_handler + 0x100	/* real int13 handler entry at 0x100 */

	jmp	1f

	. = int13_handler + 0x103

	/*******************************************************************/
	/* ----------------- SafeMBRHook structure begin ----------------- */

	.ascii	"$INT13SF"		/* Win9x use this! Don't touch! */

	.ascii	"GRUB4DOS"		/* 8-byte Vender ID */

	. = int13_handler + 0x113

ROM_int13_dup:		/* Win9x Safe-MBR-Hook structure requires this! */

	.long	0

	. = int13_handler + 0x117

VARIABLE(safe_mbr_hook)

	.long	0x00000001		/* safe MBR hook flag */

	/* -----------------  SafeMBRHook structure end  ----------------- */
	/*******************************************************************/

	/* But Win9x may expect additional data after SafeMBRHook structure.
	 * This is undocumented, and mysterious. If this area is not what
	 * Win9x expected, Win9x could hang.
	 */

	. = int13_handler + 0x11B

VARIABLE(int13_scheme)

	/* bit 0 controls how we access sectors in protected mode.
	 * bit0=0: use pushf and far call for ROM int13 service.
	 * bit0=1: use the `int $0x13' instruction.
	 */
	.long	0x00000001
	
	. = int13_handler + 0x120

VARIABLE(saved_pxe_ip)

	.long	0

	. = int13_handler + 0x124

VARIABLE(saved_pxe_mac)
	
	.byte	0, 0, 0, 0, 0, 0

	/* space reserved. */

	. = int13_handler + 0x140

#if 0
1:
	/* backup far return address */
	popl	%cs:(int13_old_cs_ip - int13_handler)

	/* build new stack: flags */
	popw	%cs:(int13_handler_end - 2 - int13_handler)

	/* backup old stack */
	movw	%sp, %cs:(int13_old_sp - int13_handler)
	movw	%ss, %cs:(int13_old_ss - int13_handler)

	/* build new stack pointer */
	movw	$(int13_handler_end - 2 - int13_handler), %cs:(int13_new_sp - int13_handler)
	movw	%cs, %cs:(int13_new_ss - int13_handler)

	/* switch to new stack */
	lssw	%cs:(int13_new_sp - int13_handler), %sp

	//pushfw			/* flags already on stack */
	pushw	%cs
	call	1f

	/* restore old stack */
	lssw	%cs:(int13_old_sp - int13_handler), %sp

	/* transfer control to caller */
	ljmp	*%cs:(int13_old_cs_ip - int13_handler)

	//iret				/* never reach here */

	.align	4

int13_old_cs_ip:	.long	0
int13_old_sp:		.word	0
int13_old_ss:		.word	0
int13_new_sp:		.word	0
int13_new_ss:		.word	0

#endif

1:

	/* backup far return address */
	popl	%cs:(int13_old_cs_ip - int13_handler)

	/* backup old flags */
	popw	%cs:(int13_old_flags - int13_handler)

#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		/* original stack layout for reference */
		/* BP+10: old Flags */
		/* BP+ 8: return CS */
		/* BP+ 6: return IP */
		/* BP+ 2: old EAX */
		/* BP+ 0: old BP */
		/* BP- 2: old SI */
		/* BP- 6: FROM, TO, Hmax, Smax */
		/* BP-10: TO_C, TO_H, TO_S */
		/* BP-14: StartLBA_Lo */
		/* BP-18: StartLBA_Hi */
		/* BP-22: S_count_Lo */
		/* BP-26: S_count_Hi */
#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	cld
	movl	%eax, %cs:(int13_old_eax - int13_handler)
	movl	%ebx, %cs:(int13_old_ebx - int13_handler)
	movl	%ecx, %cs:(int13_old_ecx - int13_handler)
	movl	%edx, %cs:(int13_old_edx - int13_handler)
	movl	%esi, %cs:(int13_old_esi - int13_handler)
	movl	%edi, %cs:(int13_old_edi - int13_handler)
	movl	%esp, %cs:(int13_old_esp - int13_handler)
	movl	%ebp, %cs:(int13_old_ebp - int13_handler)
	movw	%ds, %cs:(int13_old_ds - int13_handler)
	movw	%es, %cs:(int13_old_es - int13_handler)

	cmpb	$0x1a, %ah	/* PS/2 low level format ESDI drive!!!! */
	je	error_01_disable	/* disabled in any case */

	cmpb	$0, %cs:(EXT_C(atapi_dev_count) - int13_handler)
	jz	1f		/* no cdrom */
	cmpb	%cs:(EXT_C(min_cdrom_id) - int13_handler), %dl
	jb	1f		/* not cdrom drive */
	cmpb	%cs:(max_cdrom_id - int13_handler), %dl
	jbe	edd30_for_cdrom
1:

/****************************************************************************/
	/* find the drive number from the drive map */
	movw	$(EXT_C(hooked_drive_map) - int13_handler - DRIVE_MAP_SLOT_SIZE), %bp
1:
	addw	$DRIVE_MAP_SLOT_SIZE, %bp
	movw	%bp, %cs:(int13_new_bp - int13_handler)
	movl	%cs:(%bp), %eax		/* FROM, TO, Hmax, Smax */

	/* check if this is the end */
	testl	%eax, %eax
	jnz	3f			/* not end, continue */

	movl	%cs:8(%bp), %eax	/* StartLBA_Lo */
	testl	%eax, %eax
	jnz	3f			/* not end, continue */

	movl	%cs:16(%bp), %eax	/* S_count_Lo */
	testl	%eax, %eax
	jz	2f		/* map whole drive to itself signals the end */
3:
	/* Now this is a valid drive map slot */
	cmpb	%cs:(%bp), %dl	/* check if this matches the drive number */
	jne	1b		/* no, continue to check the next map */

	/* yes, found the map corresponding to drive DL */

	movw	%cs:2(%bp), %ax		/* AL=Hmax, AH=Smax */

	/* bit 1-5 already cleared for in-situ */
	//andb	$0xC1, %ah		/* clear bit 5 - bit 1 */

	testb	$0x80, %cs:7(%bp)	/* TO_S */
	jnz	1f			/* in-situ */

	/* non-zero StartLBA signals emulation */

	cmpl	$0, %cs:8(%bp)		/* StartLBA_Lo */
	jnz	drive_emulation

	/* StartLBA_Lo == 0 */

	/* if FROM and TO are both cdrom, this is a whole drive map. */
	testw	$0x4000, %cs:4(%bp)	/* TO_C bit 14=TO has 2048-byte cdrom sector */
	jz	3f			/* not cdrom */
	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jz	3f			/* not cdrom */
	movb	%cs:1(%bp), %dl		/* DL changed(!!) to TO_drive */
	cmpb	$0, %cs:(EXT_C(atapi_dev_count) - int13_handler)
	jz	2f		/* no cdrom by our driver, so it is a bios drive map. */
	cmpb	%cs:(EXT_C(min_cdrom_id) - int13_handler), %dl	/* TO_drive */
	jb	2f		/* not cdrom drive by our driver, so it is a bios drive map. */
	cmpb	%cs:(max_cdrom_id - int13_handler), %dl	/* TO_drive */
	ja	2f		/* not cdrom drive by our driver, so it is a bios drive map. */
	jmp	edd30_for_cdrom
3:

	/* S_count being not 1 signals emulation */

	movl	%cs:16(%bp), %eax	/* S_count_Lo */
	shrl	$1, %eax
	jnz	drive_emulation

	/* now StartLBA=0 and sector count=1(for whole disk) */

	/* if sectors per track > 1, this is force geometry screw. */

	movw	%cs:2(%bp), %ax		/* AL=Hmax, AH=Smax */
	testb	$62, %ah	/* Sectors > 1 means force geom, this -- */
	jnz	drive_emulation	/* -- also leads to drive emulation */

	/* ignore geom and directly map a whole drive */

1:
	/* bit 7 of the TO_S is for in-situ primary partition(alter MBR) */

	/* bits of AH:
	 *    7		bit set means readonly/fakewrite
	 *    6		bit set means disable LBA
	 *  5 - 1	bits already cleared(=0)
	 *    0		bit cleared means disable CHS
	 * So, if AH!=1, it is a restricted disk access;
	 * and if AH=1, it is a normal disk access.
	 */
	cmpb	$1, %ah
	je	1f
//	testb	$0x3F, %ah
//	jz	3f
//	testb	$0xC0, %ah
//	jz	1f
//3:
	call	restricted_map
1:
	/* map a whole drive, normal access */

	/* but if --in-situ was used, we should avoid writing the MBR! */
	testb	$0x80, %cs:7(%bp)	/* TO_S */
	jz	1f			/* not in-situ, allow write */
	testb	$0x40, %cs:7(%bp)	/* TO_S. bit 6 here means safe-boot */
	jz	1f			/* unsafe-boot, allow write */
	movw	%cs:(int13_old_eax - int13_handler), %ax
	cmpb	$0x03, %ah		/* is it CHS write? */
	jne	3f

	/* check if it is a write to MBR, i.e., C/H/S=0/0/1 */
	cmpw	$0x0001, %cx		/* C=0, S=1 */
	jne	1f
	cmpb	$0x00, %dh		/* H=0 */
	jne	1f

	/* deny the write and end the int 13 call */
	call	readonly_fakewrite	/* NO RETURN!! */
3:
	cmpb	$0x43, %ah		/* is it LBA write? */
	jne	1f			/* no, continue the normal access */

	/* check if it is a write to MBR, i.e., LBA=0 */

	xorl	%eax, %eax
	orl	12(%si), %eax		/* LBA_hi */
	orl	8(%si), %eax		/* LBA_lo */
	je	readonly_fakewrite	/* deny the write and end */

1:
	movb	%cs:1(%bp), %dl	/* Let DL access TO instead of FROM */
2:
	/* might map to itself, i.e., actually not mapped */

	movl	%cs:(int13_old_eax - int13_handler), %eax
	movl	%cs:(int13_old_ebp - int13_handler), %ebp  /* BP changed!! */

	call	backup_int13

	pushw	%cs:(int13_old_flags - int13_handler)
	popfw

	int	$0x13

	/* save the returned CF flag to int13_old_flags */

	jnc	1f
	orb	$1, %cs:(int13_old_flags - int13_handler)
	jmp	2f
1:
	andb	$0xFE, %cs:(int13_old_flags - int13_handler)
2:
	call	restore_int13

	/* restore BP!! */
	movw	%cs:(int13_new_bp - int13_handler), %bp
	xchgw	%ax, %cs:(int13_old_eax - int13_handler)
				/* old AX changed!! */

	/* check int13/AH=4Bh for cdrom */
	testw	$0x4000, %cs:4(%bp)	/* TO_C bit 14=TO has 2048-byte cdrom sector */
	jz	1f			/* not cdrom */
	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jz	1f			/* not cdrom */
	cmpb	$0x4B, %ah
	jne	1f
	cmpb	$0x01, %al
	ja	1f
	/* this is int13 function 0x4B00 or 0x4B01 */
	/* restore DS:SI, just in case they were changed by buggy int13 */
	movw	%cs:(int13_old_esi - int13_handler), %si
	movw	%cs:(int13_old_ds - int13_handler), %ds
	cmpb	$0x13, (%si)		/* packet size */
	jne	1f
	testb	$0x0F, 1(%si)		/* boot type(0=no emu) */
	jne	1f
	movb	%cs:1(%bp), %dl		/* just in case DL was changed by a buggy int13 */
	cmpb	%dl, 2(%si)
	jne	1f
	movb	%cs:(%bp), %dl		/* restore DL back to the FROM drive */
	movb	%dl, 2(%si)
	jmp	3f			/* return */
1:


#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	pushw	%cs:(int13_old_flags - int13_handler)
	popfw

	/* check if should restore(reversely map) the drive number */
	jc	1f		/* restore DL on error */

	/* alter MBR after read */

	testb	$0x80, %cs:7(%bp)	/* TO_S */
	jz	2f

	cmpb	$0x02, %ah
	je	4f		/* alter MBR */
	cmpb	$0x42, %ah
	je	4f		/* alter MBR */
2:
	cmpb	$0x08, %ah	/* int13 AH=08h, read drive parameters */
	jne	2f

	/* DL==number of drives, should not restore */
	xorw	%ax, %ax
	movw	%ax, %ds
	movb	0x475, %dl	/* DL=number of hard drives */
	testb	$0x80, %cs:(int13_old_edx - int13_handler) /* harddrive? */
	jnz	3f		/* yes, jump */
	//movw	$0x10, %bx	/* for floppy, refer to function 0x48 */
	movb	0x410, %al
	rorb	$1, %al
	cbw
	shlb	$1, %al
	shrb	$6, %al
	incw	%ax
	andb	%ah, %al
	movb	%al, %dl	/* DL=number of floppy drives */
	//lesw	0x0078, %di	/* point to int 1E floppy parameters */
	jmp	3f
2:
	cmpb	$0x15, %ah	/* read drive type */
	jne	2f
	testb	$0x80, %cs:(int13_old_edx - int13_handler) /* harddrive? */
	jnz	3f		/* yes, do not restore DL */
	/* restore DL for floppy int13 AH=15h call */
	jmp	1f
4:
	call	modify_in_situ
	movl	%cs:(int13_old_ebx - int13_handler), %ebx
	movl	%cs:(int13_old_ecx - int13_handler), %ecx
	movl	%cs:(int13_old_edx - int13_handler), %edx
	movl	%cs:(int13_old_edi - int13_handler), %edi
	movw	%cs:(int13_old_es - int13_handler), %es
2:
	movw	%cs:(%bp), %ax	/* get the drive mapping */
	/* try to restore DL if possible */
	cmpb	%al, %ah	/* check if the mapping was performed */
	je	3f		/* not performed, so need not restore DL */
	cmpb	%dl, %ah
	jne	3f		/* DL changed by int13, so do not restore */
1:
	movb	%cs:(int13_old_edx - int13_handler), %dl	/* restore DL back to the FROM drive */
3:
	/* return */

	/* BX, CX, DX, ES, DI are output registers and should not touch. */

	pushw	%cs:(int13_old_flags - int13_handler)
	popfw

	movl	%cs:(int13_old_esi - int13_handler), %esi
	movw	%cs:(int13_old_ds - int13_handler), %ds
	movl	%cs:(int13_old_eax - int13_handler), %eax
	movl	%cs:(int13_old_ebp - int13_handler), %ebp
	movw	%cs:(int13_old_esp - int13_handler), %sp
	ljmp	*%cs:(int13_old_cs_ip - int13_handler)

	.align	4

int13_old_cs_ip:	.long	0
int13_old_eax:		.long	0
int13_old_ebx:		.long	0
int13_old_ecx:		.long	0
int13_old_edx:		.long	0
int13_old_esi:		.long	0
int13_old_edi:		.long	0
int13_old_esp:		.long	0
int13_old_ebp:		.long	0
int13_old_ds:		.word	0
int13_old_es:		.word	0
int13_old_flags:	.word	0
int13_new_bp:		.word	0

/****************************************************************************/
restricted_map:
	movw	%cs:(int13_old_eax - int13_handler), %ax

	/* CHS read functions */
	cmpb	$0x02, %ah	/* read sectors */
	je	2f
	cmpb	$0x04, %ah	/* verify sectors, also a read operation */
	je	2f
	cmpb	$0x0a, %ah	/* read long sectors */
	je	2f
	cmpb	$0x0c, %ah	/* seek to cylinder */
	je	2f
	cmpb	$0x21, %ah	/* PS/1 and newer PS/2 - read multiple disk sectors */
	jne	1f
2:
	movb	%cs:3(%bp), %ah	/* AH=Smax */
	testb	$63, %ah	/* check if Sectors=0, i.e., disable CHS */
	jz	error_01_disable
	ret
1:
	/* CHS write functions */
	cmpb	$0x03, %ah	/* CHS write sectors */
	je	2f
	cmpb	$0x05, %ah	/* PC/XT/AT/EISA format tracks */
	je	2f
	cmpb	$0x06, %ah	/* PC/XT format tracks with bad sectors */
	je	2f
	cmpb	$0x07, %ah	/* PC/XT format multiple cylinders */
	je	2f
	cmpb	$0x0b, %ah	/* PC/XT/AT/EISA write sectors with ECC */
	je	2f
	cmpb	$0x0f, %ah	/* PC/XT/PS/1 write sector buffer */
	je	2f
	cmpb	$0x22, %ah	/* PS/1 and newer PS/2 - write multiple disk sectors */
	jne	1f
2:
	movb	%cs:3(%bp), %ah	/* AH=Smax */
	testb	$63, %ah	/* check if Sectors=0, i.e., disable CHS */
	jz	error_01_disable
	testb	$0x80, %ah	/* readonly access? */
	jnz	readonly_fakewrite
	ret
1:
	/* LBA read functions */
	cmpb	$0x41, %ah	/* Extensions - INSTALLATION CHECK */
	je	2f
	cmpb	$0x42, %ah	/* Extensions - EXTENDED READ */
	je	2f
	cmpb	$0x44, %ah	/* Extensions - verify sectors */
	je	2f
	cmpb	$0x45, %ah	/* Extensions - LOCK/UNLOCK DRIVE */
	je	2f
	cmpb	$0x46, %ah	/* Extensions - EJECT MEDIA */
	je	2f
	cmpb	$0x47, %ah	/* Extensions - EXTENDED SEEK */
	je	2f
	cmpb	$0x48, %ah	/* Extensions - GET DRIVE PARAMETERS */
	je	2f
	cmpb	$0x49, %ah	/* Extensions - detect media change */
	je	2f
	cmpb	$0x4a, %ah	/* Bootable CDROM - INITIATE DISK EMULATION */
	je	2f
	cmpb	$0x4b, %ah	/* Bootable CDROM - TERMINATE DISK EMULATION */
	je	2f
	cmpb	$0x4c, %ah	/* Bootable CDROM - INITIATE DISK EMULATION AND BOOT */
	je	2f
	cmpb	$0x4d, %ah	/* Bootable CDROM - RETURN BOOT CATALOG */
	je	2f
	cmpb	$0x4e, %ah	/* Extensions v2.1 - SET HARDWARE CONFIGURATION */
	jne	1f
2:
	movb	%cs:3(%bp), %ah	/* AH=Smax */
	testb	$64, %ah	/* disable LBA? */
	jnz	error_01_disable
	ret
1:
	/* LBA write functions */
	cmpb	$0x43, %ah	/* Extensions - EXTENDED WRITE */
	jne	1f
2:
	movb	%cs:3(%bp), %ah	/* AH=Smax */
	testb	$64, %ah	/* disable LBA? */
	jnz	error_01_disable
	testb	$0x80, %ah	/* readonly access? */
	jnz	readonly_fakewrite
1:
	/* no restrictions, return and continue */
	ret

error_01_disable:
	/* function not supported, or the input CHS is invalid */
	movl	%cs:(int13_old_eax - int13_handler), %eax
	movb	$0x01, %ah	/* invalid function call */
1:
	pushw	%cs:(int13_old_flags - int13_handler)
	popfw
	stc			/* error */
	jmp	1f

readonly_fakewrite:
	testb	$0x40, %cs:7(%bp)	/* bit 6 of TO_S */
	movl	%cs:(int13_old_eax - int13_handler), %eax
	movb	$0x03, %ah	/* write protection */
	jz	1b		/* read only */
	xorb	%ah, %ah	/* fake write */
	pushw	%cs:(int13_old_flags - int13_handler)
	popfw
	clc			/* write succeeded */
1:
	movl	%cs:(int13_old_esi - int13_handler), %esi
	movw	%cs:(int13_old_ds - int13_handler), %ds
	movl	%cs:(int13_old_ebp - int13_handler), %ebp
	movw	%cs:(int13_old_esp - int13_handler), %sp
	ljmp	*%cs:(int13_old_cs_ip - int13_handler)


/****************************************************************************/
drive_emulation:
	movw	%cs:2(%bp), %ax	/* AL=Hmax, AH=Smax */
	testb	$63, %ah	/* disable CHS? */
	jz	2f		/* yes, call restricted map */
	testb	$0xc0, %ah	/* readonly or disable LBA? */
	jz	1f
2:
	call	restricted_map
1:
	movw	%cs:(int13_old_eax - int13_handler), %ax

	testb	%ah, %ah	/* reset disk system, always succeed */
	jnz	1f
	/*clc*/			/* CF already cleared by TEST */
	jmp	int13_return
/****************************************************************************/
1:
	cmpb	$0x01, %ah	/* get status, always succeed */
	jnz	1f
	xorb	%ah, %ah
	jmp	int13_return
/****************************************************************************/
1:
	cmpb	$0x04, %ah	/* verify sectors, always succeed */
	jnz	1f
	xorb	%ah, %ah
	jmp	int13_return
/****************************************************************************/
1:
	cmpb	$0x09, %ah /* INITIALIZE CONTROLLER WITH DRIVE PARAMETERS */
	jnz	1f
	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jnz	error_01_invalid	/* CDROM */
	xorb	%ah, %ah
	jmp	int13_return
/****************************************************************************/
1:
	cmpb	$0x0c, %ah	/* SEEK TO CYLINDER */
	jnz	1f
	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jnz	error_01_invalid	/* CDROM */
	xorb	%ah, %ah
	jmp	int13_return
/****************************************************************************/
1:
	cmpb	$0x0d, %ah	/* reset hard disks */
	jnz	1f
	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jnz	error_01_invalid	/* CDROM */
	xorb	%ah, %ah
	jmp	int13_return
/****************************************************************************/
1:
	cmpb	$0x10, %ah	/* check if drive ready */
	jnz	1f
	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jnz	error_01_invalid	/* CDROM */
	xorb	%ah, %ah
	jmp	int13_return
/****************************************************************************/
1:
	cmpb	$0x11, %ah	/* recalibrate drive */
	jnz	1f
	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jnz	error_01_invalid	/* CDROM */
	xorb	%ah, %ah
	jmp	int13_return
/****************************************************************************/
1:
	cmpb	$0x14, %ah	/* CONTROLLER INTERNAL DIAGNOSTIC */
	jnz	1f
	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jnz	error_01_invalid	/* CDROM */
	xorb	%ah, %ah
	jmp	int13_return
/****************************************************************************/
1:
	cmpb	$0x05, %ah	/* format track */
	jnz	1f
	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jnz	error_01_invalid	/* CDROM */

	/* CH=cylinder number (bits 8,9 in high bits of CL)
	 * CL=high bits of cylinder number (bits 7,6)
	 * DH=head number
	 * DL=drive number
	 */

	xorb	%ah, %ah	/* do nothing but return success */
	jmp	int13_return
/****************************************************************************/
1:
	cmpb	$0x08, %ah	/* get drive parameters */
	jnz	1f
	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jnz	error_01_invalid	/* CDROM */
	pushw	%ds
	xorw	%ax, %ax
	movw	%ax, %ds
	testb	%dl, %dl	/* hard drive? */
	movb	0x475, %dl	/* DL=number of hard drives */
	js	2f		/* yes, jump */
	movw	$0x10, %bx	/* for floppy, refer to function 0x48 */
	movb	0x410, %al
	rorb	$1, %al
	cbw
	shlb	$1, %al
	shrb	$6, %al
	incw	%ax
	andb	%ah, %al
	xchgw	%ax, %dx	/* DL=number of floppy drives */
	lesw	0x0078, %di	/* point to int1E floppy parameters */
2:
	popw	%ds
	movw	%cs:2(%bp), %ax	/* AL=Hmax, AH=Smax */
	movb	%al, %dh	/* max head number */
	andb	$63, %ah
	movb	%ah, %cl	/* max sector number */

	/* max cylinder number */

	pushl	%edx
	pushl	%ecx
	movzbl	%dh, %eax
	movzbl	%cl, %ecx
	incl	%eax
	mull	%ecx		/* EDX=0, EAX=sectors per cylinder */
	xchgl	%eax, %ecx

	movl	%cs:16(%bp), %eax	/* S_count_Lo */
	cmpl	$4, %eax
	ja	2f
	cmpl	$0, %cs:8(%bp)		/* StartLBA_Lo */
	jnz	2f
	/* map whole drive, use TO_C instead. */
	movw	%cs:4(%bp), %ax		/* TO_C */
	jmp	3f
2:
	decl	%eax
	//xorl	%edx, %edx
	divl	%ecx		/* EAX=max cylinder number */
3:
	popl	%ecx
	popl	%edx

	movb	%al, %ch	/* low 8 bits of cylinder */
	shlb	$6, %ah		/* high 2 bits of cylinder */
	orb	%ah, %cl

	xorw	%ax, %ax
	jmp	int13_return
/****************************************************************************/
1:
	cmpb	$0x15, %ah	/* get disk type */
	jnz	1f
	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jnz	error_01_invalid	/* CDROM */
	testb	%dl, %dl	/* hard drive? */
	js	2f		/* yes, jump */
	movb	$0x02, %ah	/* floppy (or other removable drive) with change-line support */
	clc
	jmp	int13_return
2:
	movb	$0x03, %ah	/* hard disk */

	/* CX:DX=total number of sectors */
	movw	%cs:16(%bp), %dx	/* lo word of S_count_Lo */
	movw	%cs:18(%bp), %cx	/* hi word of S_count_Lo */
	clc
	jmp	int13_return
/****************************************************************************/
1:
	cmpb	$0x16, %ah
	jnz	1f
	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jnz	error_01_invalid	/* CDROM */
	xorb	%ah, %ah	/* AH=0 means disk not changed */
	jmp	int13_return
/****************************************************************************/
1:
	cmpb	$0x17, %ah	/* set floppy type for format */
	jnz	1f
	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jnz	error_01_invalid	/* CDROM */
	movb	$0x03, %al	/* 1.44M drive, 1.44M floppy */
	xorb	%ah, %ah
	jmp	int13_return
/****************************************************************************/
1:
	cmpb	$0x18, %ah	/* set media type for format */
	jnz	1f
	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jnz	error_01_invalid	/* CDROM */
	testb	%dl, %dl	/* hard drive? */
	js	error_01_invalid
	pushw	%ax
	xorw	%ax, %ax
	movw	%ax, %es
	movw	$0x0078, %di
	movl	%es:(%di), %eax
	movw	%ax, %di
	shrl	$0x10, %eax
	movw	%ax, %es
	popw	%ax
	xorb	%ah, %ah
	jmp	int13_return
/****************************************************************************/
1:
	/* Now AH is neither 0 nor 1 */

	testb	$0xfc, %ah	/* CHS read/write sectors */
	jnz	1f

	/* so AH is either 2(for read) or 3(for write) */

	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jnz	error_01_invalid	/* CDROM */

	cmpw	$0x0301, %ax	/* is it write 1 sector? */
	jne	2f
	cmpw	$0x0001, %cx	/* write to cylinder 0, sector 1? */
	jne	2f
	cmpb	$0x01, %dh	/* write to head 0 or 1? */
	ja	2f
	/* cmpw	$0xaa55, %es:0x1fe(%bx)
	je	2f */
	testb	%dl, %dl
	js	3f		/* protect hard disk head 0 and 1 */
	testb	%dh, %dh	/* write to floppy head 0? */
	jne	2f		/* no, write permitted */
3:
	testb	$0x40, %cs:7(%bp)	/* TO_S, bit 6=fake write(safeboot) */
	jnz	readonly_fakewrite	/* fake the write */
2:
	cmpb	$0x7F, %al	/* check if sectors exceed 127 */
	ja	error_01_invalid
	testb	%al, %al	/* read 0 sectors not allowed */
	jz	error_01_invalid
	testb	$63, %cl	/* beginning sector number 0 is invalid */
	jz	error_01_invalid
	movb	%cs:3(%bp), %ah	/* AH=Smax */
	andb	$63, %ah
	pushw	%cx
	andb	$63, %cl
	cmpb	%ah, %cl	/* CL should not > max sector number */
	popw	%cx
	ja	error_01_invalid
	movb	%cs:2(%bp), %ah	/* AH=Hmax */
	cmpb	%ah, %dh	/* DH should not > max head number */
	ja	error_01_invalid

	pushw	%cs
	popw	%ds		/* DS=CS */

	movw	$(EBIOS_disk_address_packet - int13_handler), %si
	movw	$0x0010, (%si)	/* Disk Address Packet length */
	movb	%al, 2(%si)	/* number of sectors to transfer */
	movw	%bx, 4(%si)	/* offset */
	movw	%es, 6(%si)	/* segment */
	xorl	%eax, %eax	/* EAX=0 */
	movb	%ah, 3(%si)	/* sectors_hi_reserved */
	movl	%eax, 12(%si)	/* zero out hi 32 bits */
	movb	%ch, %al	/* cylinder number lo 8 bits */
	movb	%cl, %ah	/* CL holds higher 2 bits */
	shrb	$6, %ah		/* AH lower holds the 2 bits */
				/* EAX=cylinder number, <1024 */
	pushl	%ebx		/* save EBX */

	xorl	%ebx, %ebx	/* EBX=0 */
	movb	%cs:2(%bp), %bl	/* BL=Hmax */
	incw	%bx		/* EBX=total heads, <=256 */

	pushl	%edx
	mull	%ebx		/* EDX=0, EAX=tracks before this cylinder */
	popl	%edx

	xorw	%bx, %bx	/* EBX=0 */
	movb	%dh, %bl	/* EBX=head number */
	addl	%ebx, %eax	/* EAX=tracks before this head */
	movb	%cs:3(%bp), %bl	/* Max sector number */
	andb	$63, %bl	/* EBX=sectors per track */

	pushl	%edx
	mull	%ebx		/* EDX=0, EAX=sectors before this head */
	popl	%edx

	movb	%cl, %bl	/* sector number */
	andb	$63, %bl
	decb	%bl
	addl	%ebx, %eax	/* EAX=lo 32 bits of logical sector number */
	movl	%eax, 8(%si)

	popl	%ebx		/* restore EBX */

disk_address_packet_ready:

	/* DS=CS */

	/* start-sector-number(LBA) in DAP is still for FROM_DRIVE */

/****************************************************************************/
	/*
	 * check if the request exceeds the boundary of the emulated disk.
	 *
	 * input:	DS:SI
	 * output:	CF=0, success, all sectors transferred
	 *		CF=1, failure, no sectors transferred
	 *		EAX changed
	 *
	 */
	cmpl	$1, %cs:16(%bp)		/* S_count_Lo */
	ja	2f
	cmpl	$0, %cs:8(%bp)		/* StartLBA_Lo */
	je	4f			/* map whole drive, no restrictions */
2:
	cmpl	$0, 12(%si)	/* hi 32-bit of the requested StartLBA */
	jnz	3f		/* non-zero is considered `too big' */
	movl	%cs:16(%bp), %eax	/* S_count_Lo */
	cmpl	%eax, 8(%si)	/* lo 32-bit of the requested StartLBA */
	jnb	3f
	subl	8(%si), %eax
	pushl	%ebx
	movzwl	2(%si), %ebx	/* requested sectors */
	cmpl	%ebx, %eax
	popl	%ebx
//	jnb	4f
//3:
//	stc
4:
/****************************************************************************/

	jc	3f		/* no sectors to transfer, fail */

	/* adjust start-sector-number(LBA) to access TO_DRIVE */
	movl	%cs:8(%bp), %eax	/* StartLBA_Lo */
	addl	%eax, 8(%si)
	adcl	$0, 12(%si)

	/* set drive number(TO_DRIVE) and function number(EBIOS) */
	movb	%cs:1(%bp), %dl		/* DL=TO_DRIVE */
	movb	%cs:(int13_old_eax - int13_handler + 1), %ah
				/* 0x02=read, 0x03=write */
	orb	$0x40, %ah	/* 0x42=EXT_read, 0x43=EXT_write */

	call	real_int13_service

	///* restore original start-sector-number(in the DAP) */
	//pushfw
	//movl	%cs:8(%bp), %eax	/* StartLBA_Lo */
	//subl	%eax, 8(%si)
	//sbbl	$0, 12(%si)
	//popfw

	jc	3f			/* failure */

	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jnz	4f			/* CDROM */
	movw	$(EBIOS_disk_address_packet - int13_handler), %si
	pushw	%cs
	popw	%ds
	/* restore original start-sector-number(in the DAP) */
	movl	%cs:8(%bp), %eax	/* StartLBA_Lo */
	subl	%eax, 8(%si)
	sbbl	$0, 12(%si)
	call	modify_boot_sectors
4:
	movb	%cs:(int13_old_eax - int13_handler), %al
	xorb	%ah, %ah
	jmp	4f			/* success */

3:
	movw	$0x400, %ax	/* no sectors transferred */
	/* write back sectors transferred to original DAP for LBA access */
	cmpb	$0x40, %cs:(int13_old_eax - int13_handler + 1)
	jb	4f		/* CF=1, function 02 or 03, not an LBA call */

	movw	%cs:(int13_old_esi - int13_handler), %si
	movw	%cs:(int13_old_ds - int13_handler), %ds

	/* DS:SI -> original DAP */
	movb	%al, 2(%si)
	stc				/* failure */
4:
	movl	%cs:(int13_old_edx - int13_handler), %edx
	movl	%cs:(int13_old_ebx - int13_handler), %ebx
	movl	%cs:(int13_old_ecx - int13_handler), %ecx
	movl	%cs:(int13_old_edi - int13_handler), %edi
	movw	%cs:(int13_old_es - int13_handler), %es
	jmp	int13_return
/****************************************************************************/
1:
	cmpb	$0x41, %ah	/* EBIOS installation check */
	jnz	1f
	cmpw	$0x55aa, %bx
	jnz	error_01_invalid
//	testb	%dl, %dl
//	jns	error_01_invalid
	movw	$0xaa55, %bx
	movb	$0x21, %ah	/* major version 2.1(EDD-1.1) */
	movw	$0x01, %cx	/* support functions 42h,43h,44h,47h,48h */
	clc
	jmp	int13_return
/****************************************************************************/
1:
	cmpb	$0x42, %ah	/* EBIOS read sectors */
	jz	2f
	cmpb	$0x43, %ah	/* EBIOS write sectors */
	jnz	1f

//	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
//	jz	2f
//
//	/* CDROM */
//	movb	$0x03, %ah	/* write protection */
//	stc			/* error */
//	jmp	int13_return
2:
	/* get old SI, disk address packet */
	movw	%cs:(int13_old_esi - int13_handler), %si
	movl	(%si), %eax	/* packet length, sectors, etc. */
	testb	%ah, %ah
	jnz	error_01_invalid
	testb	$0xf0, %al
	jz	error_01_invalid
	shrl	$16, %eax
	testw	$0xff80, %ax
	jnz	error_01_invalid
	testb	%al, %al
	jz	error_01_invalid

	/* copy disk address packet to EBIOS_disk_address_packet */

	pushw	%es
	pushw	%cx
	pushw	%di

	movw	%cs, %ax
	movw	%ax, %es	/* ES=CS */

	movw	$(EBIOS_disk_address_packet - int13_handler), %di
	movw	$8, %cx		/* will copy only 16 bytes! */
	cld
	repz movsw

	popw	%di
	popw	%cx
	popw	%es

	/* set DS:SI */

	movw	%cs, %ax
	movw	%ax, %ds	/* DS=CS */
	movw	$(EBIOS_disk_address_packet - int13_handler), %si

	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jz	2f
	shlb	$2, 2(%si)
	movl	8(%si), %eax
	shldl	$2, %eax, 12(%si)
	shll	$2, 8(%si)
2:
	jmp	disk_address_packet_ready

/****************************************************************************/
1:
	cmpb	$0x44, %ah	/* EBIOS verify sectors */
	jnz	1f
	xorb	%ah, %ah
	clc
	jmp	int13_return
/****************************************************************************/
1:
	cmpb	$0x47, %ah	/* EBIOS seek */
	jnz	1f
	xorb	%ah, %ah
	clc
	jmp	int13_return
/****************************************************************************/
1:
	cmpb	$0x48, %ah	/* EBIOS GET DRIVE PARAMETERS */
	jnz	1f

	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jz	2f			/* normal disks */

	/* CDROM */
	/* get old SI, extended drive parameter table */
	movw	%cs:(int13_old_esi - int13_handler), %si
	cmpw	$26, (%si)
	jb	error_01_invalid

	movw	$26, (%si)	/* buffer length */
	movw	$0x00, 2(%si)		# no flags
	movw	$0x800, 24(%si)		# bytes per sect=2048
	xorl	%eax, %eax
	decw	%ax
	movl	%eax, 4(%si)		# cylinders=0xFFFF
	movb	$0, %ah
	movl	%eax, 8(%si)		# heads=0xFF
	movb	$15, %al
	movl	%eax, 12(%si)		# sectors per track=15
	movl	%eax, 20(%si)		# total sectors hi dword=0
	xorw	%ax, %ax		# CF cleared
	decl	%eax			# EAX=0xFFFFFFFF
	movl	%eax, 16(%si)		# total sectors lo dword
					# CF is cleared

	xorw	%ax, %ax	/* success, CF cleared */
	jmp	int13_return
2:
	/* get old SI, extended drive parameter table */
	movw	%cs:(int13_old_esi - int13_handler), %si
	movw	$26, (%si)	/* buffer length */
	movw	$2, 2(%si)	/* info */
	xorl	%eax, %eax
	movl	%eax, 8(%si)	/* total heads */
	movl	%eax, 12(%si)	/* sectors per track */
	movl	%eax, 16(%si)	/* total sectors */
	movl	%eax, 20(%si)	/* hi 32 bits of total sectors */
	pushl	%ebx
	xorl	%ebx, %ebx
	movw	%cs:2(%bp), %ax	/* AL=Hmax, AH=Smax */
	andb	$63, %ah
	movb	%ah, 12(%si)	/* sectors per track */
	movb	%ah, %bl
	xorb	%ah, %ah
	incw	%ax		/* total heads=Hmax+1 */
	movw	%ax, 8(%si)	/* total heads */
	pushl	%edx
	mulw	%bx		/* DX:AX=product, DX=0 */
	movw	%ax, %bx	/* BX=sectors per cylinder */
	movl	%cs:16(%bp), %eax	/* S_count_Lo */
	#;andb	$0xfe, %al
	movl	%eax, 16(%si)	/* total sectors */
	xorl	%edx, %edx	/* EDX:EAX=64bit total sectors */
	testw	%bx, %bx
	jz	2f
	divl	%ebx		/* EAX=quotient, EDX=residue */
2:
	testl	%edx, %edx
	popl	%edx
	popl	%ebx
	jz	2f
	incl	%eax
2:
	movl	%eax, 4(%si)	/* total cylinders */
	movw	$512, 24(%si)	/* bytes per sector */
	xorb	%ah, %ah
	/*clc*/			/* signal success, CF already cleared by XOR */
	jmp	int13_return

/****************************************************************************/
1:
	cmpw	$0x4B01, %ax	/* CDROM GET DISK EMULATION STATUS */
	jnz	1f

	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jz	error_01_invalid	/* normal disks have no 0x4B01 function */

	/* CDROM */
	pushw	%es
	pushw	%di
	movw	%cs:(int13_old_esi - int13_handler), %di	/* old SI */
	pushw	%ds
	popw	%es
	movw	$0x0013, %ax	/* packet size=13h, boot type=0 (no-emu) */
	cld
	stosw
	movb	%dl, %al	/* drive=DL, controller=0 */
	stosw

	pushl	%cs:EXT_C(lba_cd_boot) - int13_handler
	popw	%ax
	stosw
	popw	%ax
	stosw			/* LBA for no-emu image */

	xorw	%ax, %ax
	stosw			/* device specification */
	stosw			/* user buffer segment */
	stosw			/* load segment */
	movb	$4, %al
	stosw			/* sector count=4 */
				/* CHS makes no sense for no-emu */
	popw	%di
	popw	%es
	xorb	%ah, %ah	/* success, CF cleared */
	jmp	int13_return

/****************************************************************************/
1:
/****************************************************************************/
error_01_invalid:
	movb	$0x01, %ah	/* unsupported function call */
	stc			/* signal error */

int13_return:
	movw	%ax, %cs:(int13_old_eax - int13_handler)	/* status */
	jnc	1f
	orb	$1, %cs:(int13_old_flags - int13_handler)
	jmp	2f
1:
	andb	$0xFE, %cs:(int13_old_flags - int13_handler)
2:
	pushw	%cs:(int13_old_flags - int13_handler)
	popfw

	movl	%cs:(int13_old_eax - int13_handler), %eax
	movl	%cs:(int13_old_esi - int13_handler), %esi
	movw	%cs:(int13_old_ds - int13_handler), %ds
	movl	%cs:(int13_old_ebp - int13_handler), %ebp
	movw	%cs:(int13_old_esp - int13_handler), %sp
	ljmp	*%cs:(int13_old_cs_ip - int13_handler)

/****************************************************************************/
real_int13_service:

	/* AH = 0x42 or 0x43 */

	/* save return address to memory */
	popw	%cs:(int13_ret_IP - int13_handler)
	/* save AX to memory */
	movw	%ax, %cs:(int13_reg_AX - int13_handler)

	cmpb	$0xff, %dl	/* mem drive */
	jne	normal_disk_drive

	testw	$0x4000, %cs:4(%bp)	/* TO_C bit 14=TO has 2048-byte cdrom sector */
	jnz	normal_disk_drive

	/* handle memdrive */

	sti		/* for hardware interrupt or watchdog */
	cmpb	$0x42, %ah
	je	3f
	cmpb	$0x43, %ah
	je	3f
	stc
	jmp	*%cs:(int13_ret_IP - int13_handler)	//ret
	/* DS:SI points to disk address packet 	*/
	/* 	2(%si), byte, sectors(1-127) 	*/
	/* 	4(%si), word, offset 		*/
	/* 	6(%si), word, segment 		*/
	/* 	8(%si), qword, lba		*/
3:
	/* AH = 0x42 or 0x43 */
	sahf	/* Store AH into flags SF ZF xx AF xx PF xx CF */
	pushf
	movzwl	4(%si), %ebx	#;  BX=offset, EBX_high_word=0
	movzwl	6(%si), %eax	#;  AX=segment
	movl	8(%si), %ecx	#; ECX=LBA of MEMDRIVE SECTOR
	shll	$9, %ecx	#; ECX=linear address of SECTOR(above 1M)
	shll	$4, %eax	#; EAX=linear base address of segment
	addl	%ebx, %eax	#; EAX=linear address of BUFFER(below 1M)
	popf
	jc	4f	/* write */
	xchgl	%eax, %ecx
4:
	/* Data transfer: (%eax) -> (%ecx), number of sectors 2(%si) */
	movl	%eax, %cs:(int13_data_addr_from - int13_handler)
	movl	%ecx, %cs:(int13_data_addr_to - int13_handler)

	cmpl	$0, %cs:(EXT_C(memdisk_raw) - int13_handler)
	je	4f		/* do not use raw mode */

	/* raw mode as in memdisk, contributed by Bean */

//	pushw	%ax
	smsw	%bx
	testb	$1, %bl
//	popw	%ax
	jnz	4f		/* protected mode */

	/* switch to protected mode myself */
	/* ebx destroy */

	//pushw	%ds
	//pushw	%es
	//pushl	%eax
	//pushl	%ecx

	//cli

	#;xorl	%ebx, %ebx
	movw	%cs, %bx	#; EBX_high_word=0
	shll	$4, %ebx
	addl	$(MyGDT - int13_handler), %ebx
	movl	%ebx, %cs:MyGDT - int13_handler + 2

	movzbl	2(%si), %ebx
	shll	$9, %ebx
	movl	%ebx, %edx
	addl	%eax, %ebx
	decl	%ebx
	orl	%eax, %ebx
	addl	%ecx, %edx
	decl	%edx
	orl	%ecx, %edx
	orl	%ebx, %edx
	testl	$0x100000, %edx	# zero means data inside an even mega
	setz	%dl		# we think of it as if A20 were on
	jz	3f		# no need to enable A20

	pushw	%dx
	movw	$0x00ff, %cx		# try so many times on failure
	movw	$0x0001, %dx		# DL=enable A20, DH=debug off

	cli	/* yes, keep interrupt off when controlling A20 */
	call	enable_disable_a20
	sti

	popw	%dx
	jz	1f		/* success */

	/* A20 control failed. */

	# notify the user
	movzbw	%cs:(%bp), %ax		/* FROM_DRIVE */
	pushw	%ax
	pushw	$a20_failure - int13_handler	# the format string
	call	realmode_printf
	addw	$4, %sp			# adjust the stack pointer

	//popl	%ecx
	//popl	%eax
	//popw	%es
	//popw	%ds

	//jmp	4f		/* try once more with int15/ah=87h */

	stc
	jmp	*%cs:(int13_ret_IP - int13_handler)	//ret

a20_failure:
	.ascii	"\r\ngrub4dos: A20 failure on memdrive=0x%X.\r\n\0"

1:
	/* CF=1 means A20 was originally enabled. */
	setc	%dl
	movb	$0, %dh

3:
	movw	$(EBIOS_disk_address_packet - int13_handler), %si
	pushw	%cs
	popw	%ds
	//popl	%ecx
	//popl	%eax
	movzbl	2(%si), %ecx	#; ECX=number of sectors to transfer
	movl	%cs:(int13_data_addr_to - int13_handler), %edi
	movl	%cs:(int13_data_addr_from - int13_handler), %esi
				#; ESI changed!!

	shll	$7, %ecx	/* 128 dwords per sector */

	cli
	lgdt	%cs:MyGDT - int13_handler
	movl	%cr0, %eax
	andl	$0x0000FFFF, %eax
	orb	$1, %al
	movl	%eax, %cr0	/* Switch to protected mode */

//	jmp	1f
//1:
//	jmp	1f
//1:


	movw	$16, %bx	/* Switch to 4G data segment */
	movw	%bx, %ds
	movw	%bx, %es

//	jmp	1f
//1:
//	jmp	1f
//1:


	cld

	addr32	rep movsl	/* ESI, EDI changed! */

	andb	$0xFE, %al
	movl	%eax, %cr0	/* Back to real mode */

//	jmp	1f
//1:
//	jmp	1f
//1:
	sti

	//popw	%es
	//popw	%ds

	cmpl	$0, %cs:(EXT_C(a20_keep_on) - int13_handler)
	jne	1f	/* Keep A20 on. This should hurt nothing. */

	/* Disable A20 if necessary ! */
	andw	%dx, %dx	/* 0=orig A20 off, 1=orig A20 on */
	jnz	1f

	movw	$0x0004, %cx		# try so many times on failure
	//movw	$0x0000, %dx		# DL=disable A20, DH=debug off
	cli	/* yes, keep interrupt off when controlling A20 */
	call	enable_disable_a20
	sti

1:
	clc
	jmp	*%cs:(int13_ret_IP - int13_handler)	//ret
4:
	movb	2(%si), %bl	/* number of sectors to be moved */
	pushw	%cs
	popw	%es
	movw	$(GDT_data - int13_handler), %si /* SI changed!! */
	movw	%ax, %es:0x12(%si)	#; source physical address low 16 bits
	shrl	$16, %eax
	movb	%al, %es:0x14(%si)	#; source physical address bit 16-23
	movb	%ah, %es:0x17(%si)	#; source physical address bit 24-32
	movw	%cx, %es:0x1a(%si)	#; dest physical address low 16 bits
	shrl	$16, %ecx
	movb	%cl, %es:0x1c(%si)	#; dest physical address bit 16-23
	movb	%ch, %es:0x1f(%si)	#; dest physical address bit 24-32

	xorw	%cx, %cx	/* ECX is 0 */
	movl	%ecx, %es:(%si)
	movl	%ecx, %es:0x04(%si)
	movl	%ecx, %es:0x08(%si)
	movl	%ecx, %es:0x0c(%si)
	movl	%ecx, %es:0x20(%si)
	movl	%ecx, %es:0x24(%si)
	movl	%ecx, %es:0x28(%si)
	movl	%ecx, %es:0x2c(%si)
	movb	%bl, %ch	/* CX=number of words to be moved */
	movb	$0x87, %ah	/* access extended memory */

	pushw	%ax
	smsw	%ax
	testb	$1, %al
	popw	%ax
	jnz	4f		/* protected mode */

	/* real mode */
	pushfw
	lcall	%cs:*(EXT_C(ROM_int15) - int13_handler)

	jmp	6f
4:
	/* protected mode */
	int	$0x15
6:
	jmp	*%cs:(int13_ret_IP - int13_handler)	//ret

/****************************************************************************/
normal_disk_drive:

	/* handle normal disk drive */

	/* AH = 0x42 or 0x43 */
	pushw	%ax
	smsw	%ax
	testb	$1, %al
	popw	%ax
	jz	3f	// real mode

	/* protected mode(Windows 98, EMM386) */

	pushw	%si		/* save SI */
	pushl	%eax		/* save EAX */
	pushl	%ebx		/* save EBX */

	/* set SI to the drive map */
	movw	$(EXT_C(hooked_drive_map) - int13_handler), %si
	/* find the drive number from the drive map */
	cld
	subw	$DRIVE_MAP_SLOT_SIZE - 4, %si
2:
	addw	$DRIVE_MAP_SLOT_SIZE - 4, %si
	lodsl	%cs:(%si), %eax
	testl	%eax, %eax	/* end of map table? */
	movl	%eax, %ebx	/* save the map to EBX */
	jz	2f		/* yes, no map found */
	cmpb	%dl, %ah	/* found the map? */
	jne	2b		/* no, check the next slot */

	/* drive is mapped. check if map a whole drive */
	shrl	$16, %eax
	testb	$62, %ah
	jnz	2b			/* no, check the next slot */
	movl	%cs:4(%si), %eax	/* StartLBA_Lo */
	testl	%eax, %eax
	jnz	2b			/* no, check the next slot */
	movl	%cs:12(%si), %eax	/* S_count_Lo */
	shrl	$1, %eax
	jnz	2b		/* no, check the next slot */
2:
	testl	%ebx, %ebx	/* mapped or not mapped ? */
	jz	2f		/* not mapped, do nothing */
	movb	%bl, %dl	/* use the mapped FROM_DRIVE for win 98 */
2:
	movb	%cs:(%bp), %al	/* AL=FROM_DRIVE */
	testb	%al, %al	/* hard drive emulation? */
	jns	2f		/* floppy, jump */
	cmpb	%al, %dl
	jb	2f
	incb	%dl
2:
	popl	%ebx
	popl	%eax
	popw	%si

	/* fall through to real mode... */

3:

	/* in real mode DOS, call original real mode int13 */
	/* DS:SI points to disk address packet 	*/
	/* 	2(%si), byte, sectors(1-127) 	*/
	/* 	4(%si), word, offset 		*/
	/* 	6(%si), word, segment 		*/
	/* 	8(%si), qword, lba		*/

	testw	$0x4000, %cs:4(%bp)	/* TO_C bit 14=TO has 2048-byte cdrom sector */
	jz	1f

	/* LBA supported, CHS not applicable for CDROM */

	movw	%es, %cs:(int13_cdrom_es - int13_handler)
	movw	%cx, %cs:(int13_cdrom_cx - int13_handler)
	movl	%ebx, %cs:(int13_cdrom_ebx - int13_handler)
	movl	%edi, %cs:(int13_cdrom_edi - int13_handler)

	movzbw	2(%si), %cx	/* sectors to transfer */
	movl	8(%si), %edi	/* start sector number(LBA) */
	movl	12(%si), %ebx
3:
	/* read 1 small sector at LBA=EDI to edd30_disk_buffer */

	/* if we have previously read it, we can skip this step now. */
	cmpb	%dl, %cs:last_read_cd_drive - int13_handler
	jne	2f
	cmpl	%ebx, %cs:last_read_sector - int13_handler + 4
	jne	2f
	movl	%edi, %eax
	andb	$0xFC, %al
	cmpl	%eax, %cs:last_read_sector - int13_handler	#; // + 4
	je	4f
2:
	movl	(%si), %eax
	movl	%eax, %cs:(tmp_dap - int13_handler)
	movl	4(%si), %eax
	movl	%eax, %cs:(tmp_dap - int13_handler + 4)
	movl	8(%si), %eax
	movl	%eax, %cs:(tmp_dap - int13_handler + 8)
	movl	12(%si), %eax
	movl	%eax, %cs:(tmp_dap - int13_handler + 12)

	movl	%edi, 8(%si)
	movl	%ebx, 12(%si)
	shrdl	$2, %ebx, 8(%si)	/* turn LBA small to LBA big */
	shrl	$2, 12(%si)

	movb	$1, 2(%si)		/* read 1 big sector */

	//pushw	%es
	movw	%bx, %cs:(int13_cdrom_bx - int13_handler)
	movw	%cs, %ax
	movw	%ax, %es
	movw	%ax, 6(%si)		/* set the buffer segment */
	movw	$edd30_disk_buffer - int13_handler, %bx
	movw	%bx, 4(%si)		/* set the buffer offset */
	movw	%cs:(int13_reg_AX - int13_handler), %ax

	/* check if DL is for anyone of (cd?)'s */
	cmpb	$0, %cs:(EXT_C(atapi_dev_count) - int13_handler)
	jz	2f			/* no cdX'es */
	cmpb	%cs:(EXT_C(min_cdrom_id) - int13_handler), %dl
	jb	2f			/* not cdX */
	cmpb	%cs:(max_cdrom_id - int13_handler), %dl
	ja	2f			/* not cdX */
	/* read 1 cdrom sector using our builtin cdrom driver. */
	movb	$1, %cs:(force_int13 - int13_handler)
2:
	call	int13_with_retry	/* read a big 2048-byte sector */

	movb	$0, %cs:(force_int13 - int13_handler)

	movw	%cs:(int13_cdrom_bx - int13_handler), %bx
	//popw	%es
	jc	3f			/* failed */

	movl	%cs:(tmp_dap - int13_handler), %eax
	movl	%eax, (%si)
	movl	%cs:(tmp_dap - int13_handler + 4), %eax
	movl	%eax, 4(%si)
	movl	%cs:(tmp_dap - int13_handler + 8), %eax
	movl	%eax, 8(%si)
	movl	%cs:(tmp_dap - int13_handler + 12), %eax
	movl	%eax, 12(%si)

	pushw	%di
	andw	$0xFFFC, %di
	movl	%edi, %cs:last_read_sector - int13_handler
	popw	%di
	movl	%ebx, %cs:last_read_sector - int13_handler + 4
	movb	%dl, %cs:last_read_cd_drive - int13_handler
4:
	pushw	%cx
	pushw	%bx
	pushw	%di

	/* calculate the buffer ES:BX */
	//movw	%di, %ax
	subw	8(%si), %di	/* sectors already read */
	shlw	$5, %di		/* paragraghs */
	addw	6(%si), %di	/* segment */
	movw	4(%si), %bx	/* offset */
	movw	%bx, %ax
	shrw	$4, %ax		/* turn to paragraghs */
	andw	$0x000F, %bx
	addw	%di, %ax	/* segment */
	movw	%ax, %es

	movw	%bx, %di
	popw	%ax		/* AX=old DI */
	pushw	%ax

	andb	$3, %al
	shlw	$9, %ax		/* offset in edd30_disk_buffer */

	addw	$edd30_disk_buffer - int13_handler, %ax

	xchgw	%ax, %si

	/* move 512 bytes */
	movw	$0x100, %cx
	cld
	cs repz movsw

	xchgw	%ax, %si

	popw	%di
	popw	%bx

	incl	%edi		/* next sector */
	jnz	2f
	incl	%ebx
2:

	popw	%cx
	decw	%cx
	jnz	3b
//	loop	3b

	movw	%cs:(int13_cdrom_es - int13_handler), %es
	movw	%cs:(int13_cdrom_cx - int13_handler), %cx
	movl	%cs:(int13_cdrom_ebx - int13_handler), %ebx
	movl	%cs:(int13_cdrom_edi - int13_handler), %edi
	clc
	jmp	*%cs:(int13_ret_IP - int13_handler)	//ret
3:
	movw	%cs:(int13_cdrom_es - int13_handler), %es
	movw	%cs:(int13_cdrom_cx - int13_handler), %cx
	movl	%cs:(int13_cdrom_ebx - int13_handler), %ebx
	movl	%cs:(int13_cdrom_edi - int13_handler), %edi
	stc
	jmp	*%cs:(int13_ret_IP - int13_handler)	//ret

/* cache 1 big 2048-byte sector at edd30_disk_buffer */

last_read_cd_drive:
	.byte	0	#; a value < 0x80 normally invalidate the cache

	.align	4
tmp_dap:
	.long	0
	.long	0
	.long	0
	.long	0

last_read_sector: /* set it to 0xFFFFFFFFFFFFFFFF to invalidate the cache */
	.long	0xFFFFFFFF	#; lo dword. for a valid cache, lowest 2 bits=0
	.long	0xFFFFFFFF	#; hi dword

int13_data_addr_from:
	.long	0
int13_data_addr_to:
	.long	0
int13_cdrom_edi:
	.long	0
int13_cdrom_ebx:
	.long	0
int13_cdrom_cx:
	.word	0
int13_cdrom_bx:
	.word	0
int13_cdrom_es:
	.word	0

1:
	#;jmp	1f	/* just check if CHS translation code works */
	testw	$0x8000, %cs:4(%bp)	/* TO_C bit 15=LBA support */
#if 0
	jnz	int13_with_retry	/* LBA mode */
#else
	jz	1f			/* CHS only, so skip the LBA access */
	movw	%es, %cs:(int13_tmp_es_bx - int13_handler + 2)
	movw	%bx, %cs:(int13_tmp_es_bx - int13_handler)
	movw	4(%si), %bx
	shrw	$4, %bx
	addw	%bx, 6(%si)
	andw	$0x000F, 4(%si)
	cmpb	$0x80, 2(%si)
	jb	2f			/* 0x7F sectors or less is ok */
	ja	3f			/* 0x81 sectors or more is bad */

	/* now 0x80 sectors requested. */

	testw	$0x2000, %cs:4(%bp)	/* TO_C bit 13=FROM has 2048-byte cdrom sector */
	jz	3f			/* not CDROM, fail out. */
	/* virtual cdrom function call could request 64K data each time */
	/* transfer 0x80 sectors = twice 0x40 sectors */
	movb	$0x40, 2(%si)		/* transfer the first 32K */
	movw	4(%si), %bx
	movw	6(%si), %es
	call	int13_with_retry
	jc	4f
	movb	$0x40, 2(%si)
	addw	$0x800, 6(%si)		/* step to next 32K of the buffer */
	movw	4(%si), %bx
	movw	6(%si), %es
	pushl	%eax
	xorl	%eax, %eax
	movb	$0x40, %al
	addl	%eax, 8(%si)		/* adjust start_LBA */
	movb	$0, %al
	adcl	%eax, 12(%si)
	popl	%eax
	call	int13_with_retry
	/* we ignore this step and keep start_LBA in a modified state. This is
	 * because only modify_boot_sectors need start_LBA and we are accessing
	 * the virtual cdrom, so we will not call modify_boot_sectors.
	 */
	//pushfw
	//pushl	%eax
	//xorl	%eax, %eax
	//movb	$0x40, %al
	//subl	%eax, 8(%si)		/* adjust start_LBA */
	//movb	$0, %al
	//sbbl	%eax, 12(%si)
	//popl	%eax
	//popfw
4:
	movb	$0x80, 2(%si)		/* restore its original value */
	lesw	%cs:(int13_tmp_es_bx - int13_handler), %bx
	movw	%bx, 4(%si)		/* restore its original value */
	movw	%es, 6(%si)		/* restore its original value */
	jmp	*%cs:(int13_ret_IP - int13_handler)	//ret

2:
	movw	4(%si), %bx
	movw	6(%si), %es
	call	int13_with_retry
	lesw	%cs:(int13_tmp_es_bx - int13_handler), %bx
	movw	%bx, 4(%si)		/* restore its original value */
	movw	%es, 6(%si)		/* restore its original value */
	jnc	4f
	testw	$0x1000, %cs:4(%bp)	/* TO_C bit 12=BIFURCATE */
	jz	1f	/* not bifurcate, use CHS access once more. */
	stc		/* bifurcate, error out. */
4:
	jmp	*%cs:(int13_ret_IP - int13_handler)	//ret

3:
	movb	$1, %ah
	stc
	jmp	*%cs:(int13_ret_IP - int13_handler)	//ret

	.align	4
int13_tmp_es_bx:	.long	0

1:
#endif
	/* CHS mode */

	/* byte at SI+2: number of sectors to access */
	/* 8 bytes at SI+8: StartLBA */

	/* CHS mode cannot access large addresses */
	cmpl	$0, 12(%si)
	jnz	1f

	pushl	%edx
	pushl	%ebx
	movw	%cs:4(%bp), %ax	/* TO_C */
	andw	$0x3FF, %ax	/* get lo 10 bits */
	incw	%ax		/* cylinders */
	pushw	%ax
	movzbw	%cs:6(%bp), %ax	/* TO_H */
	incw	%ax		/* heads */
	movzbw	%cs:7(%bp), %bx	/* TO_S */
	andb	$0x3F, %bl
	mulw	%bx		/* DX=0, AX=heads*sectors */
	popw	%bx		/* cylinders */
	mulw	%bx		/* DX:AX=total sectors in drive */
	pushw	%dx
	pushw	%ax
	movzbl	2(%si), %eax	/* number of sectors to access */
	addl	8(%si), %eax	/* last sector number + 1 */
	popl	%edx		/* total sectors in drive */
	cmpl	%edx, %eax
	popl	%ebx
	popl	%edx
	jnb	1f

	/* all requested sectors can be accessed by CHS */

	/* we will access one sector at a time */

	movzbw	2(%si), %cx	/* sectors to transfer */
	movl	8(%si), %edi	/* start sector number(LBA) */
3:
	/* translate LBA to CHS */

	//pushw	%cx
	movw	%cx, %cs:(int13_chs_cx - int13_handler)

	/* get sectors per cylinder */

	//pushw	%dx
	movw	%dx, %cs:(int13_chs_dx - int13_handler)

	pushl	%edi		/* lba */
	movzbw	%cs:6(%bp), %ax	/* TO_H */
	incw	%ax		/* heads */
	movzbw	%cs:7(%bp), %bx	/* TO_S */
	andb	$0x3F, %bl
	mulw	%bx		/* DX=0, AX=sectors per cylinder */
	popw	%cx		/* lba_lo */
	xchgw	%ax, %cx	/* CX=sectors per cylinder, AX=lba_lo */
	popw	%dx		/* DX:AX=lba */

	divw	%cx		/* AX=cylinder number, DX=rem */
	xchgw	%ax, %dx	/* DX=cylinder number, AX=rem */

	movb	%dl, %ch	/* CH=lo 8 bits of cylinder */

	divb	%bl		/* AL=head number, AH=sector number - 1 */
	movb	%ah, %cl
	incw	%cx		/* CL=sector number */
	shlb	$6, %dh		/* hi 2 bits of cylinder */
	orb	%dh, %cl

	movw	%cs:(int13_chs_dx - int13_handler), %dx	/* DL=drive number */
	//popw	%dx		/* DL=drive number */
	//pushw	%dx

	movb	%al, %dh	/* DH=head number */

	//pushl	%edi
	movl	%edi, %cs:(int13_chs_edi - int13_handler)

	//movw	%di, %ax
	subw	8(%si), %di	/* sectors already read */
	shlw	$5, %di		/* paragraghs */
	addw	6(%si), %di	/* segment */
	movw	4(%si), %bx	/* offset */
	movw	%bx, %ax
	shrw	$4, %ax		/* turn to paragraghs */
	andw	$0x000F, %bx
	addw	%di, %ax	/* segment */
	movw	%ax, %es

	movb	%cs:(int13_old_eax - int13_handler + 1), %ah
				/* 0x42/0x02=read, 0x43/0x03=write */
	andb	$0x03, %ah	/* AH=0x02 or 0x03 */
	movb	$0x01, %al	/* number of sectors to read/write */

	call	int13_with_retry

	movl	%cs:(int13_chs_edi - int13_handler), %edi
	movw	%cs:(int13_chs_dx - int13_handler), %dx
	//popl	%edi
	//popw	%dx

	incl	%edi		/* next sector */

	movw	%cs:(int13_chs_cx - int13_handler), %cx
	//popw	%cx
	jc	1f
	loop	3b

	jmp	*%cs:(int13_ret_IP - int13_handler)	//ret
1:
	stc
	jmp	*%cs:(int13_ret_IP - int13_handler)	//ret

	.align	2
int13_ret_IP:
	.word	0
int13_reg_AX:
	.word	0
int13_chs_cx:
	.word	0
int13_chs_dx:
	.word	0

	.align	4
int13_chs_edi:
	.long	0

/****************************************************************************/
int13_with_retry:

	/* save return address to memory */
	popw	%cs:(int13_retry_IP - int13_handler)
//	/* backup the last DWORD in the buffer */
//	pushl	%es:0x1FC(%bx)
//	popl	%cs:(sector_last_dword - int13_handler)

	/* retry count should be an odd number initially */
	movb	$5, %cs:(retry_count - int13_handler)

	cmpb	$0, %cs:(force_int13 - int13_handler)
	jnz	3f	/* we should use current int 13 instead of ROM_int13 */
	call	backup_int13
3:
	movb	%ah, %cs:(int13_retry_ah - int13_handler)
	call	int13_simple

	jnc	3f		/* success */

//	/* if the last dword changed, consider it is success */
//
//	pushl	%eax
//	movl	%es:0x1FC(%bx), %eax
//	cmpl	%eax, %cs:(sector_last_dword - int13_handler)
//	popl	%eax
//
//	jne	3f		/* success */

	decb	%cs:(retry_count - int13_handler)
	jz	2f		/* finally failed */

	/* reset disk and try again */
	movb	$0, %cs:(int13_retry_ah - int13_handler)
	call	int13_simple

//	testb	$1, %cs:(int13_old_eax - int13_handler + 1)
//				/* 0x42/0x02=read, 0x43/0x03=write */
//	jnz	3b		/* it is write */
//
//	/* touch the last dword of the buffer */
//	notl	%es:0x1FC(%bx)
//	notl	%cs:(sector_last_dword - int13_handler)

	jmp	3b		/* try again, ignoring the reset failure */
2:
	stc			/* failure */
	jmp	2f
3:
	clc			/* success */
2:
	pushfw

	cmpb	$0, %cs:(force_int13 - int13_handler)
	jnz	2f	/* we have not touched the int 13 vector */
	call	restore_int13
2:
	popfw
	jmp	*%cs:(int13_retry_IP - int13_handler)	//ret

backup_int13:

	/* backup the current int 13 to tmp_int13, then install ROM_int13 */
	cli
	pushw	%ds
	pushl	%eax
	xorw	%ax, %ax
	movw	%ax, %ds
	movl	0x4C, %eax	/* current int 13 vector */
	movl	%eax, %cs:(tmp_int13 - int13_handler)
	movl	%cs:(ROM_int13 - int13_handler), %eax
	movl	%eax, 0x4C
	popl	%eax
	popw	%ds
	ret

restore_int13:

	/* restore the current int 13 from tmp_int13 */
	cli
	pushw	%ds
	pushl	%eax
	xorw	%ax, %ax
	movw	%ax, %ds
	movl	%cs:(tmp_int13 - int13_handler), %eax
	movl	%eax, 0x4C
	popl	%eax
	popw	%ds
	ret

retry_count:
	.byte	5

int13_retry_ah:
	.byte	0
force_int13:
	.byte	0

	.align	2
int13_retry_IP:
	.word	0
int13_retry_ds:
	.word	0
int13_retry_es:
	.word	0

	.align	4
int13_retry_eax:
	.long	0
int13_retry_ebx:
	.long	0
int13_retry_ecx:
	.long	0
int13_retry_edx:
	.long	0
int13_retry_esi:
	.long	0
int13_retry_edi:
	.long	0
int13_retry_ebp:
	.long	0
tmp_int13:
	.long	0
//sector_last_dword:
//	.long	0

/****************************************************************************/
int13_simple:

	/* input:	CF=0	reset disk	*/

	/* save return address to memory */
	popw	%cs:(int13_simple_IP - int13_handler)

	movl	%eax, %cs:(int13_retry_eax - int13_handler)
	movl	%ebx, %cs:(int13_retry_ebx - int13_handler)
	movl	%ecx, %cs:(int13_retry_ecx - int13_handler)
	movl	%edx, %cs:(int13_retry_edx - int13_handler)
	movl	%esi, %cs:(int13_retry_esi - int13_handler)
	movl	%edi, %cs:(int13_retry_edi - int13_handler)
	movl	%ebp, %cs:(int13_retry_ebp - int13_handler)
	movw	%ds, %cs:(int13_retry_ds - int13_handler)
	movw	%es, %cs:(int13_retry_es - int13_handler)

	//sti

	cmpb	$0, %cs:(force_int13 - int13_handler)
	jz	3f
	/* read 1 cdrom sector using our builtin cdrom driver. */

	/* backup old variables to bak variables */
	movl	%cs:(int13_old_eax - int13_handler), %eax
	movl	%eax, %cs:(int13_bak_eax - int13_handler)
	movl	%cs:(int13_old_ebx - int13_handler), %eax
	movl	%eax, %cs:(int13_bak_ebx - int13_handler)
	movl	%cs:(int13_old_ecx - int13_handler), %eax
	movl	%eax, %cs:(int13_bak_ecx - int13_handler)
	movl	%cs:(int13_old_edx - int13_handler), %eax
	movl	%eax, %cs:(int13_bak_edx - int13_handler)
	movl	%cs:(int13_old_esi - int13_handler), %eax
	movl	%eax, %cs:(int13_bak_esi - int13_handler)
	movl	%cs:(int13_old_edi - int13_handler), %eax
	movl	%eax, %cs:(int13_bak_edi - int13_handler)
	movl	%cs:(int13_old_ebp - int13_handler), %eax
	movl	%eax, %cs:(int13_bak_ebp - int13_handler)
	movw	%cs:(int13_old_ds - int13_handler), %ax
	movw	%ax, %cs:(int13_bak_ds - int13_handler)
	movw	%cs:(int13_old_es - int13_handler), %ax
	movw	%ax, %cs:(int13_bak_es - int13_handler)
	movw	%cs:(int13_old_flags - int13_handler), %ax
	movw	%ax, %cs:(int13_bak_flags - int13_handler)
	movl	%cs:(int13_old_cs_ip - int13_handler), %eax
	movl	%eax, %cs:(int13_bak_cs_ip - int13_handler)

	/* setup old variables for use by edd30_for_cdrom */

	/* edd30_for_cdrom far return address */
	movw	%cs, %cs:(int13_old_cs_ip + 2 - int13_handler)
	movw	$(2f - int13_handler), %cs:(int13_old_cs_ip - int13_handler)
	/* edd30_for_cdrom flags */
	pushfw
	popw	%cs:(int13_old_flags - int13_handler)
	movw	%cs:(int13_retry_es - int13_handler), %ax
	movw	%ax, %cs:(int13_old_es - int13_handler)
	movw	%cs:(int13_retry_ds - int13_handler), %ax
	movw	%ax, %cs:(int13_old_ds - int13_handler)
	movl	%cs:(int13_retry_ebp - int13_handler), %eax
	movl	%eax, %cs:(int13_old_ebp - int13_handler)
	movl	%cs:(int13_retry_edi - int13_handler), %eax
	movl	%eax, %cs:(int13_old_edi - int13_handler)
	movl	%cs:(int13_retry_esi - int13_handler), %eax
	movl	%eax, %cs:(int13_old_esi - int13_handler)
	movl	%cs:(int13_retry_edx - int13_handler), %eax
	movl	%eax, %cs:(int13_old_edx - int13_handler)
	movl	%cs:(int13_retry_ecx - int13_handler), %eax
	movl	%eax, %cs:(int13_old_ecx - int13_handler)
	movl	%cs:(int13_retry_ebx - int13_handler), %eax
	movl	%eax, %cs:(int13_old_ebx - int13_handler)
	movl	%cs:(int13_retry_eax - int13_handler), %eax
	movl	%eax, %cs:(int13_old_eax - int13_handler)

	movb	%cs:(int13_retry_ah - int13_handler), %ah
	jmp	edd30_for_cdrom		/* will return at 2f */
2:
	/* restore old variables from bak variables */
	movl	%cs:(int13_bak_eax - int13_handler), %eax
	movl	%eax, %cs:(int13_old_eax - int13_handler)
	movl	%cs:(int13_bak_ebx - int13_handler), %eax
	movl	%eax, %cs:(int13_old_ebx - int13_handler)
	movl	%cs:(int13_bak_ecx - int13_handler), %eax
	movl	%eax, %cs:(int13_old_ecx - int13_handler)
	movl	%cs:(int13_bak_edx - int13_handler), %eax
	movl	%eax, %cs:(int13_old_edx - int13_handler)
	movl	%cs:(int13_bak_esi - int13_handler), %eax
	movl	%eax, %cs:(int13_old_esi - int13_handler)
	movl	%cs:(int13_bak_edi - int13_handler), %eax
	movl	%eax, %cs:(int13_old_edi - int13_handler)
	movl	%cs:(int13_bak_ebp - int13_handler), %eax
	movl	%eax, %cs:(int13_old_ebp - int13_handler)
	movw	%cs:(int13_bak_ds - int13_handler), %ax
	movw	%ax, %cs:(int13_old_ds - int13_handler)
	movw	%cs:(int13_bak_es - int13_handler), %ax
	movw	%ax, %cs:(int13_old_es - int13_handler)
	movw	%cs:(int13_bak_flags - int13_handler), %ax
	movw	%ax, %cs:(int13_old_flags - int13_handler)
	movl	%cs:(int13_bak_cs_ip - int13_handler), %eax
	movl	%eax, %cs:(int13_old_cs_ip - int13_handler)
	/* eax changed but no problem */

	jmp	2f
3:
	movb	%cs:(int13_retry_ah - int13_handler), %ah
	int	$0x13
2:
	movw	%cs:(int13_retry_ds - int13_handler), %ds
	movw	%cs:(int13_retry_es - int13_handler), %es
	movl	%cs:(int13_retry_eax - int13_handler), %eax
	movl	%cs:(int13_retry_ebx - int13_handler), %ebx
	movl	%cs:(int13_retry_ecx - int13_handler), %ecx
	movl	%cs:(int13_retry_edx - int13_handler), %edx
	movl	%cs:(int13_retry_esi - int13_handler), %esi
	movl	%cs:(int13_retry_edi - int13_handler), %edi
	movl	%cs:(int13_retry_ebp - int13_handler), %ebp

	jmp	*%cs:(int13_simple_IP - int13_handler)	//ret

	.align	2
int13_simple_IP:
	.word	0

	.align	4
int13_bak_cs_ip:	.long	0
int13_bak_eax:		.long	0
int13_bak_ebx:		.long	0
int13_bak_ecx:		.long	0
int13_bak_edx:		.long	0
int13_bak_esi:		.long	0
int13_bak_edi:		.long	0
int13_bak_ebp:		.long	0
int13_bak_ds:		.word	0
int13_bak_es:		.word	0
int13_bak_flags:	.word	0

/****************************************************************************/
modify_boot_sectors:

	movw	%cs:(int13_old_eax - int13_handler), %ax
//	/* check CHS read */
//	cmpb	$0x02, %ah	/* is it read? */
//	jne	3f
//	cmpw	$0x0001, %cx	/* read from cylinder 0, sector 1? */
//	je	5f
//3:
	/* check LBA read */
	//cmpb	$0x42, %ah	/* is it extended read? */
	testb	$0x01, %ah	/* is it extended read? */
	jne	4f		/* no, do nothing. */
	cmpl	$0, 12(%si)	/* read from LBA_high=0? */
	jne	4f		/* no, do nothing. */

	movb	$0, %dh		/* simulate CHS read of head 0 */
	cmpl	$0, 8(%si)	/* read from LBA_low=0? */
	je	3f		/* yes, continue. */

	movzbl	%cs:3(%bp), %eax	/* AL=Smax */
	andb	$63, %al	/* EAX=sectors per track */
	movb	$1, %dh		/* simulate CHS read of head 1 */
	cmpl	%eax, 8(%si)	/* read from LBA_low=sectors per track? */
	jne	4f		/* no, exit. */
3:
	movw	4(%si), %bx	/* simulate CHS read buffer offset */
	movw	6(%si), %es	/* simulate CHS read buffer segment */

5:
	/* CHS mode read from cylinder 0, sector 1 */

#;	testb	%dl, %dl	/* The TO_DRIVE is hard drive? */
#;	jns	4f		/* no, do nothing */
	cmpw	$0xaa55, %es:0x1fe(%bx)	/* is it a valid boot sector? */
	jne	4f			/* no, skip */
	movl	%cs:16(%bp), %eax	/* S_count_Lo */
	shrl	$1, %eax	/* map a whole drive? */
	jz	4f		/* yes, nothing need to change */
	movl	%cs:8(%bp), %eax	/* StartLBA_Lo */
	testl	%eax, %eax	/* geometry translation only? */
	jz	4f		/* yes, needn't change */
	movl	%cs:(%bp), %eax	/* FROM_DRIVE, TO_DRIVE, H, S */

	testb	%al, %al	/* The FROM_DRIVE is hard drive? */
	jns	modify_floppy	/* no, goto floppy boot record modification */

	testb	%dh, %dh	/* read from head 0? */
	jnz	modify_HD_DOS	/* no, goto HD DOS boot record modification */

	/* we have read an MBR, and we need to modify the partition table */

	/* the partition table could be an extended partition table. if so,
	 * we need to turn it to be a primary partition table.
	 */

	/* if all start_sectors are not sectors_per_track, then it is not
	 * an extended partition table.
	 */

	/* first, the MS magic number should be non-zero */
	cmpl	$0, %es:0x1b8(%bx)
	jne	5f
	movb	%al, %es:0x1b8(%bx)	/* let it be the FROM_DRIVE */
5:
	shrl	$16, %eax	/* AL=Hmax, AH=Smax */
	andb	$63, %ah	/* AH=Smax */
//	cmpb	$1, %ah
//	jbe	4f /* do not modify partition table when disable CHS mode */

	/* an extended partition table should have an entry with
	 * StartSector=Smax.
	 */
	movzbl	%ah, %eax
	movw	$0x1c6, %si		/* SI=0x1c6 */
	cmpl	%eax, %es:(%bx, %si)
	jz	5f
	addw	$16, %si		/* SI=0x1d6 */
	cmpl	%eax, %es:(%bx, %si)
	jz	5f
	addw	$16, %si		/* SI=0x1e6 */
	cmpl	%eax, %es:(%bx, %si)
	jz	5f
	addw	$16, %si		/* SI=0x1f6 */
	cmpl	%eax, %es:(%bx, %si)
	jnz	4f	/* not an extended partition table */
5:
	/* check if it is a primary partition table */

	/* now the StartSector=Smax. if it is a primary partition table entry,
	 * it must have C/H/S=0/1/1.
	 */

	movl	%es:-8(%bx, %si), %eax
	shrl	$8, %eax	/* hi word:cylinder/sector, lo word:head */
	cmpl	$0x000101, %eax		/* 0x00=C, 0x01=S, 0x01=H */
	je	4f	/* primary partition table, nothing to do */

	/* now we are sure this is an extended partition table */

	/* compose a master boot record routine */

	cld
#if 1
	movw	%bx, %di
	movb	$0xFA, %al		/* cli */
	stosb
	movl	$0xD08EC033, %eax	/* xor AX,AX; mov SS,AX */
	stosl
	movl	$0xFB7C00BC, %eax	/* mov SP,7C00 ; sti */
	stosl
	movl	$0x07501F50, %eax	/* push AX; pop DS; push AX; pop ES */
	stosl
	movl	$0x7C1CBEFC, %eax	/* cld; mov SI,7C1C */
	stosl
	movl	$0x50061CBF, %eax	/* mov DI,061C ; push AX */
	stosl
	movl	$0x01E4B957, %eax	/* push DI ; mov CX, 01E4 */
	stosl
	movl	$0x1ECBA4F3, %eax	/* repz movsb; retf; push DS */
	stosl
	movl	$0x537C00BB, %eax	/* mov BX,7C00 ; push BX */
	stosl
	movl	$0x520180BA, %eax	/* mov DX,0180 ; push DX */
	stosl
	movl	$0x530201B8, %eax	/* mov AX,0201 ; push BX */
	stosl
	movl	$0x5F13CD41, %eax	/* inc CX; int 13; pop DI */
	stosl
	movl	$0x5607BEBE, %eax	/* mov SI,07BE ; push SI */
	stosl
	movl	$0xCBFA5A5D, %eax	/* pop BP; pop DX; cli; retf */
	stosl
#endif

	/* empty all other entries except this one by 2 steps: */

	/* step 1. move this entry onto the first one (overwrite it) */
	movl	%es:-8(%bx, %si), %eax
	movl	%eax, %es:0x1be(%bx)
	movl	%es:-4(%bx, %si), %eax
	movl	%eax, %es:0x1c2(%bx)
	movl	%es:(%bx, %si), %eax
	movl	%eax, %es:0x1c6(%bx)
	movl	%es:4(%bx, %si), %eax
	movl	%eax, %es:0x1ca(%bx)

#if 1
	/* step 2. empty the last 3 entries */
	xorw	%ax, %ax
	movw	%bx, %di
	addw	$0x1ce, %di
	movw	$24, %cx
	repz stosw		/* DI=BX+0x1fe */
#else
	xorw	%ax, %ax
	movw	%bx, %di
	addw	$0x1d1, %di
	stosw			/* DI=BX+0x1d3 */
	addw	$2, %di		/* DI=BX+0x1d5 */
	stosb			/* DI=BX+0x1d6 */
	addw	$0x28, %di	/* DI=BX+0x1fe */
#endif

	/* modify the start_CHS of the first entry */
	subw	$0x40, %di	/* DI=BX+0x1be */
	movb	$0x80, %al	/* set boot indicator */
	stosb			/* DI=BX+0x1bf */
	movb	$0x01, %al	/* AX=1 */
	stosb			/* DI=BX+0x1c0, H=0x01 */
	stosw			/* DI=BX+0x1c2, S=0x01, C=0x00 */

#if 0
	movb	$0x0B, %al
	stosb			/* DI=BX+0x1c3 */
#else
	/* modify the end_CHS of the first entry */
	incw	%di		/* DI=BX+0x1c3 */
#endif

	/* get total sectors */
	movl	%es:7(%di), %eax	/* DI+7=BX+0x1ca */

	/* calculate the end sector number */
	addl	%es:3(%di), %eax	/* DI+3=BX+0x1c6 */
	//decl	%eax

	pushl	%eax		/* EAX-1=end sector number */
	movzwl	%cs:2(%bp), %eax	/* Hmax, Smax */
	andb	$63, %ah
	movzbl	%ah, %ecx
	mulb	%cl
	addw	%cx, %ax	/* EAX=sectors per cylinder */
	pushl	%eax		/* EAX=sectors per cylinder */
	movw	$1024, %cx
	mull	%ecx		/* EAX=CHS addressible total sectors */
				/* EDX=0 */
	popl	%edx		/* EDX=sectors per cylinder */
	popl	%ecx		/* ECX-1=end sector number */
	cmpl	%eax, %ecx
	jb	5f

	/* assign max end sector number */
	movl	%eax, %ecx
	//subl	$0x3EC1, %ecx	/* XXX: 0x3EC1=255*63 */
5:
	pushl	%ecx		/* ECX-1=end sector number */
	movl	%edx, %ecx	/* ECX=sectors per cylinder */
	xorl	%edx, %edx
	popl	%eax		/* EAX-1=end sector number */
	decl	%eax

	divl	%ecx		/* EAX=cylinder number */
				/* EDX=sector number in the last cylinder */
				/* EAX hi=0, EDX hi=0 */
	xchgw	%ax, %cx	/* CX=cylinder number */
	xchgw	%ax, %dx	/* AX=sector number in the last cylinder */
	movb	%cs:3(%bp), %dl	/* DL=Smax */
	andb	$63, %dl
	divb	%dl		/* AL=head number, AH=sector number - 1 */
	stosb			/* DI=BX+0x1c4 */
	movb	%ah, %al
	incw	%ax		/* AL=sector number */
	movb	%cl, %ah	/* cylinder lo 8 bits */
	shlb	$6, %ch		/* cylinder hi 2 bits */
	orb	%ch, %al
	//movb	$0, %ah		/* XXX: let cylinder lo 8 bits=0 */
	stosw			/* DI=BX+0x1c6 */

4:
	/* end partition table modification */
	/* needn't restore registers */
	ret

/****************************************************************************/
modify_floppy:

	/* AL=FROM_DRIVE is the floppy drive number. */

	cmpb	$0x00, %dh	/* read from head 0? */
	jne	4b

	testw	$0x0800, %cs:4(%bp)	/* TO_C bit 11=bootsector has known filesystem */
	jz	4b		/* unknown boot sector */

	pushl	%ecx
	pushw	%si

	/* FAT12/FAT16/NTFS drive number is at offset 0x24. */
	movw	$0x24, %si

	xorl	%ecx, %ecx

	/* check if it is FAT32. */

	/* FAT32 should have 0 root_dir_entries and total_sectors_short. */
	cmpl	%ecx, %es:0x11(%bx)
	jne	5f			/* not FAT32 */

	/* FAT32 should have 0 sectors_per_fat. */
	cmpw	%cx, %es:0x16(%bx)
	jne	5f			/* not FAT32 */

	/* FAT32 should have non-zero total_sectors_long. */
	cmpl	%ecx, %es:0x20(%bx)
	je	5f			/* not FAT32 */

	/* FAT32 should have non-zero sectors_per_fat32. */
	cmpl	%ecx, %es:0x24(%bx)
	je	5f			/* not FAT32 */

	/* Now it is FAT32, and the drive number is at offset 0x40. */
	movw	$0x40, %si
5:
	movb	%al, %es:(%bx, %si)	/* modify the boot drive number. */

	movl	%ecx, %es:0x1c(%bx)	/* let number of hidden sectors=0 */
	/*movb	$0xf0, %es:0x15(%bx)*/	/* set floppy media descriptor */

	popw	%si
	popl	%ecx

	jmp	4b

/****************************************************************************/
modify_HD_DOS:

	cmpb	$0x01, %dh		/* read from head 1? */
	jne	4b
	movl	%cs:8(%bp), %eax		/* StartLBA_Lo */
	testl	%eax, %eax
	jz	4b
	cmpl	%eax, %es:0x1c(%bx)	/* Number of hidden sectors */
	jbe	4b
	subl	%eax, %es:0x1c(%bx)
	xorl	%eax, %eax
	cmpl	%eax, %es:0x1c(%bx)
	jne	4b
	movb	%cs:3(%bp), %al		/* AL=Smax */
	andb	$63, %al
	movb	%al, %es:0x1c(%bx)
	//cmpl	%eax, %es:0x1c(%bx)	/* Number of hidden sectors */
	//jnb	4b
	//movl	%eax, %es:0x1c(%bx)
	jmp	4b

/****************************************************************************/
modify_in_situ:

	movb	%al, %cs:(int13_in_situ_sectors - int13_handler)

	cld

	/* if FROM is not harddrive, do nothing. */
	testb	$0x80, %cs:(%bp)	/* FROM drive */
	jz	4f

	cmpb	$0x02, %ah	/* is it CHS read? */
	jne	3f

	/* AL=sectors read */

	/* translate CHS to LBA */

	movw	%dx, %di	/* save DX to DI */
	movw	%cx, %si	/* save CX to SI */

	movb	%cs:7(%bp), %al	/* TO_S */
	andb	$63, %al
	movzbw	%al, %dx
	mulb	%cs:6(%bp)	/* TO_H */
	addw	%dx, %ax	/* AX=sectors_per_cylinder */

	/* get current cylinder number */

	shrb	$6, %cl
	xchgb	%cl, %ch

	mulw	%cx		/* DX:AX=sectors */

	pushw	%dx
	pushw	%ax

	/* restore DX */

	movw	%di, %dx

	/* get current head number */

	movb	%cs:7(%bp), %al	/* TO_S */
	andb	$63, %al
	mulb	%dh		/* AX=sectors */

	popl	%edx

	movzwl	%ax, %eax
	addl	%edx, %eax

	movw	%si, %cx	/* restore CX */
	andb	$0x3F, %cl
	decw	%cx
	movzbl	%cl, %ecx
	addl	%ecx, %eax	/* EAX=start sector number */
	movzbl	%cs:(int13_in_situ_sectors - int13_handler), %ecx
	jmp	6f

3:
	cmpb	$0x42, %ah	/* is it LBA read? */
	jne	4f
	cmpl	$0, 12(%si)	/* read from LBA_high=0? */
	jne	4f
	movl	8(%si), %eax	/* start sector number */
	movzbl	2(%si), %ecx	/* sectors read */
	movw	4(%si), %bx	/* simulate CHS read buffer offset */
	movw	6(%si), %es	/* simulate CHS read buffer segment */
6:
#;	testb	%dl, %dl	/* The TO_DRIVE is hard drive? */
#;	jns	4f		/* no, do nothing */
	/* ECX=sectors read */
	cmpl	$0, %eax	/* start sector number 0 is for MBR */
	je	5f

	/* if EAX <= startLBA_Lo < EAX + ECX, then the boot sector is read. */

	cmpl	%cs:8(%bp), %eax	/* startLBA_Lo */
	ja	4f		/* boot sector was not read */
	addl	%eax, %ecx
	cmpl	%cs:8(%bp), %ecx
	jbe	4f		/* boot sector was not read */

	movl	%cs:8(%bp), %ecx
	subl	%eax, %ecx	/* sectors between ES:BX and boot record */

	/* modify hidden sectors of the partition boot record */
	cmpb	$0x07, %cs:4(%bp)	/* NTFS */
	je	3f
	cmpb	$0x0C, %cs:4(%bp)	/* FAT32(LBA) */
	je	3f
	cmpb	$0x0E, %cs:4(%bp)	/* FAT12/16 */
	je	3f
	cmpb	$0x83, %cs:4(%bp)	/* EXT2 */
	jne	4f
3:
	movw	%es, %ax
	shlw	$5, %cx		/* 1 sector is 32 paragraphs */
	addw	%cx, %ax
	movw	%ax, %es
	cmpw	$0xaa55, %es:0x1fe(%bx)
	jne	4f
	movl	%cs:8(%bp), %eax	/* startLBA_Lo */
	movl	%eax, %es:0x1c(%bx)

	jmp	4f
5:
	/* Modify partition table. Note that there are 4 partition entries.
	 * The first one is called entry 0, and the last is entry 3. */

	cmpw	$0xaa55, %es:0x1fe(%bx)
	jne	4f

	/* first, the MS magic number should be non-zero */
	cmpl	$0, %es:0x1b8(%bx)
	jne	5f
	movb	%cs:(%bp), %al	/* FROM drive */
	movb	%al, %es:0x1b8(%bx)	/* let it be the FROM_DRIVE */
5:
	leaw	0x1be(%bx), %di	/* DI=BX+0x1be */

	movw	%di, %si

	/* if the first entry is empty, we simply put our new entry on it. */

	cmpb	$0, %es:4(%si)	/* consider partition type 00 as empty */
	jz	5f
	testb	$63, %es:2(%si)	/* invalid start sector number of 0 */
	jz	5f
	testb	$63, %es:6(%si)	/* invalid end sector number of 0 */
	jz	5f
	cmpl	$0, %es:8(%si)	/* invalid start LBA of 0 */
	jz	5f
	cmpl	$0, %es:12(%si)	/* invalid sector count of 0 */
	jz	5f

	/* Now that the first entry is not empty, we should find an empty one
	 * in entries 1, 2, 3(the last 3 partition entries). */

7:
	addw	$0x10, %si	/* SI=BX+0x1ce or BX+0x1de or BX+0x1ee */

	cmpb	$0, %es:4(%si)	/* consider partition type 00 as empty */
	jz	7f
	testb	$63, %es:2(%si)	/* invalid start sector number of 0 */
	jz	7f
	testb	$63, %es:6(%si)	/* invalid end sector number of 0 */
	jz	7f
	cmpl	$0, %es:8(%si)	/* invalid start LBA of 0 */
	jz	7f
	cmpl	$0, %es:12(%si)	/* invalid sector count of 0 */
	jz	7f

	leaw	0x1ee(%bx), %bx
	cmpw	%bx, %si
	leaw	-0x1ee(%bx), %bx
	jb	7b		/* try next entry */

	/* Now SI=BX+0x1ee points to the last entry */

7:
	/* SI points to empty entry(or the last entry), we move old entry 0
	 * onto this one.
	 */

	movl	%es:(%di), %eax
	movl	%eax, %es:(%si)
	movl	%es:4(%di), %eax
	movl	%eax, %es:4(%si)
	movl	%es:8(%di), %eax
	movl	%eax, %es:8(%si)
	movl	%es:12(%di), %eax
	movl	%eax, %es:12(%si)

5:
	/* build our new entry 0 */

	/* DI=BX+0x1be */

	movl	%cs:8(%bp), %eax	/* startLBA_Lo */

	call	lba_to_chs

	movb	$0x80, %al	/* set boot indicator */
	//movb	$0x00, %al	/* clear boot indicator */
	stosl			/* DI=BX+0x1c2 */

	movl	%cs:16(%bp), %eax
	addl	%cs:8(%bp), %eax
	decl	%eax		/* endLBA */

	call	lba_to_chs

	/* if in situ, TO_C holds the partition type:
	 * 	0x07(NTFS), 0x0C(FAT32), 0x0E(FAT12/16), 0x83(EXT2/3)
	 */
	movb	%cs:4(%bp), %al	/* TO_C */
	stosl			/* DI=BX+0x1c6 */

	/* modify start sector and sector count */

	movl	%cs:8(%bp), %eax
	stosl			/* DI=BX+0x1ca */
	movl	%cs:16(%bp), %eax
	stosl			/* DI=BX+0x1ce */

#if 1
	/* clear other boot indicators in the last 3 entries */

	movb	$0, %al		/* DI=BX+0x1ce */
	stosb			/* DI=BX+0x1cf */

	leaw	0x1de(%bx), %di	/* DI=BX+0x1de */
	stosb			/* DI=BX+0x1df */

	leaw	0x1ee(%bx), %di	/* DI=BX+0x1ee */
	stosb			/* DI=BX+0x1ef */
#endif

//#if 0
//	/* hide entry 3 (test only) */
//	orb	$0x10, %es:0x1f2(%bx)
//#endif

#if 0
	/* move extended partition entries to last */

	cmpb	$0x0F, %es:0x1f2(%bx)
	je	4f		/* entry 3 is extended, do nothing */

	cmpb	$0x05, %es:0x1f2(%bx)
	je	4f		/* entry 3 is extended, do nothing */

	cmpb	$0x0F, %es:0x1e2(%bx)
	je	7f		/* entry 2 is extended */

	cmpb	$0x05, %es:0x1e2(%bx)
	je	7f		/* entry 2 is extended */

	/* both entry 2 and entry 3 are not extended */

	cmpb	$0x0F, %es:0x1d2(%bx)
	je	8f		/* entry 1 is extended */

	cmpb	$0x05, %es:0x1d2(%bx)
	jne	4f	/* all entries 1, 2, 3 are not extended, do nothing */
8:
	/* entry 1 is extended, but entry 2 and entry 3 are not extended.
	 * so exchange entry 1 and entry 3. */

	xchgl	%eax, %es:0x1ce(%bx)
	xchgl	%eax, %es:0x1ee(%bx)
	xchgl	%eax, %es:0x1ce(%bx)

	xchgl	%eax, %es:0x1d2(%bx)
	xchgl	%eax, %es:0x1f2(%bx)
	xchgl	%eax, %es:0x1d2(%bx)

	xchgl	%eax, %es:0x1d6(%bx)
	xchgl	%eax, %es:0x1f6(%bx)
	xchgl	%eax, %es:0x1d6(%bx)

	xchgl	%eax, %es:0x1da(%bx)
	xchgl	%eax, %es:0x1fa(%bx)
	xchgl	%eax, %es:0x1da(%bx)

	jmp	4f

7:
	/* entry 2 is extended, but entry 3 is not. if entry 1 is extended,
	 * we exchange entry 1 and 3, else, we exchange entry 2 and 3. */
	 */

	cmpb	$0x0F, %es:0x1d2(%bx)
	je	8b		/* entry 1 is extended */

	cmpb	$0x05, %es:0x1d2(%bx)
	je	8b		/* entry 1 is extended */

	/* exchange entry 2 and 3 */
	xchgl	%eax, %es:0x1de(%bx)
	xchgl	%eax, %es:0x1ee(%bx)
	xchgl	%eax, %es:0x1de(%bx)

	xchgl	%eax, %es:0x1e2(%bx)
	xchgl	%eax, %es:0x1f2(%bx)
	xchgl	%eax, %es:0x1e2(%bx)

	xchgl	%eax, %es:0x1e6(%bx)
	xchgl	%eax, %es:0x1f6(%bx)
	xchgl	%eax, %es:0x1e6(%bx)

	xchgl	%eax, %es:0x1ea(%bx)
	xchgl	%eax, %es:0x1fa(%bx)
	xchgl	%eax, %es:0x1ea(%bx)
#endif

//	/* empty the last 3 entries */
//	xorw	%ax, %ax
//	movw	%bx, %di
//	addw	$0x1ce, %di
//	movw	$24, %cx
//	repz stosw		/* DI=BX+0x1fe */

4:
	/* end partition table and BPB modification */
	ret

int13_in_situ_sectors:
	.byte	0

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

lba_to_chs:

	/* input:
	 *
	 *	EAX:	LBA
	 *
	 * output:
	 *
	 *	EAX:	CHS
	 *
	 *
	 *
	 */

	pushl	%eax		/* EAX=end sector number */
	movzwl	%cs:6(%bp), %eax	/* TO_H, TO_S */
	andb	$63, %ah
	movzbl	%ah, %ecx
	mulb	%cl
	addw	%cx, %ax	/* EAX=sectors per cylinder */
	pushl	%eax		/* EAX=sectors per cylinder */
	movw	$1024, %cx
	mull	%ecx		/* EAX=CHS addressible total sectors */
				/* EDX=0 */
	popl	%edx		/* EDX=sectors per cylinder */
	popl	%ecx		/* ECX=end sector number */
	decl	%eax
	cmpl	%eax, %ecx
	jb	5f

	/* assign max end sector number */
	movl	%eax, %ecx
	//subl	$0x3EC1, %ecx	/* XXX: 0x3EC1=255*63 */
5:
	pushl	%ecx		/* ECX=end sector number */
	movl	%edx, %ecx	/* ECX=sectors per cylinder */
	xorl	%edx, %edx
	popl	%eax		/* EAX=end sector number */

	divl	%ecx		/* EAX=cylinder number */
				/* EDX=sector number in the last cylinder */
				/* EAX hi=0, EDX hi=0 */
	xchgw	%ax, %cx	/* CX=cylinder number */
	xchgw	%ax, %dx	/* AX=sector number in the last cylinder */
	movb	%cs:7(%bp), %dl	/* TO_S */
	andb	$63, %dl
	divb	%dl		/* AL=head number, AH=sector number - 1 */
	//stosb			/* DI=BX+0x1c4 */

	xchgb	%ah, %al	/* AH=head number, AL=sector number - 1 */

	pushw	%ax

	//movb	%ah, %al
	incw	%ax		/* AL=sector number */
	movb	%cl, %ah	/* cylinder lo 8 bits */
	shlb	$6, %ch		/* cylinder hi 2 bits */
	orb	%ch, %al
	//movb	$0, %ah		/* XXX: let cylinder lo 8 bits=0 */
	//stosw			/* DI=BX+0x1c6 */
	shll	$16, %eax

	popw	%ax

	ret

/****************************************************************************/
max_cdrom_id:		.byte	0xE0

	.align	4
ENTRY(memdisk_raw)	.long	1	/* set to 0 if accessing memdrives using int15/AH=87h */
ENTRY(a20_keep_on)	.long	1	/* set to 0 if a20 should be auto-off */
ENTRY(lba_cd_boot)	.long	0	/* LBA of no-emulation boot image, in 2048-byte sectors */

minimum_mem_lo_in_map:
	.long	0	/* min ram drive base below 16M */
minimum_mem_hi_in_map:
	.long	0	/* min ram drive base above 16M */

/****************************************************************************/
int15_e820_handler:

	/* Comments are mostly gotten from Ralf Brown's Interrupt List. */

	cmpw	$0xe820, %ax		//cmpl	$0x0000e820, %eax
	jne	1f

	/* Newer BIOSes - GET SYSTEM MEMORY MAP */

	//AX = E820h
	//EAX = 0000E820h
	//EDX = 534D4150h ('SMAP')
	//EBX = continuation value or 00000000h to start at beginning of map
	//ECX = size of buffer for result, in bytes (should be >= 20 bytes)
	//ES:DI -> buffer for result

	//Return:
	//CF clear if successful
	//EAX = 534D4150h ('SMAP')
	//ES:DI buffer filled
	//EBX = next offset from which to copy or 00000000h if all done
	//ECX = actual length returned in bytes
	//CF set on error
	//AH = error code (86h)

	/* Notes: Originally introduced with the Phoenix BIOS v4.0, this
	 * function is now supported by most newer BIOSes, since various
	 * versions of Windows call it to find out about the system memory.
	 * A maximum of 20 bytes will be transferred at one time, even if ECX
	 * is higher; some BIOSes (e.g. Award Modular BIOS v4.50PG) ignore the
	 * value of ECX on entry, and always copy 20 bytes. Some BIOSes expect
	 * the high word of EAX to be clear on entry, i.e. EAX=0000E820h. If
	 * this function is not supported, an application should fall back to
	 * AX=E802h, AX=E801h, and then AH=88h. The BIOS is permitted to return
	 * a nonzero continuation value in EBX and indicate that the end of the
	 * list has already been reached by returning with CF set on the next
	 * iteration. This function will return base memory and ISA/PCI memory
	 * contiguous with base memory as normal memory ranges; it will
	 * indicate chipset-defined address holes which are not in use and
	 * motherboard memory-mapped devices, and all occurrences of the system
	 * BIOS as reserved; standard PC address ranges will not be reported
	 */

//Format of Phoenix BIOS system memory map address range descriptor:
//
//Offset Size	Description
//------ -----	----------------------
//  00h  QWORD	base address
//  08h  QWORD	length in bytes
//  10h  DWORD	type of address range

//Values for System Memory Map address type:
//01h memory, available to OS
//02h reserved, not available (e.g. system ROM, memory-mapped device)
//03h ACPI Reclaim Memory (usable by OS after reading ACPI tables)
//04h ACPI NVS Memory (OS is required to save this memory between NVS sessions)
//other not defined yet -- treat as Reserved

	cmpl	$0x534D4150, %edx	/* "SMAP" */
	jne	1f
	cmpl	$20, %ecx
	jb	2f

	pushfw
	lcall	%cs:*(EXT_C(ROM_int15) - int13_handler)

	jc	3f
	cmpl	$0x534D4150, %eax	/* "SMAP" */
	jne	2f
	pushal
	movl	%es:4(%di), %eax	/* BaseAddrHigh */
	testl	%eax, %eax
	jnz	4f
	movl	%es:16(%di), %eax	/* Type */
	decl	%eax /* 1=usable memory, available to the operating system */
	jnz	4f
	/* set %si to the drive map */
	movw	$(EXT_C(hooked_drive_map) - int13_handler), %si
	movw	$(DRIVE_MAP_SIZE), %cx
6:
	cmpb	$0xff, %cs:1(%si)
	jne	5f
	testb	$0x40, %cs:5(%si)	/* TO_C bit 14=TO has 2048-byte cdrom sector */
	jnz	5f
	movl	%cs:8(%si), %ebx	/* start_sector */
	shll	$9, %ebx
	movl	%es:4(%di), %eax	/* BaseAddrHigh */
	testl	%eax, %eax
	jnz	5f
	cmpl	%es:(%di), %ebx		/* BaseAddrLow */
	jb	5f
	movl	%es:12(%di), %eax	/* LengthHigh */
	testl	%eax, %eax
	jnz	7f
	movl	%es:8(%di), %eax	/* LengthLow */
	addl	%es:(%di), %eax
	jc	7f
	cmpl	%eax, %ebx
	jnb	5f
7:
	subl	%es:(%di), %ebx		/* new length */
	movl	%ebx, %es:8(%di)	/* LengthLow */
	xorl	%ebx, %ebx
	movl	%ebx, %es:12(%di)	/* LengthHigh */
	//jmp	4f

5:
	/* try next slot */
	addw	$DRIVE_MAP_SLOT_SIZE, %si
	loop	6b
	//memory block length update done
4:
	popal
	clc
	lret	$2

2:
	movb	$0x86, %ah	/* function not supported */
3:
	stc
	lret	$2
#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1:
	cmpw	$0xe801, %ax	//cmpl	$0x0000e801, %eax
	je	1f
	cmpw	$0xe881, %ax	//cmpl	$0x0000e881, %eax
	je	1f
	cmpw	$0xda88, %ax
	je	1f
	cmpb	$0xc7, %ah
	je	1f
	cmpb	$0x8a, %ah
	je	1f
	cmpb	$0x88, %ah
	je	1f
	ljmp	%cs:*(EXT_C(ROM_int15) - int13_handler)
#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1:

	pushal

	/* find minimum mem ever used in the drive map slots */

	movl	$-1, %eax	/* 0xffffffff */
	movl	%eax, %cs:(minimum_mem_hi_in_map - int13_handler)
	movl	$0x1000000, %cs:(minimum_mem_lo_in_map - int13_handler)
	/* set %si to the drive map */
	movw	$(EXT_C(hooked_drive_map) - int13_handler), %si
	movw	$(DRIVE_MAP_SIZE), %cx
6:
	cmpb	$0xff, %cs:1(%si)
	jne	5f
	testb	$0x40, %cs:5(%si)	/* TO_C bit 14=TO has 2048-byte cdrom sector */
	jnz	5f
	movl	%cs:8(%si), %ebx	/* StartLBA_Lo */
	shll	$9, %ebx

	cmpl	$0x1000000, %ebx	/* 16M */
	jb	7f
	/* hi mem */
	cmpl	%ebx, %cs:(minimum_mem_hi_in_map - int13_handler)
	jbe	5f
	movl	%ebx, %cs:(minimum_mem_hi_in_map - int13_handler)
	jmp	5f
7:
	/* lo mem */
	cmpl	%ebx, %cs:(minimum_mem_lo_in_map - int13_handler)
	jbe	5f
	movl	%ebx, %cs:(minimum_mem_lo_in_map - int13_handler)
5:
	/* try next slot */
	addw	$DRIVE_MAP_SLOT_SIZE, %si
	loop	6b

	popal

	cmpl	$-1, %cs:(minimum_mem_hi_in_map - int13_handler)
	jne	1f
	cmpl	$0x1000000, %cs:(minimum_mem_lo_in_map - int13_handler)
	jne	1f

	ljmp	%cs:*(EXT_C(ROM_int15) - int13_handler)

#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1:
	cmpw	$0xe801, %ax	//cmpl	$0x0000e801, %eax
	jne	1f

	/* Phoenix BIOS v4.0 - GET MEMORY SIZE FOR >64M CONFIGURATIONS */

	//AX = E801h

	//Return:
	//CF clear if successful
	//AX = extended memory between 1M and 16M, in K (max 3C00h = 15MB)
	//BX = extended memory above 16M, in 64K blocks
	//CX = configured memory 1M to 16M, in K
	//DX = configured memory above 16M, in 64K blocks
	//CF set on error

	/* Notes: Supported by the A03 level (6/14/94) and later XPS P90
	 * BIOSes, as well as the Compaq Contura, 3/8/93 DESKPRO/i, and
	 * 7/26/93 LTE Lite 386 ROM BIOS. Supported by AMI BIOSes dated
	 * 8/23/94 or later. On some systems, the BIOS returns AX=BX=0000h;
	 * in this case, use CX and DX instead of AX and BX. This interface
	 * is used by Windows NT 3.1, OS/2 v2.11/2.20, and is used as a
	 * fall-back by newer versions if AX=E820h is not supported. This
	 * function is not used by MS-DOS 6.0 HIMEM.SYS when an EISA machine
	 * (for example with parameter /EISA) (see also MEM F000h:FFD9h), or
	 * no Compaq machine was detected, or parameter /NOABOVE16 was given.
	 */

	//movw	$0x3c00, %ax	/* 1-16M mem in K (0x3c00KB = 15MB) */
	//movw	$0x0300, %bx	/* mem above 16M in 64K blocks */
4:
	pushfw

	pushfw
	lcall	%cs:*(EXT_C(ROM_int15) - int13_handler)

	jc	3f

	pushl	%eax

	/* between 16M and 4G, we modify BX, DX */

	movl	%cs:(minimum_mem_hi_in_map - int13_handler), %eax
	cmpl	$-1, %eax		/* 4G - 1 */
	je	5f
	subl	$0x1000000, %eax	/* 16M */
	shrl	$16, %eax	/* AX=mem above 16M in 64K blocks */
	movw	%ax, %bx
	movw	%ax, %dx
	popl	%eax
	popfw
	pushfw
	pushl	%eax
	jnc	5f
	movzwl	%bx, %ebx
	movzwl	%dx, %edx
5:
	popl	%eax
	pushl	%eax

	/* between 1M and 16M, we modify AX, CX */

	movl	%cs:(minimum_mem_lo_in_map - int13_handler), %eax
	cmpl	$0x1000000, %eax	/* 16M */
	je	5f
	subl	$0x0100000, %eax	/* 1M */
	shrl	$10, %eax	/* AX=1-16M mem in K */
	movw	%ax, %cx
	popl	%eax
	movw	%cx, %ax
	popfw
	pushfw
	pushl	%eax
	jnc	5f
	popl	%eax
	movzwl	%ax, %eax
	pushl	%eax
	movzwl	%cx, %ecx
5:

	popl	%eax

	popfw
	clc
	lret	$2
3:
	popfw
	stc
	lret	$2

#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1:
	cmpw	$0xe881, %ax	//cmpl	$0x0000e881, %eax
	jne	1f

	/* Phoenix BIOS v4.0 - GET MEMORY SIZE FOR >64M CONFIGURATIONS (32-bit) */

	//AX = E881h

	//Return:
	//CF clear if successful
	//EAX = extended memory between 1M and 16M, in K (max 3C00h = 15MB)
	//EBX = extended memory above 16M, in 64K blocks
	//ECX = configured memory 1M to 16M, in K
	//EDX = configured memory above 16M, in 64K blocks
	//CF set on error

	/* Notes: Supported by AMI BIOSes dated 8/23/94 or later. This
	 * interface is used by Windows NT 3.1, OS/2 v2.11/2.20, and is used
	 * as a fall-back by newer versions if AX=E820h is not supported
	 */

	//movl	$0x3c00, %eax	/* 1-16M mem in K (0x3c00 = 15MB) */
	//movl	$0x0300, %ebx	/* mem above 16M in 64K blocks */

	stc
	jmp	4b
#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1:
	cmpw	$0xda88, %ax
	jne	1f

	/* AMI PCI BIOS - GET EXTENDED MEMORY SIZE */

	//AX = DA88h

	//Return:
	//CF clear (successful)
	//AX = 0000h
	//CL:BX = extended memory size in KBytes

	/* 63M = 3f00000 Bytes = 0xfc00 K */
	//movw	$0xfc00, %bx

#;	pushfw
#;	lcall	%cs:*(EXT_C(ROM_int15) - int13_handler)
#;
#;	jc	3f

	pushl	%eax

	movl	%cs:(minimum_mem_lo_in_map - int13_handler), %eax
	cmpl	$0x1000000, %eax	/* 16M */
	jb	5f
	movl	%cs:(minimum_mem_hi_in_map - int13_handler), %eax
5:
	subl	$0x0100000, %eax
	shrl	$10, %eax	/* EAX=extended mem in K */
	xchgw	%ax, %bx
	shrl	$16, %eax
	movb	%al, %cl

	popl	%eax

	xorw	%ax, %ax
	clc
	lret	$2
#;3:
#;	stc
#;	lret	$2

#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1:
	cmpb	$0xC7, %ah
	jne	1f

	/* SYSTEM - later PS/2s - RETURN MEMORY-MAP INFORMATION */

	//AH = C7h
	//DS:SI -> user supplied memory map table

	//Return:
	//CF set on error
	//CF clear if successful

	/* Notes: Call AH=C0h and examine bit 4 of feature byte 2 to check if
	 * this function is supported. IBM classifies this function as
	 * optional. Windows95 OSR2 reportedly does not support this function
	 * even though INT 15/AH=C0h indicates that it is available (observed
	 * on several different systems)
	 */

//Format of memory-map table structure:
//
//Offset Size	Description
//------ -----	--------------------------------------------------------------
//  00h  WORD	length of table (excluding this word)
//  02h  DWORD	local memory between 1M and 16M, in 1K blocks
//  06h  DWORD	local memory between 16M and 4G, in 1K blocks
//  0Ah  DWORD	system memory between 1M and 16M, in 1K blocks
//  0Eh  DWORD	system memory between 16M and 4G, in 1K blocks
//  12h  DWORD	cacheable memory between 1M and 16M, in 1K blocks
//  16h  DWORD	cacheable memory between 16M and 4G, in 1K blocks
//  1Ah  DWORD	1K blocks before start of non-system memory between 1M and 16M
//  1Eh  DWORD	1K blocks before start of non-system memory between 16M and 4G
//  22h  WORD	start segment of largest free block from C0000h-DFFFFh
//  24h  WORD	size of largest free block
//  26h  DWORD	reserved

	//movw	$0x0028, (%si)
	pushw	%ds
	pushw	%si

	pushfw
	lcall	%cs:*(EXT_C(ROM_int15) - int13_handler)

	popw	%si
	popw	%ds

	jc	3f

	pushl	%eax

	/* between 16M and 4G */

	movl	%cs:(minimum_mem_hi_in_map - int13_handler), %eax
	cmpl	$-1, %eax		/* 4G - 1 */
	je	5f
	subl	$0x1000000, %eax	/* 16M */
	shrl	$10, %eax	/* AX=mem above 16M in 1K blocks */
	movl	%eax, 0x0e(%si)
	movl	%eax, 0x1e(%si)
5:

	/* between 1M and 16M */

	movl	%cs:(minimum_mem_lo_in_map - int13_handler), %eax
	cmpl	$0x1000000, %eax	/* 16M */
	je	5f
	subl	$0x0100000, %eax	/* 1M */
	shrl	$10, %eax	/* AX=1-16M mem in K */
	movl	%eax, 0x0a(%si)
	movl	%eax, 0x1a(%si)
5:

	popl	%eax

	clc
	lret	$2
3:
	stc
	lret	$2

#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1:
	cmpb	$0x8a, %ah
	jne	1f

	/* Phoenix BIOS v4.0 - GET BIG MEMORY SIZE */

	//AH = 8Ah

	//Return:
	//DX:AX = extended memory size in K
	//movw	$0xfc00, %ax

#;	pushfw
#;	lcall	%cs:*(EXT_C(ROM_int15) - int13_handler)
#;
#;	jc	3f

	pushl	%eax

	movl	%cs:(minimum_mem_lo_in_map - int13_handler), %eax
	cmpl	$0x1000000, %eax	/* 16M */
	jb	5f
	movl	%cs:(minimum_mem_hi_in_map - int13_handler), %eax
5:
	subl	$0x0100000, %eax
	shrl	$10, %eax	/* EAX=extended mem in K */
	popw	%dx
	pushw	%ax
	shrl	$16, %eax
	xchgw	%ax, %dx

	popl	%eax

	clc
	lret	$2
#;3:
#;	stc
#;	lret	$2

#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1:
	cmpb	$0x88, %ah
	jne	1f

	/* SYSTEM - GET EXTENDED MEMORY SIZE (286+) */

	//AH = 88h

	//Return:
	//CF clear on success(Not all BIOSes correctly return the carry flag)
	//AX = number of contiguous KB starting at absolute address 100000h
	//CF set on error
	//AH = status
	//	80h invalid command (PC,PCjr)
	//	86h unsupported function (XT,PS30)
	//movw	$0xfc00, %ax

#;	pushfw
#;	lcall	%cs:*(EXT_C(ROM_int15) - int13_handler)
#;
#;	jc	3f

	pushl	%eax

	movl	%cs:(minimum_mem_lo_in_map - int13_handler), %eax
	cmpl	$0x1000000, %eax	/* 16M */
	jb	5f
	movl	%cs:(minimum_mem_hi_in_map - int13_handler), %eax
5:
	subl	$0x0100000, %eax
	shrl	$10, %eax	/* EAX=extended mem in K */

	cmpl	$0x10000, %eax
	jb	5f
	movw	$0xFFFF, %ax
5:
	addw	$2, %sp
	pushw	%ax
	popl	%eax

	clc
	lret	$2
#;3:
#;	stc
#;	lret	$2

1:
	ljmp	%cs:*(EXT_C(ROM_int15) - int13_handler)

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

#include "a20.inc"

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

/* EDD30 code imported from edd30.asm of Smart Boot Manager. */

/*
#; asmsyntax=nasm
#;
#; CD-ROM Boot Extension v 1.1 for Smart Boot Manager
#;
#; Copyright (C) 2000, Christopher Li <chrisl@gnuchina.org>.
#; Copyright (C) 2000, James Su <suzhe@gnuchina.org>
#;
#; This is free software, you can redistribute it and/or modify it
#; under the terms of the GNU General Public License version 2 or above.
#;
#; The ATAPI driver is based on the source code of atadrv written by
#; Hale Landis <hlandis@ibm.net>, Thanks him a lot!
#;
#; Without his great program, we could not implement the CD-ROM Boot feature
#; so quickly.
#;
*/


//#define	EDD_3_0

#define	sane_check
#define	check_extra_fail

//#define CB_DATA	0	//; data reg         in/out pio_base_addr1+0
//#define CB_FR		1	//; feature reg         out pio_base_addr1+1
//#define CB_SC		2	//; sector count     in/out pio_base_addr1+2
//#define CB_SN		3	//; sector number    in/out pio_base_addr1+3
//#define CB_CL		4	//; cylinder low     in/out pio_base_addr1+4
//#define CB_CH		5	//; cylinder high    in/out pio_base_addr1+5
//#define CB_DH		6	//; device head      in/out pio_base_addr1+6
//#define CB_STAT	7	//; primary status   in     pio_base_addr1+7
//#define CB_CMD	7	//; command             out pio_base_addr1+7
//#define CB_DC		8	//; device control      out pio_base_addr2+6
//#define CB_ASTAT	8	//; alternate status in     pio_base_addr2+6
//#define CB_DC_SRST	0x04	//; soft reset
#define	CB_STAT_BSY	0x80	//; busy
#define	CB_STAT_RDY	0x40	//; ready
#define	CB_STAT_SKC	0x10	//; seek complete

#define	CB_STAT_DRQ	0x08	//; data request
#define	CB_STAT_ERR	0x01	//; error

#define	CB_SC_P_TAG	0xf8	//; ATAPI tag (mask)
#define	CB_SC_P_REL	0x04	//; ATAPI release
#define	CB_SC_P_IO	0x02	//; ATAPI I/O
#define	CB_SC_P_CD	0x01	//; ATAPI C/D

#define	FAILBIT8	0x0100	//; SC( CD/IO bits) wrong at end of cmd
#define	FAILBIT6	0x0040	//; byte count wrong at data packet xfer time
#define	FAILBIT5	0x0020	//; SC (IO bit) wrong at data packet xfer time
#define	FAILBIT4	0x0010	//; SC (CD bit) wrong at data packet xfer time
#define	FAILBIT3	0x0008	//; byte count wrong at cmd packet xfer time
#define	FAILBIT2	0x0004	//; SC wrong at cmd packet xfer time
#define	FAILBIT0	0x0001	//; slow setting BSY=1 or DRQ=1 after AO cmd

#define	CB_DC_HD15	0x08	//; bit should always be set to one
#define	CB_DC_NIEN	0x02	//; disable interrupts
#define	cmd_DC	CB_DC_HD15
#define cmd_DC_ni CB_DC_HD15 | CB_DC_NIEN

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

__reg_select_dev:
#; ax = dev
#; simple version of the select dev
	addb	$0x0A, %al	#; AL= 0x0A or 0x0B
	shlb	$4, %al		#; AL= 0xA0 or 0xB0
	movw	reg_addr - int13_handler + (6 * 2), %dx	#; CB_DH = drive select
	outb	%al, %dx

	call	delay400ns
	ret


reg_select_dev:
#; input: ax = bx = dev
#; set_timeout first
//	movw	%ax, %bx
	cmpb	$2, (reg_dev_info - int13_handler)(%bx)	# REG_CONFIG_TYPE_ATA
	jb	__reg_select_dev	# not ATA

	/* The device is ATA */

//	pushw	%bx

	call	reg_poll_busy		# change AL, DX

#if 0
	orw	%ax, %ax
	jnz	1f	# return if,{or ax,ax},nz
#else
	ja	1f	# timeout, failure
#endif

	movw	%bx, %ax
	call	__reg_select_dev	# change AL, DX

#ifndef USE_ATA
	call	reg_poll_busy		# change AL, DX
#if 0
	jmp	1f
#else
#if 0
	orw	%ax, %ax
	jz	1f
#else
	jna	1f
#endif
#endif
#else	//; USE_ATA
3:
	movw	reg_addr - int13_handler + (7 * 2), %dx		#; CB_STAT
	inb	%dx, %al

	cmpb	$2, (reg_dev_info - int13_handler)(%bx)	# REG_CONFIG_TYPE_ATA
	jne	4f
	andb	$(CB_STAT_BSY | CB_STAT_RDY | CB_STAT_SKC), %al
	# return if,{cmp al,CB_STAT_RDY|CB_STAT_SKC},e
	cmpb	$(CB_STAT_RDY | CB_STAT_SKC), %al
	je	1f
	jmp	3b
4:
	# return if,{test al,CB_STAT_BSY},z
	testb	$CB_STAT_BSY, %al
	jz	1f
	jmp	3b
#endif	//; USE_ATA


	call	__reg_select_dev
1:
//	popw	%bx
	ret


select_atapi:
	cmpw	%cs:(atapi_cur_dev - int13_handler), %ax	# device serial number
	jne	select_atapi_force
	//clc	#; CF already cleared
	ret


select_atapi_force:
#; input: ax = device serial number
#; return: cf =0 success, cf =1 failed
	pushaw
	pushw	%es

	pushw	%cs
	popw	%es

	cmpb	%cs:(EXT_C(atapi_dev_count) - int13_handler), %al
	cmc
	jb	2f

	movw	%ax, %cs:(atapi_cur_dev - int13_handler)	# device serial number

	movw	$atapi_dev_base - int13_handler, %si	# array of ATAPI reg pointer and dev
	shlw	$2, %ax					# each element of array has 4 bytes
	addw	%ax, %si
	movw	%cs:(%si), %bx				# BX is the reg pointer of the current device

	call	reg_setup_base_addr			# fill the 10-word reg_addr area
							# change no registers

	movw	%cs:2(%si), %bx				# BX is the dev number of the current device
	movw	%bx, %ax

	call	set_timeout				# set timeout for reg_select_dev
							# change no registers

	call	reg_select_dev

	addb	$0x0A, %bl		#; BL= 0x0A or 0x0B
	shlb	$4, %bl			#; BL= 0xA0 or 0xB0
	movb	%bl, %cs:(reg_cur_dev - int13_handler)

	clc
2:
	pop	%es
	popaw
	ret


read_atapi:
#;input: es:di -> buffer, cx = sector count, edx = lba address
#;return: cf =0 success, cx = number of bytes actually read
	pushaw
	orw	%cx, %cx
	jz	3f

1:
	pushw	%cx
	pushl	%edx

	call	clear_atapi_buffer
	movb	$0x28, atapi_cmd_buffer - int13_handler

	bswapl	%edx
	movl	%edx, atapi_cmd_buffer - int13_handler + 2
	movb	$1, atapi_cmd_buffer - int13_handler + 8

	# invoke reg_packet,byte, 0, es, di, REG_ATAPI_MAX_BYTES
	pushw	$0x8000			#;   REG_ATAPI_MAX_BYTES
	pushw	%di
	pushw	%es
	pushw	$0
	call	reg_packet
	addw	$8, %sp

	orw	%ax, %ax
	jnz	2f
	cmpw	$0x800, %cx		#; CDSECTOR_SIZE
	jne	2f
	popl	%edx
	incl	%edx
	addw	%cx, %di
	popw	%cx
	loop	1b

	clc
	jmp	4f

2:
	popl	%edx
	popw	%cx

3:
	stc
4:
	popaw
	ret


read_bios_time:
#;	return EAX as the long time
	pushw	%ds
	xorw	%ax, %ax
	movw	%ax, %ds
	movl	0x46c, %eax
	popw	%ds
	ret


edd30_read_cdrom:
#;return cf=0 success, cf=1 fail, ah = fail code
#;will change all registers, including ES!!

	call	test_atapi_ready
	movb	$0xAA, %ah	# error code if CF=1
	jc	4f

	movw	%ds, %ax	# save DS

	# get DS for the disk address packet
	movw	%cs:(int13_old_ds - int13_handler), %ds
	movw	2(%si), %cx	# struc_int13ext.blk_count
	movw	4(%si), %di	# struc_int13ext.buf_addr_off
	movw	6(%si), %bx	# struc_int13ext.buf_addr_seg
	movl	8(%si), %edx	# struc_int13ext.blk_num_low1
	cmpb	$16, (%si)	# valid packet size is 16

	movw	%ax, %ds	# restore DS

	setne	%ah		# AH=1 for invalid function call
	stc			# error out if ...
	jne	4f		# ... invalid packet size

	movw	%bx, %es	# ES changed!!

	call	read_atapi

	movb	$0x0C, %ah	# error code if CF=1
4:
//	/* debug print AX and FLAGS */
//	pushfw
//	pushw	%ax
//	pushw	$read_cdrom_ah_flags - int13_handler	# the format string
//	call	realmode_printf
//	popw	%ax
//	popw	%ax
//	popfw
	ret

//read_cdrom_ah_flags:
//	.ascii	"edd30_read_cdrom: AX=%X, FLAGS=%X\r\n\0"


reg_reset:
#; call after reg_probe_exist
	pushaw

#;	mov_ax 0
#;	call __reg_select_dev

#if 0
	/* The ATA software reset mechanism, SRST, (bit 2 in the Device Control
	 * Register) cannot be used for ATAPI Device, because resets issued by
	 * the ATAPI driver would also reset any attached hard disk and vice
	 * versa. To solve this, ATAPI defines an ATAPI Soft Reset command
	 * using a reserved ATA opcode which could be decoded by the interface
	 * controller hardware.
	 */

	movb	$cmd_DC, %al
	orb	$0x04, %al	#; CB_DC_SRST = soft reset

	movw	reg_addr - int13_handler + (8 * 2), %dx		#; CB_DC
	outb	%al, %dx

	call	delay400ns
#endif

#if 0
	movb	$0x08, %al		#; ATAPI Soft Reset
	movw	reg_addr - int13_handler + (7 * 2), %dx		#; CB_CMD
	outb	%al, %dx

	call	delay400ns
#endif

#if 1
	/* set features (0xEF to command port) */
	/* set transfer mode (0x03 in feature register) */
	/* mode value in Sector Count register (0 for PIO default mode) */

	movb	$0, %al		#; PIO default transfer mode
	movw	reg_addr - int13_handler + (2 * 2), %dx	#; CB_SC = interrupt reason register
	outb	%al, %dx

	call	delay400ns

	movb	$0x03, %al	#; 0x03 = set transfer mode based on value in sector count register
	movw	reg_addr - int13_handler + (1 * 2), %dx	#; CB_FR = Feature Register
	outb	%al, %dx

	call	delay400ns

	movb	$0xEF, %al		#; Set Features Command
	movw	reg_addr - int13_handler + (7 * 2), %dx		#; CB_CMD
	outb	%al, %dx

	call	delay400ns
#endif

#if 1
	movb	$0x08, %al		#; ATAPI Soft Reset
	movw	reg_addr - int13_handler + (7 * 2), %dx		#; CB_CMD
	outb	%al, %dx

	call	delay400ns
#endif

#; Test only: do a hard reset and see if it works
#if 1
	/* The ATA software reset mechanism, SRST, (bit 2 in the Device Control
	 * Register) cannot be used for ATAPI Device, because resets issued by
	 * the ATAPI driver would also reset any attached hard disk and vice
	 * versa. To solve this, ATAPI defines an ATAPI Soft Reset command
	 * using a reserved ATA opcode which could be decoded by the interface
	 * controller hardware.
	 */

	movb	$cmd_DC, %al
	orb	$0x04, %al	#; CB_DC_SRST = soft reset

	movw	reg_addr - int13_handler + (8 * 2), %dx		#; CB_DC
	outb	%al, %dx

	call	delay400ns
#endif

	movb	$cmd_DC, %al
	movw	reg_addr - int13_handler + (8 * 2), %dx		#; CB_DC
	outb	%al, %dx

	call	delay400ns

	call	set_timeout

	cmpb	$0, reg_dev_info - int13_handler
	je	1f			#; master not exist

	call	sub_atapi_delay

	call	reg_poll_busy

1:
	cmpb	$0, reg_dev_info - int13_handler + 1
	je	1f			#; slave not exist

	call	sub_atapi_delay

	//call	reg_poll_busy		#; added recently for test

	//jmp	1f			#; added recently for test

2:
	movw	$1, %ax

	call	__reg_select_dev

	movw	reg_addr - int13_handler + (2 * 2), %dx	#; CB_SC = interrupt reason register
	inb	%dx, %al
	movb	%al, %ah

	movw	reg_addr - int13_handler + (3 * 2), %dx	#; CB_SN = reserved for SAM TAG byte
	inb	%dx, %al

	cmpw	$0x0101, %ax
	je	1f

	call	check_timeout
	jna	2b
1:
	popaw
	ret


test_atapi_ready:
#; return: cf =0 ready, cf =1 not ready
	pushaw
	movw	$2, %cx

1:
	pushw	%cx

	call	clear_atapi_buffer

	#; TEST UNIT READY Command
	#;
	#; The TEST UNIT READY command provides a means to check if the Device
	#; is ready. This is not a request for a self-test. If the Device would
	#; accept an appropriate medium-access command without returning CHECK
	#; CONDITION status, this command shall return a GOOD status. If the
	#; Device cannot become operational or is in a state such that a Host
	#; Computer action(e.g. START/STOP UNIT command with LoEj = 0 and Start
	#; = 1) is required to make the unit ready, the ATAPI CD-ROM Drive
	#; shall return CHECK CONDITION status with a sense key of NOT READY.
	#;
	#; Byte
	#;   0			Operation code (00h)
	#;   1			Reserved
	#;   2			Reserved
	#;   3			Reserved
	#;   4			Reserved
	#;   5			Reserved
	#;   6			Reserved
	#;   7			Reserved
	#;   8			Reserved
	#;   9			Reserved
	#;  10			Reserved
	#;  11			Reserved
	#;
	#; Using the TEST UNIT READY Command
	#;
	#; The TEST UNIT READY command is useful in that it allows a Host
	#; Computer to poll a Device until it is ready without the need to
	#; allocate space for returned data. It is especially useful to check
	#; cartridge status. ATAPI CD-ROM Drives are expected to respond
	#; promptly to indicate the current status of the device.

	# invoke reg_packet,byte,0,cs, atapi_tmp_buffer, 128
	pushw	$128
	pushw	$atapi_tmp_buffer - int13_handler
	pushw	%cs
	pushw	$0
	call	reg_packet
	addw	$8, %sp

//	/* debug print AX */
//	pushw	%ax
//	pushw	$test_atapi_ax - int13_handler	# the format string
//	call	realmode_printf
//	popw	%ax
//	popw	%ax

	orw	%ax, %ax
	jnz	2f

	call	get_atapi_sense

//	/* debug print AL, FLAGS */
//	pushfw
//	pushw	%ax
//	pushw	$test_atapi_al_flags - int13_handler	# the format string
//	call	realmode_printf
//	popw	%ax
//	popw	%ax
//	popfw

	jc	2f
	orb	%al, %al
	jnz	2f

	popw	%cx
	clc
	popaw
	ret
2:
	call	start_stop_unit		#; start unit

	popw	%cx
	loop	1b			#; try again

	stc
	popaw
	ret

//test_atapi_ax:
//	.ascii	"test_atapi_ax: AX=%X\r\n\0"

//test_atapi_al_flags:
//	.ascii	"test_atapi_al_flags: AX=%X, FLAGS=%X\r\n\0"


start_stop_unit:
#; return: cf =0 ready, cf =1 not ready
	pushaw
	movw	$1, %cx

1:
	pushw	%cx

	call	clear_atapi_buffer

	#; START/STOP UNIT Command
	#;
	#; The START/STOP UNIT command requests that the ATAPI CD-ROM Drive
	#; enable or disable media access operations.
	#;
	#;      |  bit     bit     bit     bit     bit     bit     bit     bit
	#; Byte |   7       6       5       4       3       2       1       0
	#; -----+--------------------------------------------------------------
	#;   0  |                    Operation code (1Bh)
	#; -----+-------------------------------------------------------+------
	#;   1  |                   Reserved                            | Immed
	#; -----+-------------------------------------------------------+------
	#;   2  |                        Reserved
	#; -----+--------------------------------------------------------------
	#;   3  |                        Reserved
	#; -----+-----------------------------------------------+-------+------
	#;   4  |                Reserved                       | LoEj  | Start
	#; -----+-----------------------------------------------+-------+------
	#;   5  |
	#; -----+--------------------------------------------------------------
	#;   6  |                        Reserved
	#; -----+--------------------------------------------------------------
	#;   7  |                        Reserved
	#; -----+--------------------------------------------------------------
	#;   8  |                        Reserved
	#; -----+--------------------------------------------------------------
	#;   9  |                        Reserved
	#; -----+--------------------------------------------------------------
	#;  10  |                        Reserved
	#; -----+--------------------------------------------------------------
	#;  11  |                        Reserved
	#; -----+--------------------------------------------------------------

	movb	$0x1B, atapi_cmd_buffer - int13_handler
	movb	$0x01, atapi_cmd_buffer - int13_handler + 4	#; start unit

	# invoke reg_packet,byte,0,cs, atapi_tmp_buffer, 128
	pushw	$128
	pushw	$atapi_tmp_buffer - int13_handler
	pushw	%cs
	pushw	$0
	call	reg_packet
	addw	$8, %sp

	orw	%ax, %ax
	jnz	2f

//	call	get_atapi_sense
//
//	jc	2f
//	orb	%al, %al
//	jnz	2f

	popw	%cx
	clc
	popaw
	ret
2:
	popw	%cx
	loop	1b

	stc
	popaw
	ret




reg_setup_base_addr:
#; input:  bx = base addr pointer
	pushw	%ax
	pushw	%di
	cld			# store upward
	movw	$reg_addr - int13_handler, %di	# points to 10-word space
	movw	%cs:(%bx), %ax	# get base address
	# store 8 ports(command block registers)

#if 0
	pushw	18(%di)
	pushw	16(%di)
	pushw	14(%di)
	pushw	12(%di)
	pushw	10(%di)
	pushw	8(%di)
	pushw	6(%di)
	pushw	4(%di)
	pushw	2(%di)
	pushw	(%di)
	pushw	2(%bx)
	pushw	%ax
	pushw	$reg_addresses - int13_handler		# the format string
	call	realmode_printf
	addw	$26, %sp			# adjust the stack pointer
#endif

	stosw
	incw	%ax
	stosw
	incw	%ax
	stosw
	incw	%ax
	stosw
	incw	%ax
	stosw
	incw	%ax
	stosw
	incw	%ax
	stosw
	incw	%ax
	stosw

	movw	%cs:2(%bx), %ax		# get base address
	stosw				# store 2 ports(control block registers)
	incw	%ax
	stosw
	popw	%di
	popw	%ax
	ret

#if 0
reg_addresses:
	.ascii	"reg_addresses: Data=%X, Ctrl=%X, di: %X, %X, %X, %X, %X, %X, %X, %X, %X, %X\r\n\0"
#endif

set_timeout:
	pushl	%eax
	call	read_bios_time
	addl	$(5 * 18), %eax	#; 20 seconds
				# not 20 seconds, but 5 seconds
	 			#; FIXME: Midnight overflow
	 			# Midnight overflow is handled in check_timeout
	movl	%eax, %cs:(time_out - int13_handler)
	popl	%eax
	ret


check_timeout:
#; you need to setup the timeout first
	pushl	%eax
	pushl	%ebx
	pushl	%edx
	call	read_bios_time
	movl	time_out - int13_handler, %ebx
	movl	%ebx, %edx
	subl	$(5 * 18), %ebx		# the time when we set_timeout
	cmpl	%ebx, %eax
	jnb	1f
	/* the next day */
	addl	$0x001800B0, %eax	/* 24 hours = 0x1800B0 ticks */
1:
	cmpl	%edx, %eax
	popl	%edx
	popl	%ebx
	popl	%eax
	ret


clear_atapi_buffer:
	pushaw
	pushw	%es
	pushw	%cs
	popw	%es
	cld
	movw	$atapi_tmp_buffer - int13_handler, %di
	movw	$128, %cx	# but note the buffer is 256-byte long!
	xorb	%al, %al
	rep stosb		# added the lost rep prefix
	movw	$atapi_cmd_buffer - int13_handler, %di
	movw	$16, %cx
	rep stosb		# added the lost rep prefix
	popw	%es
	popaw
	ret


reg_packet:
#;proc	reg_packet,withlocal,dir,packet_seg,packet_off,packet_len
#; input:
#; return ax = 0 noerror, ah = error code al= error bit
#;	 cx = len
	pushaw

	movw	%sp, %bp

	call	set_timeout
	movb	$0, _pre_fail_bit7_  - int13_handler

#;	outbytes CB_DC,cmd_DC_ni,CB_FR,0,CB_SC,0,CB_SN,0,
	# outbytes CB_DC,cmd_DC,CB_FR,0,CB_SC,0,CB_SN,0,
	movb	$cmd_DC, %al
	movw	reg_addr - int13_handler + (8 * 2), %dx	#; CB_DC
	outb	%al, %dx

	call	delay400ns	#; added recently

	call	sub_atapi_delay	#; added recently

	movb	$0, %al
	movw	reg_addr - int13_handler + (1 * 2), %dx	#; CB_FR = Feature Register
	outb	%al, %dx

	movb	$0, %al
	movw	reg_addr - int13_handler + (2 * 2), %dx	#; CB_SC = interrupt reason register
	outb	%al, %dx

	movb	$0, %al
	movw	reg_addr - int13_handler + (3 * 2), %dx	#; CB_SN = reserved for SAM TAG byte
	outb	%al, %dx


	# outbytes CB_CL,[.packet_len],CB_CH,[.packet_len+1]
	movb	24(%bp), %al		#; packet_len low byte
	movw	reg_addr - int13_handler + (4 * 2), %dx	#; CB_CL = byte count low
	outb	%al, %dx

	movb	25(%bp), %al		#; packet_len high byte
	movw	reg_addr - int13_handler + (5 * 2), %dx	#; CB_CH = byte count high
	outb	%al, %dx

	# outbyte CB_DH,[reg_cur_dev]
	movb	reg_cur_dev - int13_handler, %al
	movw	reg_addr - int13_handler + (6 * 2), %dx	#; CB_DH = drive select
	outb	%al, %dx

	# outbyte CB_CMD,CMD_PACKET
	movb	$0xA0, %al		#; CMD_PACKET
	movw	reg_addr - int13_handler + (7 * 2), %dx		#; CB_CMD
	outb	%al, %dx

	call	delay400ns

	call	sub_atapi_delay

	subw	%bx, %bx		#; clear error number

	# while {inbyte CB_ASTAT},{test al,CB_STAT_BSY|CB_STAT_ERR|CB_STAT_DRQ},z
3:
	# inbyte CB_ASTAT
	movw	reg_addr - int13_handler + (8 * 2), %dx		#; CB_ASTAT=8, CB_STAT=7
	inb	%dx, %al

	testb	$(CB_STAT_BSY | CB_STAT_DRQ | CB_STAT_ERR), %al
	jnz	3f

	orb	$FAILBIT0, %bl		#; error
	call	check_timeout
	jna	3b
3:
	# endwhile

	# while {inbyte CB_ASTAT},{test al, CB_STAT_BSY}, nz
3:
	# inbyte CB_ASTAT
	movw	reg_addr - int13_handler + (8 * 2), %dx		#; CB_ASTAT=8, CB_STAT=7
	inb	%dx, %al

	testb	$CB_STAT_BSY, %al
	jz	3f

	call	check_timeout
	jna	3b
	movw	$-1, 18(%bp)
	movb	$51, %bh		#; error
3:
	# endwhile

	cmpb	$0, %bh
	jnz	2f			#; error

	# inbyte CB_STAT,[.status]
	movw	reg_addr - int13_handler + (7 * 2), %dx		#; CB_STAT
	inb	%dx, %al
	movb	%al, _status_ - int13_handler

	# inbyte CB_SC, [.reason]
	movw	reg_addr - int13_handler + (2 * 2), %dx	#; CB_SC = interrupt reason register
	inb	%dx, %al
	movb	%al, _reason_ - int13_handler

	# inbyte CB_CL, [.bcnt]
	movw	reg_addr - int13_handler + (4 * 2), %dx	#; CB_CL = byte count low
	inb	%dx, %al
	movb	%al, _bcnt_ - int13_handler

	# inbyte CB_CH, [.bcnt+1]
	movw	reg_addr - int13_handler + (5 * 2), %dx	#; CB_CH = byte count high
	inb	%dx, %al
	movb	%al, _bcnt_ - int13_handler + 1

	movb	_status_ - int13_handler, %al
	andb	$(CB_STAT_BSY | CB_STAT_DRQ), %al
	cmpb	$CB_STAT_DRQ, %al
	jz	3f
	movb	$52, %bh		#; error

	jmp	2f
3:

#ifdef sane_check
	movb	_reason_ - int13_handler, %al
	testb	$(CB_SC_P_TAG | CB_SC_P_REL | CB_SC_P_IO), %al
	jnz	3f
	testb	$CB_SC_P_CD, %al
	jnz	4f
3:
	orb	$FAILBIT2, %bl		#; error
4:
	movw	_bcnt_ - int13_handler, %ax
	cmpw	24(%bp), %ax
	jz	3f
	orb	$FAILBIT3, %bl		#; error

3:

#endif	//; sane_check

	movw	$atapi_cmd_buffer - int13_handler, %si
#if 0
	movw	$12, %cx		#; cmd_buff_len is 12
	shrw	$1, %cx
#else
	movw	$6, %cx			#; cmd_buff_len is 12
#endif
	movw	reg_addr - int13_handler + (0 * 2), %dx		#; CB_DATA = 16-bit data port
	cld
	repz outsw

	call	delay400ns

	subw	%cx, %cx
3:
	# while

	call	sub_atapi_delay
4:
	# while {inbyte CB_ASTAT},{test al,CB_STAT_BSY},nz
	# inbyte CB_ASTAT
	movw	reg_addr - int13_handler + (8 * 2), %dx		#; CB_ASTAT=8, CB_STAT=7
	inb	%dx, %al

	testb	$CB_STAT_BSY, %al
	jz	4f
	call	check_timeout
	jna	4b
	movb	$54, %bh		#; error
	jmp	2f
	# endwhile
4:

	#; Data transfer loop
	#; read the primary state register

	# inbyte CB_STAT,[.status]
	movw	reg_addr - int13_handler + (7 * 2), %dx		#; CB_STAT
	inb	%dx, %al
	movb	%al, _status_ - int13_handler

	# inbyte CB_SC, [.reason]
	movw	reg_addr - int13_handler + (2 * 2), %dx	#; CB_SC = interrupt reason register
	inb	%dx, %al
	movb	%al, _reason_ - int13_handler

	# inbyte CB_CL, [.bcnt]
	movw	reg_addr - int13_handler + (4 * 2), %dx	#; CB_CL = byte count low
	inb	%dx, %al
	movb	%al, _bcnt_ - int13_handler

	# inbyte CB_CH, [.bcnt+1]
	movw	reg_addr - int13_handler + (5 * 2), %dx	#; CB_CH = byte count high
	inb	%dx, %al
	movb	%al, _bcnt_ - int13_handler + 1

	testb	$(CB_STAT_BSY | CB_STAT_DRQ), _status_ - int13_handler
	jnz	4f
	orb	$0x80, 18(%bp)		#; NON_DATA
	jmp	2f
4:
	movb	_status_ - int13_handler, %al
	andb	$(CB_STAT_BSY | CB_STAT_DRQ), %al
	cmpb	$CB_STAT_DRQ, %al
	jz	4f
	movb	$55, %bh		#; error
	jmp	2f
4:

#ifdef sane_check
	testb	$(CB_SC_P_TAG | CB_SC_P_REL | CB_SC_P_CD), _reason_ - int13_handler
	jz	4f
	orb	$FAILBIT4, %bl		#; error
4:
	testb	$CB_SC_P_IO, _reason_ - int13_handler
	jz	4f
	cmpb	$0, 18(%bp)
	jz	4f
	orb	$FAILBIT5, %bl		#; error
4:
#endif	//; sane_check

	#; do the slow data transfer

	call	sub_atapi_delay

	movw	_bcnt_ - int13_handler, %ax
	orw	%ax, %ax
	jnz	4f
	movb	$60, %bh		#; error
	orb	$0x80, 18(%bp)		#; NON_DATA
	jmp	2f
4:

#ifdef sane_check
	cmpw	$0x8000, %ax		#; REG_ATAPI_MAX_BYTES
	jna	4f
	orb	$FAILBIT6, %bl		#; error
4:
#endif	//; sane_check
	movb	_pre_fail_bit7_ - int13_handler, %dl
	orb	%dl, %bl		#; error
	testb	$1, %al
	setnz	_pre_fail_bit7_ - int13_handler

	movw	%cx, %dx
	addw	%ax, %dx
	cmpw	24(%bp), %dx
	jna	4f
	movb	$59, %bh		#; error
	orb	$0x80, 18(%bp)		#; NON_DATA
	jmp	2f
4:

	pushw	%dx
	movw	%ax, %cx
	incw	%cx
	shrw	$1, %cx
	movw	reg_addr - int13_handler + (0 * 2), %dx		#; CB_DATA = 16-bit data port
	cld
	cmpb	$0, 18(%bp)
	jz	4f
	pushw	%ds
	movw	20(%bp), %ds		#; packet_seg
	movw	22(%bp), %si		#; packet_off
	repz outsw
	popw	%ds
	jmp	5f
4:
	pushw	%es
	movw	20(%bp), %es		#; packet_seg
	movw	22(%bp), %di		#; packet_off
	repz insw
	popw	%es
5:
	popw	%cx

	addw	%ax, 22(%bp)		#; packet_off
	call	delay400ns
	jmp	3b
	# endwhile
3:
	testb	$0x80, 18(%bp)		#; NON_DATA
	jnz	3f
	call	sub_atapi_delay

	call	check_timeout		# !!added recently!!
	jna	3f

	movb	$57, %bh		#; error: time is out
	jmp	2f
3:

	# inbyte CB_STAT, #; [.status]
	movw	reg_addr - int13_handler + (7 * 2), %dx		#; CB_STAT
	inb	%dx, %al

	testb	$(CB_STAT_BSY | CB_STAT_DRQ | CB_STAT_ERR), %al
	jz	1f

	movb	$58, %bh		#; error
	jmp	2f
1:
	# inbyte CB_SC, [.reason]
	movw	reg_addr - int13_handler + (2 * 2), %dx	#; CB_SC = interrupt reason register
	inb	%dx, %al
	movb	%al, _reason_ - int13_handler
2:

#ifdef check_extra_fail
	movb	_reason_ - int13_handler, %al
	testb	$(CB_SC_P_TAG | CB_SC_P_REL), %al
	jnz	1f
	testb	$CB_SC_P_IO, %al
	jz	1f
	testb	$CB_SC_P_CD, %al
	jnz	2f
1:
	orw	$FAILBIT8, %bx		#; error
2:
#endif	//; check_extra_fail

#;	outbyte CB_DC,cmd_DC

	movw	%bx, 14(%bp)	# __AX	#; error
	movw	%cx, 12(%bp)	# __CX

	popaw
	ret

#	local status,1,reason,1,bcnt,2,pre_fail_bit7,1
_status_:		.byte	0
_reason_:		.byte	0
_bcnt_:			.byte	0, 0
_pre_fail_bit7_:	.byte	0
# //; end of function reg_packet


delay400ns:

	pushw	%cx
	movw	$0x0040, %cx
1:
	pause
	loop	1b
	popw	%cx
	ret

	# inbyte CB_ASTAT
	movw	reg_addr - int13_handler + (8 * 2), %dx		#; CB_ASTAT=8, CB_STAT=7
	inb	%dx, %al
	inb	%dx, %al
	inb	%dx, %al
	inb	%dx, %al
	ret


sub_atapi_delay:
#; delay a few clicks
	cmpl	$0, EXT_C(slow_atapi_device) - int13_handler
	jz	1f

	# delay 1 millisecond

	pushl	%ecx
	//pushfw

	//sti		#; sti should already be done by the caller

	movl	delay_repeat_num - int13_handler, %ecx	# loops per millisecond
2:
	call	read_bios_time
	cmpw	%ax, %ax	# for more accurate timing
	addr32 loope	2b

	//popfw
	popl	%ecx

1:
	ret


reg_poll_busy:
#; need to setup the timeout first
#;return ax=0	ok
#;	ax = 1	timeout

1:
	movw	reg_addr - int13_handler + (7 * 2), %dx		#; CB_STAT
	inb	%dx, %al

	andb	$0x80, %al	# CB_STAT_BSY
	jz	1f
	call	check_timeout
	jna	1b
	# timeout
1:
	ret


get_atapi_sense:
#; return: cf =0 success, al = sense key, bl = asc, bh = ascq
#;	  cf =1 failed

	call	clear_atapi_buffer

	#; REQUEST SENSE Command
	#;
	#; The REQUEST SENSE command requests that the ATAPI CD-ROM Drive
	#; transfer sense data to the Host Computer.
	#;
	#; Byte
	#;   0			Operation code (03h)
	#;   1			Reserved
	#;   2			Reserved
	#;   3			Reserved
	#;   4			Allocation Length
	#;   5			Reserved
	#;   6			Reserved
	#;   7			Reserved
	#;   8			Reserved
	#;   9			Reserved
	#;  10			Reserved
	#;  11			Reserved

	movb	$0x03, atapi_cmd_buffer - int13_handler
	movb	$32, atapi_cmd_buffer - int13_handler + 4

	# invoke reg_packet,byte,0,cs, atapi_tmp_buffer, 128
	pushw	$128
	pushw	$atapi_tmp_buffer - int13_handler
	pushw	%cs
	pushw	$0
	call	reg_packet
	addw	$8, %sp

	orw	%ax, %ax
	jnz	1f

	movb	atapi_tmp_buffer - int13_handler, %al
	/* bit 7 is `Valid' bit, and should be 1
	 * bit 0-6 is the error code and can be 70h or 71h
	 */
	andb	$0x7f, %al
	cmpb	$0x70, %al
	je	2f
	cmpb	$0x71, %al
	jne	1f
2:
	movb	atapi_tmp_buffer - int13_handler + 2, %al	#; get sense key
	andb	$0x0f, %al	#; low 4 bits are sense key
	xorw	%bx, %bx
	cmpb	$0x06, atapi_tmp_buffer - int13_handler + 7	#; additional sense length
	jb	3f
	movw	atapi_tmp_buffer - int13_handler + 12, %bx
3:
	clc
	ret
1:
	stc
	ret


edd30_for_cdrom:
//	; Stack layout:
//	;   +10 INT flags
//	;   +8  INT CS
//	;   +6  INT IP
//	;   +2  EAX
//	; BP+0 BP
//	;   -2 ax
//	;   -4 cx
//	;   -6 dx
//	;   -8 bx
//	;   -10 sp
//	;   -12 bp
//	;   -14 si
//	;   -16 di
//	;   -18 ds
//	;   -20 es
//	;   -24 edx

	/* will read these variables:
	 *	int13_old_eax
	 *	int13_old_ebx
	 *	int13_old_ecx
	 *	int13_old_edx
	 *	int13_old_esi
	 *	int13_old_edi
	 *	int13_old_ebp
	 *	int13_old_ds
	 *	int13_old_es
	 *	int13_old_flags
	 *	int13_old_cs_ip
	 * will write these variables:
	 *	int13_old_eax
	 *	int13_old_ebx
	 *	int13_old_ecx
	 *	int13_old_flags
	 */

	sti

	cld

	pushw	%cs
	popw	%ds

	movzbw	%dl, %ax
	subb	%cs:(EXT_C(min_cdrom_id) - int13_handler), %al
	call	select_atapi	#; select_atapi_force
	movw	%cs:(int13_old_eax - int13_handler), %ax

	jnc	1f
	movb	$0xAA, %ah
	jmp	2f	//fail_out
1:
	cmpb	$0, %ah
	je	6f	//reset
	cmpb	$1, %ah
	je	7f	//get_last_stat
	cmpb	$0x41, %ah
	je	8f	//install_check
	cmpb	$0x42, %ah
	je	9f	//ext_read
	cmpb	$0x43, %ah
	je	10f	//ext_write
	cmpb	$0x44, %ah		/* verify sectors */
	je	5f	//success_out
	cmpb	$0x47, %ah		/* extended seek */
	je	5f	//success_out
	cmpb	$0x48, %ah
	je	11f	//get_drv_param
	cmpb	$0x4B, %ah
	jne	3f	//invalid_cmd

	//stop_disk_emu:
	cmpb	$1, %al			/* only 0x4B01 supported */
	jne	3f	//invalid_cmd

	movw	%si, %di
	movw	%cs:(int13_old_ds - int13_handler), %es
	movw	$0x0013, %ax	/* packet size=13h, boot type=0 (no-emu) */
	cld
	stosw
	//movb	%dl, %al	/* drive=DL, controller=0 */
	movb	%cs:(int13_old_edx - int13_handler), %al	/* drive=DL, controller=0 */
	stosw
	xorw	%ax, %ax
	stosw
	stosw			/* LBA for no-emu image=0 */
	stosw			/* device specification */
	stosw			/* user buffer segment */
	stosw			/* load segment */
	movb	$4, %al
	stosw			/* sector count=4 */
				/* CHS makes no sense for no-emu */
	jmp	5f	//success_out

11:	//get_drv_param:
	movw	%cs:(int13_old_ds - int13_handler), %ds
	cmpw	$26, (%si)
	jb	3f		//invalid_cmd

	movw	$26, (%si)
//	movw	$0x74, 2(%si)		#; struc_extparam.flags: removable, lock, chg line
	movw	$0x00, 2(%si)		# none, tinybit 2007-11-15
	movw	$0x800, 24(%si)		# bytes per sect=2048
	xorl	%eax, %eax
	decw	%ax
	movl	%eax, 4(%si)		# cylinders=0xFFFF
	movb	$0, %ah
	movl	%eax, 8(%si)		# heads=0xFF
	movb	$15, %al
	movl	%eax, 12(%si)		# sectors per track=15
	movl	%eax, 20(%si)		# total sectors hi dword=0
	xorw	%ax, %ax		# CF cleared
	decl	%eax			# EAX=0xFFFFFFFF
	movl	%eax, 16(%si)		# total sectors lo dword
					# CF is cleared
	jmp	5f	//success_out

10:	//ext_write:
	movb	$0x03, %ah	# error code of write-protection
	jmp	2f	//fail_out

9:	//ext_read:
	call	edd30_read_cdrom
	jnc	5f	//success_out
	jmp	2f	//fail_out

8:	//install_check:
	cmpw	$0x55AA, %bx	# added by tinybit 2007-11-15
	jne	3f	//invalid_cmd

	movw	$0xAA55, %cs:(int13_old_ebx - int13_handler)	#; bx=0xaa55
	movb	$0x21, %ah	# ah=0x21  edd-1.1
	movb	$0x01, %cs:(int13_old_ecx - int13_handler)	# cx=0x01, ext disk access ok -- tinybit 2007-11-15
	jmp	4f	//success_out_no_ah

7:	//get_last_stat:
	movb	%cs:(int13_last_stat - int13_handler), %ah
	jmp	4f	//success_out_no_ah

6:	//reset:
	call	reg_reset
	movw	%cs:(atapi_cur_dev - int13_handler), %ax	# device serial number
	call	select_atapi_force

5:	//success_out:
	xorb	%ah, %ah

4:	//success_out_no_ah:
	andb	$0xFE, %cs:(int13_old_flags - int13_handler)
	jmp	1f

3:	//invalid_cmd:
	movb	$0x01, %ah

2:	//fail_out:
	orb	$1, %cs:(int13_old_flags - int13_handler)	// set CF=1 for ERROR

1:
	/* set error code AH */
	movb	%ah, %cs:(int13_last_stat - int13_handler)
	movb	%ah, %cs:(int13_old_eax + 1 - int13_handler)

	movl	%cs:(int13_old_eax - int13_handler), %eax
	movl	%cs:(int13_old_ebx - int13_handler), %ebx
	movl	%cs:(int13_old_ecx - int13_handler), %ecx
	movl	%cs:(int13_old_edx - int13_handler), %edx
	movl	%cs:(int13_old_esi - int13_handler), %esi
	movl	%cs:(int13_old_edi - int13_handler), %edi
	movl	%cs:(int13_old_ebp - int13_handler), %ebp
	movw	%cs:(int13_old_ds - int13_handler), %ds
	movw	%cs:(int13_old_es - int13_handler), %es

	/* restore old flags */
	pushw	%cs:(int13_old_flags - int13_handler)
	popfw
	/* transfer control to caller */
	ljmp	*%cs:(int13_old_cs_ip - int13_handler)

#;============================================================================

int13_last_stat:	.byte	0			#; the error code in AH

start_of_atapi_data:

atapi_cur_dev:		.word	0	#; device serial number
reg_cur_dev:		.byte	0	#; 0xA0 for master, 0xB0 for slave
time_out:		.long	0
reg_dev_info:		.byte	0, 0


atapi_dev_base:		.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device

atapi_dev_base_bak:	.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device
			.word	0, 0	#; base pointer, device

reg_addr:		.word	0, 0, 0, 0, 0, 0, 0, 0, 0, 0
atapi_cmd_buffer:	.space	16
atapi_tmp_buffer:	.space	256
//atapi_devinfo:	.space	32	# SIZE_OF_ATAPI_DEVINFO
delay_repeat_num:	.long	0	# loops per millisecond

end_of_atapi_data:

			.align	16

edd30_disk_buffer:	.space	0x800

ENTRY(slow_atapi_device)
			.long	0

#;============================================================================

/* void realmode_printf(const char *format, ...)
 *
 * input:	format is offset in CS segment
 *
 * Usage example:
 *
 * 		pushw	IntegerN
 *		 ... ... ... ...
 * 		pushw	Integer2
 * 		pushw	Integer1
 * 		pushw	$format_string - int13_handler
 *		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

#;============================================================================

	.align	16

GDT_data:		/* used by int15/ah=87h */

/*00*/	.long	0, 0	/* unused descriptor, must be 0 */
/*08*/	.long	0, 0	/* GDT descriptor, must be 0 */

	/* source descriptor */
/*10*/	.word	0xffff	/* segment limit, 64K */
/*12*/	.byte	0, 0, 0	/* physical address(low 24 bits) */
/*15*/	.byte	0x93	/* access rights, 93h=readable/writable */
/*16*/	.byte	0	/* low 4 bits are high 4 bits of segment limit(here
			   should be 0). high 4 bits are flags, also should
			   be set to 0 for this function call */
/*17*/	.byte	0	/* physical address(high 8 bits) */

	/* destination descriptor */
/*18*/	.word	0xffff	/* segment limit, 64K */
/*1A*/	.byte	0, 0, 0	/* physical address(low 24 bits) */
/*1D*/	.byte	0x93	/* access rights, 93h=readable/writable */
/*1E*/	.byte	0	/* low 4 bits are high 4 bits of segment limit(here
			   should be 0). high 4 bits are flags, also should
			   be set to 0 for this function call */
/*1F*/	.byte	0	/* physical address(high 8 bits) */

/*20*/	.long	0, 0	/* code segment descriptor, must be 0 */
/*28*/	.long	0, 0	/* stack segment descriptor, must be 0 */
/*30*/

	.align	16

MyGDT:	.word	MyGDTEnd - MyGDT - 1
	.long	0		/* Pointer to self */
	.word	0

MyRMDS:	.long	0x0000ffff	/* 64K data segment */
	.long	0x00009200

MyPMDS:	.long	0x0000ffff	/* 4GB data segment */
	.long	0x00cf9200

MyGDTEnd:


	#;*******************************************************************
	#;			ATA Registers Layout
	#;*******************************************************************
	#;
	#; Address		Read			Write
	#; ---------	---------------------	-----------------------------
	#; A N 0 0 0	16-bit data port for both read and write
	#; ------------------------------------------------------------------
	#; A N 0 0 1	ATAPI/ATA Error(Read)	ATAPI/ATA Feature(Write)
	#; ------------------------------------------------------------------
	#; A N 0 1 0	ATAPI Interrupt Reason(Read) / ATA Sector Count
	#; ------------------------------------------------------------------
	#; A N 0 1 1	Reserved for SAM TAG Byte / ATA Sector Number
	#; ------------------------------------------------------------------
	#; A N 1 0 0	ATAPI Byte Count LSB(R/W) / ATA Cylinder Low(R/W)
	#; ------------------------------------------------------------------
	#; A N 1 0 1	ATAPI Byte Count MSB(R/W) / ATA Cylinder High(R/W)
	#; ------------------------------------------------------------------
	#; A N 1 1 0	ATAPI Drive Select(R/W) / ATA Drive/Head Select(R/W)
	#; ------------------------------------------------------------------
	#; A N 1 1 1	ATAPI/ATA Status(Read)	ATA Command(Write)
	#; ------------------------------------------------------------------
	#; N A 1 1 0	Alternate Status(Read)	Device Control(Write)
	#; ------------------------------------------------------------------

reg_base_addr:
			.word	0x01f0, 0x03f6
			.word	0x0170, 0x0376
			.word	0x00f0, 0x02f6
			#;.word	0x0070, 0x0276	//port 70 and 71 is for CMOS
			.word	0x0180, 0x0386
			.word	0x6b00, 0x6f00
			.word	0x7300, 0x7700
ENTRY(reg_base_addr_append)
			.word	0, 0, 0, 0

/* EBIOS_disk_address_packet should be at the end of the handler because some
 * buggy BIOSes could destroy the memory that immediately follows.
 */

EBIOS_disk_address_packet:
	.byte	0x10	/* packet size, 16 or more */
	.byte	0	/* reserved, must be 0 */
	.byte	0	/* number of sectors, must be from 1 to 127 */
	.byte	0	/* reserved, must be 0 */
	.word	0	/* displacement of memory address */
	.word	0	/* segment of memory address */
	.long	0	/* 64bit, start logical sector number */
	.long	0

/* Don't insert code or data here! Buggy BIOSes could overwrite this area! */

//	.align	16

//	.space	0x1000	/* 4KB stack */

int13_handler_end:

#;============================================================================

#define CDROM_INIT
#ifdef CDROM_INIT

/* unsigned long
 * init_atapi() : return number of atapi cdrom devices.
 */

ENTRY(init_atapi)

	.code32

	pushl	%ebp
	pushl	%ebx

	call	EXT_C(prot_to_real)	/* enter real mode */

	.code16

	sti		/* enable interrupt for ATAPI */

	cld

	/* set CS base to be int13_handler */
#if 0
	ljmp	$((ABS(int13_handler)) >> 4), $(1f - int13_handler)
#else
	.byte	0xEA
	.word	1f - int13_handler
	.word	(ABS(int13_handler)) >> 4
#endif
1:
	pushaw
	pushw	%es
	pushw	%ds

	pushw	%cs
	popw	%ds
	pushw	%cs
	popw	%es
	xorw	%ax, %ax

	# initialize variables

	movw	$(start_of_atapi_data - int13_handler), %di
	movw	$(end_of_atapi_data - start_of_atapi_data), %cx
	repz stosb

	decw	atapi_cur_dev - int13_handler	# device serial number initialized to 0xFFFF

	#; begin init_timer

	call	read_bios_time
	movw	%ax, %bx		#; store initial timer in BX
1:
	call	read_bios_time
	cmpw	%ax, %bx		#; timer just changed?
	je	1b			#; no, continue.

	/* now AX=BX+1 */

	movw	%ax, %bx		#; store initial timer in BX
	xorl	%ecx, %ecx
1:
	call	read_bios_time
	cmpw	%ax, %bx		#; timer changed?
	addr32 loope	1b

	negl	%ecx

	/* ECX loops=a tick=1/18.2second=1000/18.2(=54.9) milliseconds */

	movl	%ecx, %eax
	xorl	%edx, %edx
	xorl	%ecx, %ecx
	movw	$55, %cx		# a tick is about 55 milliseconds
	divl	%ecx

	/* EAX=loops per millisecond */

	movl	%eax, delay_repeat_num - int13_handler

	#; end init_timer

	call	init_atapi_cdroms

	//movw	$-1, atapi_cur_dev - int13_handler	# device serial number initialized to 0xFFFF

	popw	%ds
	popw	%es

	movw	%sp, %bp
	movw	%cx, 8(%bp)	#; 8(%bp) is old BX on stack !
	popaw

	ljmp	$0, $ABS(1f)
1:

	DATA32	call	EXT_C(real_to_prot)
	.code32

	movzwl	%bx, %eax

	popl	%ebx
	popl	%ebp
	ret

#endif	/* CDROM_INIT */


#ifdef CDROM_INIT

init_atapi_cdroms:

	.code16

#; return: CF=0 success, CX=number of cdroms
#;         CF=1 failed, no cdrom found
#;	push cs
#;	pop es

	call	reg_probe	#; return cx = number of atapi devices

	orw	%cx, %cx
	jnz	1f

	call	reg_probe	#; return cx = number of atapi devices

	orw	%cx, %cx
	jnz	1f

	stc
	ret

1:
#if 1
	movw	$atapi_dev_base - int13_handler, %si	#; array of ATAPI reg pointer and dev
	movw	$atapi_dev_base_bak - int13_handler, %di	#; for CDROMs

	cld

	pushw	%si
	pushw	%di

	xorw	%ax, %ax
	xorw	%bx, %bx
1:
	call	select_atapi_force	#;input: ax = device serial number
					#;return: cf=0 success, cf=1 failed


	jc	2f

	call	check_atapi_cdrom	#;return: cf=0 is cdrom, cf=1 not cdrom

	jc	2f

	/* It is CDROM */

	incw	%bx			#;count the cdroms
	movsw				#;store CDROM base pointer
	movsw				#;store CDROM device number
	subw	$4, %si

	# debug print the reg and dev
	pushw	%bx

	movw	(%si), %bx		# BX=reg group pointer

	pushw	2(%si)			# CDROM device number
	pushw	2(%bx)			# base port of control block regs, i.e., device control port
	pushw	(%bx)			# base port of command block regs, i.e., data port
	pushw	$cdrom_reg_dev - int13_handler		# the format string
	call	realmode_printf
	addw	$8, %sp			# adjust the stack pointer

	popw	%bx

2:
	addw	$4, %si
	incw	%ax			#; try next ATAPI device
	loop	1b

	popw	%si			#; points to atapi_dev_base_bak, which is for CDROMs
	popw	%di			#; points to atapi_dev_base, which is for ATAPIs

	movw	$64, %cx		#; overwrite ATAPIs with CDROMs
	repz movsb

	movb	%bl, EXT_C(atapi_dev_count) - int13_handler
	movb	EXT_C(min_cdrom_id) - int13_handler, %cl
	addb	%bl, %cl
	decw	%cx
	movb	%cl, max_cdrom_id - int13_handler
	movw	%bx, %cx
#endif

	clc
	ret

cdrom_reg_dev:
	.ascii	"CDROM device found: Data=%X, Ctrl=%X, dev=%X(Note: 0=master, 1=slave)\r\n\0"

#endif	/* CDROM_INIT */


#ifdef CDROM_INIT

/* Called by init_atapi_cdroms
 * Calls reg_setup_base_addr, reg_reset
 */
reg_probe:

	.code16

#; return cx = number of atapi devices
	pushw	%ax
	pushw	%bx
	pushw	%si
	pushw	%di
	cld
	movb	$0, EXT_C(atapi_dev_count) - int13_handler		# reset counter
	movw	$reg_base_addr - int13_handler, %bx		# BX points to base address array
	movw	$atapi_dev_base - int13_handler, %di		# will store base pointers and device numbers
1:
	call	reg_setup_base_addr	# setup the base reg addresses(reg_addr) for reg base address BX

	#; begin reg_probe_exist
	movb	$cmd_DC, %al
	movw	reg_addr - int13_handler + (8 * 2), %dx		#; CB_DC
	outb	%al, %dx

	movw	$0, %ax

	call	reg_probe_dev_exist

	movb	%al, reg_dev_info - int13_handler	# master device existence
	movw	$1, %ax

	call	reg_probe_dev_exist

	movb	%al, reg_dev_info - int13_handler + 1	# slave device existence

	#; end reg_probe_exist

	call	reg_reset

	xor	%si, %si		# SI=0 for master
2:
//	movw	%si, %ax
//	call	reg_probe_dev_exist

	cmpb	$0, (reg_dev_info - int13_handler)(%si)		# check existence
//	cmpb	$0, %al						# check existence
	je	3f						# device not exist, so skip

	/* The device exists, so do a further check for device type.  */

	movw	%si, %ax

	#; begin reg_check_dev_type

	#; call after a reset

	call	__reg_select_dev

	movw	reg_addr - int13_handler + (2 * 2), %dx	#; CB_SC = interrupt reason register
	inb	%dx, %al
	movb	%al, %ah

	movw	reg_addr - int13_handler + (3 * 2), %dx	#; CB_SN = reserved for SAM TAG byte
	inb	%dx, %al

	cmpw	$0x0101, %ax		# success?
	movw	$1, %ax			# REG_CONFIG_TYPE_UNKN
	jne	4f			# No. The device type is unknown.

	movw	reg_addr - int13_handler + (4 * 2), %dx	#; CB_CL = byte count low
	inb	%dx, %al
	movb	%al, %ah

	movw	reg_addr - int13_handler + (5 * 2), %dx	#; CB_CH = byte count high
	inb	%dx, %al

	cmpw	$0x14EB, %ax		# Is ATAPI?
	je	5f			# yes.

	testb	%al, %al		# 0 is for ATA
	movw	$1, %ax			# REG_CONFIG_TYPE_UNKN
	jnz	4f
					# AX=1

	movw	reg_addr - int13_handler + (7 * 2), %dx		#; CB_STAT
	inb	%dx, %al
					# AH=0
	//if AL==0, then AX=1=REG_CONFIG_TYPE_UNKN, else AX=2=REG_CONFIG_TYPE_ATA
	testb	%al, %al
	setne	%al
	incw	%ax
	jmp	4f
5:
	movw	$3, %ax			# REG_CONFIG_TYPE_ATAPI
4:

	#; end reg_check_dev_type


	movb	%al, (reg_dev_info - int13_handler)(%si)	#; added (%si) recently
	cmpb	$3, %al			# Is ATAPI?
	jne	3f			# no, ignore it.

	#; It is ATAPI, so add it to the list at atapi_dev_base
	incb	EXT_C(atapi_dev_count) - int13_handler	# count it
	movw	%bx, %ax		# store the pointer to the base reg pair
	stosw
	movw	%si, %ax		# store the device number(master/slave)
	stosw

	# debug print the reg and dev
	pushw	%si
	pushw	2(%bx)			# base port of control block regs, i.e., device control port
	pushw	(%bx)			# base port of command block regs, i.e., data port
	pushw	$atapi_reg_dev - int13_handler		# the format string
	call	realmode_printf
	addw	$8, %sp			# adjust the stack pointer
3:
	incw	%si			# SI=1 for slave
	cmpw	$2, %si
	jb	2b

	addw	$4, %bx			# try next group of base reg
	#; incw	%bx			# try next group of base reg
	#; incw	%bx
	cmpw	$0, (%bx)		# end?
	jnz	1b			# no, probe next reg group

	movzbw	EXT_C(atapi_dev_count) - int13_handler, %cx
	movw	%cx, %bx
	decw	%bx
	movb	EXT_C(min_cdrom_id) - int13_handler, %al
	addb	%bl, %al
	movb	%al, max_cdrom_id - int13_handler
	popw	%di
	popw	%si
	popw	%bx
	popw	%ax
	ret

atapi_reg_dev:
	.ascii	"ATAPI device found: Data=%X, Ctrl=%X, dev=%X(Note: 0=master, 1=slave)\r\n\0"

#endif	/* CDROM_INIT */


#ifdef CDROM_INIT

check_atapi_cdrom:

	.code16

#;return: cf =0 is cdrom, cf =1 not cdrom
	pushaw
#;	push es
#;	push cs
#;	pop es
#;	mov di, atapi_devinfo
	call	inquiry_atapi
	jc	1f
	clc

#define	ATATYPE_CDR	0x4	//#; Write-once device
#define	ATATYPE_CD	0x5	//#; CD-ROM device

	cmpb	$ATATYPE_CD, %al
	je	1f
	cmpb	$ATATYPE_CDR, %al
	je	1f
	stc
1:
#;	pop es
	popaw
	ret

#endif	/* CDROM_INIT */


#ifdef CDROM_INIT

inquiry_atapi:

	.code16

#;input: es:di -> atapi_devinfo
#;return: cf =0 success, al = device type,
#;        cf =1 fail
#;	save si, di, cx
	call	clear_atapi_buffer
	movb	$0x12, atapi_cmd_buffer - int13_handler
	movb	$128, atapi_cmd_buffer - int13_handler + 4

	# invoke reg_packet,byte,0,cs, atapi_tmp_buffer, 128
	pushw	$128
	pushw	$atapi_tmp_buffer - int13_handler
	pushw	%cs
	pushw	$0
	call	reg_packet
	addw	$8, %sp

	orw	%ax, %ax
	jnz	1f

	movb	atapi_tmp_buffer - int13_handler, %al
	testb	$0xe0, %al
	jnz	1f

	andb	$0x1f, %al

#if 0
	mov [es:di + struc_atapi_devinfo.dev_type], al

	mov ah, [atapi_tmp_buffer+7]
	mov [es:di + struc_atapi_devinfo.dev_flags], ah

	add di, struc_atapi_devinfo.vender_id
	mov si, atapi_tmp_buffer + 8
	mov cx, 24
	cld
	rep movsb
#endif
	clc
	ret
1:
	stc
	ret

#endif	/* CDROM_INIT */


#ifdef CDROM_INIT

reg_probe_dev_exist:

	.code16

#; input ax=dev
#; return ax = 1: exist		# <----- "ax=1" should be "al=1"
	call	__reg_select_dev

	# outbytes CB_SC,0x55,CB_SN,0xaa,CB_SC,0xaa,CB_SN,0x55,CB_SC,0x55
	movb	$0x55, %al
	movw	reg_addr - int13_handler + (2 * 2), %dx	#; CB_SC = interrupt reason register
	outb	%al, %dx

	movb	$0xAA, %al
	movw	reg_addr - int13_handler + (3 * 2), %dx	#; CB_SN = reserved for SAM TAG byte
	outb	%al, %dx

	movb	$0xAA, %al
	movw	reg_addr - int13_handler + (2 * 2), %dx	#; CB_SC = interrupt reason register
	outb	%al, %dx

	movb	$0x55, %al
	movw	reg_addr - int13_handler + (3 * 2), %dx	#; CB_SN = reserved for SAM TAG byte
	outb	%al, %dx

	movb	$0x55, %al
	movw	reg_addr - int13_handler + (2 * 2), %dx	#; CB_SC = interrupt reason register
	outb	%al, %dx

	movb	$0xAA, %al
	movw	reg_addr - int13_handler + (3 * 2), %dx	#; CB_SN = reserved for SAM TAG byte
	outb	%al, %dx

	movw	reg_addr - int13_handler + (2 * 2), %dx	#; CB_SC = interrupt reason register
	inb	%dx, %al
	movb	%al, %ah

	movw	reg_addr - int13_handler + (3 * 2), %dx	#; CB_SN = reserved for SAM TAG byte
	inb	%dx, %al

	cmpw	$0x55AA, %ax
	sete	%al
	ret

#endif	/* CDROM_INIT */

	.code32


/*
 * chain_stage1(segment, offset, part_table_addr)
 *
 *  This starts another stage1 loader, at segment:offset.
 */

ENTRY(chain_stage1)
	/* no need to save anything, just use %esp */

	/* store %ESI, presuming %ES is 0 */
	movl	0xc(%esp), %esi

	/* store new offset */
	movl	0x8(%esp), %eax
	movl	%eax, offset

	/* store new segment */
	movw	0x4(%esp), %ax
	movw	%ax, segment

	/* set up to pass boot drive */
	movb	EXT_C(boot_drive), %dl

	call	EXT_C(prot_to_real)

	.code16

	//sti		/* it is not bad keeping interrupt off */
	sti		/* for hardware interrupt or watchdog */

#ifdef ABSOLUTE_WITHOUT_ASTERISK
	DATA32	ADDR32	ljmp	(offset)
#else
	DATA32	ADDR32	ljmp	*(offset)
#endif
	.code32
#endif /* STAGE1_5 */


#ifdef STAGE1_5
/*
 * chain_stage2(segment, offset, second_sector)
 *
 *  This starts another stage2 loader, at segment:offset.  It presumes
 *  that the other one starts with this same "asm.S" file, and passes
 *  parameters by writing the embedded install variables.
 */

ENTRY(chain_stage2)
	/* no need to save anything, just use %esp */

	/* store new offset */
	movl	0x8(%esp), %eax
	movl	%eax, offset
	movl	%eax, %ebx

	/* store new segment */
	movw	0x4(%esp), %ax
	movw	%ax, segment
	shll	$4, %eax

	/* generate linear address */
	addl	%eax, %ebx

	/* set up to pass the partition where stage2 is located in */
	movl	EXT_C(current_partition), %eax
	movl	%eax, (EXT_C(install_partition)-EXT_C(main))(%ebx)

	/* set up to pass the drive where stage2 is located in */
	movb	EXT_C(current_drive), %dl

	/* set up to pass the second sector of stage2 */
	movl	0xc(%esp), %ecx

	call	EXT_C(prot_to_real)

	.code16

	//sti		/* it is not bad keeping interrupt off */
	sti		/* for hardware interrupt or watchdog */

	movl	%ecx, %ebp

#ifdef ABSOLUTE_WITHOUT_ASTERISK
	DATA32	ADDR32	ljmp	(offset)
#else
	DATA32	ADDR32	ljmp	*(offset)
#endif

	.code32
#endif /* STAGE1_5 */

/*
 *  These next two routines, "real_to_prot" and "prot_to_real" are structured
 *  in a very specific way.  Be very careful when changing them.
 *
 *  NOTE:  Use of either one messes up %eax and %ebp.
 */

ENTRY(real_to_prot)
	.code16

	cli

	/* load the GDT register */
	xorw	%ax, %ax
	movw	%ax, %ds
	DATA32	ADDR32	lgdt	gdtdesc

	/* turn on protected mode */
	movl	%cr0, %eax
	andl	$0x0000FFFF, %eax
	orl	$CR0_PE_ON, %eax
	movl	%eax, %cr0

	/* jump to relocation, flush prefetch queue, and reload %cs */
	DATA32	ljmp	$PROT_MODE_CSEG, $protcseg

	/*
	 *  The ".code32" directive only works in GAS, the GNU assembler!
	 *  This gets out of "16-bit" mode.
	 */
	.code32

protcseg:
	/* reload other segment registers */
	movw	$PROT_MODE_DSEG, %ax
	movw	%ax, %ds
	movw	%ax, %es
	movw	%ax, %fs
	movw	%ax, %gs
	movw	%ax, %ss

#;	/* put the return address in a known safe location */
#;	movl	(%esp), %eax
#;	movl	%eax, STACKOFF

#;	/* get protected mode stack */
#;	movl	protstack, %eax
#;	movl	%eax, %esp
#;	movl	%eax, %ebp

#;	/* get return address onto the right stack */
#;	movl	STACKOFF, %eax
#;	movl	%eax, (%esp)

	/* zero %eax */
	xorl	%eax, %eax

	/* return on the old (or initialized) stack! */
	ret


ENTRY(prot_to_real)
	/* just in case, set GDT */
	lgdt	gdtdesc

#;	/* save the protected mode stack */
#;	movl	%esp, %eax
#;	movl	%eax, protstack

#;	/* get the return address */
#;	movl	(%esp), %eax
#;	movl	%eax, STACKOFF

#;	/* set up new stack */
#;	movl	$STACKOFF, %eax
#;	movl	%eax, %esp
#;	movl	%eax, %ebp

	/* set up segment limits */
	movw	$PSEUDO_RM_DSEG, %ax
	movw	%ax, %ds
	movw	%ax, %es
	movw	%ax, %fs
	movw	%ax, %gs
	movw	%ax, %ss

	/* this might be an extra step */
	ljmp	$PSEUDO_RM_CSEG, $tmpcseg	/* jump to a 16 bit segment */

tmpcseg:
	.code16

	/* clear the PE bit of CR0 */
	movl	%cr0, %eax
	//andl 	$CR0_PE_OFF, %eax
	andl 	$0x0000FFFE, %eax
	movl	%eax, %cr0

	/* flush prefetch queue, reload %cs */
	DATA32	ljmp	$0, $realcseg

realcseg:
	/* we are in real mode now
	 * set up the real mode segment registers : DS, SS, ES
	 */
	/* zero %eax */
	xorl	%eax, %eax

	movw	%ax, %ds
	movw	%ax, %es
	movw	%ax, %fs
	movw	%ax, %gs
	movw	%ax, %ss

	/* restore interrupts */
	/* oh, don't enable interrupt when we are controlling gateA20 */
	//sti

	/* return on new stack! */
	DATA32	ret

	.code32


/*
 *   int biosdisk_int13_extensions (int ax, int drive, void *dap)
 *
 *   Call IBM/MS INT13 Extensions (int 13 %ax=AX) for DRIVE. DAP
 *   is passed for disk address packet. If an error occurs, return
 *   non-zero, otherwise zero.
 */

ENTRY(biosdisk_int13_extensions)
	pushl	%ebp
	movl	%esp, %ebp

	#; +16	dap
	#; +12	drive
	#;  +8	 ax
	#;  +4	EIP
	#; ebp	EBP
	#;  -4	ESI
	#;  -8	EBX

	pushl	%esi
	pushl	%ebx
	pushl	%ecx
	pushl	%edx

	/* compute the address of disk_address_packet */
	movl	0x10(%ebp), %eax	#; linear address of dap

	/* if DS can be 0x40, we can avoid AWARD BIOS bug of int13/AX=4B01 */
	subl	$0x400, %eax

	shll	$1, %eax
	movw	%ax, %si
	shrw	$1, %si			#; low 15-bit for offset

	xorw	%ax, %ax
	shrl	$5, %eax		#; segment value in AX
	addw	$0x40, %ax
	movw	%ax, %cx	/* save the segment to cx */

	/* drive */
	movb	0xc(%ebp), %dl
	/* ax */
	movw	0x8(%ebp), %bx
	/* enter real mode */
	call	EXT_C(prot_to_real)

	.code16

	sti	#; cli should also work for biosdisk_int13_extensions

	movw	%bx, %ax
	movw	%cx, %ds

	/* set additional registers to serve buggy BIOSes. */
	pushw	%di
	pushw	%bx
	movw	%cx, %es
	movw	%si, %di
	movw	%si, %bx

#if defined(STAGE1_5) || 1
	int	$0x13
#else
	call	safe_int13
#endif
	popw	%bx
	popw	%di

#if 1
	/* some buggy USB BIOSes fail to clear AH on success */
	setc	%dl
#else
	movb	$1, %dl		/* set error */
	jc	1f
	movb	%ah, %dl	/* save return value */
1:
#endif

	/* clear the data segment */
	xorw	%ax, %ax
	movw	%ax, %ds
	movw	%ax, %es
#ifndef STAGE1_5

	/* if it is not read/write operation, we can skip the A20 code. */

	andb	$0xFE, %bh
	cmpb	$0x42, %bh
	jne	1f

	/* ensure A20 is on when we come back to protected mode. */

	pushal

	movw	$0x00ff, %cx		# try so many times on failure
	movw	$0x0001, %dx		# DL=enable A20, DH=debug off

	cli	/* yes, keep interrupt off when controlling A20 */
	call	enable_disable_a20
	sti

	//sete	%dl			# DL=1 means success

	popal
1:
#endif /* ! STAGE1_5 */
	/* back to protected mode */
	DATA32	call	EXT_C(real_to_prot)
	.code32

	movzbl	%dl, %eax	/* return value in %eax */

	popl	%edx
	popl	%ecx
	popl	%ebx
	popl	%esi
	popl	%ebp

	ret

/*
 *   int biosdisk_standard (int ah, int drive, int coff, int hoff, int soff,
 *                          int nsec, int segment)
 *
 *   Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write
 *   NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs,
 *   return non-zero, otherwise zero.
 */

ENTRY(biosdisk_standard)
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%ebx
	pushl	%edi
	pushl	%esi

	/* set up CHS information */
	movl	0x10(%ebp), %eax
	movb	%al, %ch
	movb	0x18(%ebp), %al
	shlb	$2, %al
	shrw	$2, %ax
	movb	%al, %cl
	movb	0x14(%ebp), %dh
	/* drive */
	movb	0xc(%ebp), %dl
	/* segment */
	movw	0x20(%ebp), %bx
	/* save nsec and ah to %di */
	movb	0x8(%ebp), %ah
	movb	0x1c(%ebp), %al
	movw	%ax, %di
	/* enter real mode */
	call	EXT_C(prot_to_real)

	.code16

	//sti	#; biosdisk_standard won't require sti
	sti	#; added 2006-11-30

	movw	%bx, %es
	movw	$5, %si		/* attempt at least 5 times */

1:
	pushw	%si
	pushw	%di
	pushw	%cx
	pushw	%dx
	xorw	%bx, %bx
	movw	%di, %ax
#if defined(STAGE1_5) || 1
	int	$0x13
#else
	call	safe_int13
#endif
	popw	%dx
	popw	%cx
	popw	%di
	popw	%si

#if 1
	/* some buggy USB BIOSes fail to clear AH on success */
	setc	%bl
	jnc	2f
#else
	movb	%ah, %bl	/* save return value */
	jc	3f		/* check if successful */

	testb	%ah, %ah
	jz	2f
3:
#endif

	movw	%di, %ax	/* get function number */
	cmpb	$0x04, %ah	/* verify sectors? */
	je	4f		/* yes, do not retry */
	/* if fail, reset the disk system */
	pushw	%si
	pushw	%di
	pushw	%cx
	pushw	%dx
	xorw	%ax, %ax
#if defined(STAGE1_5) || 1
	int	$0x13
#else
	call	safe_int13
#endif
	popw	%dx
	popw	%cx
	popw	%di
	popw	%si

	decw	%si
	jnz	1b		/* retry */
4:
	movb	$1, %bl		/* set error */
2:
#ifndef STAGE1_5
	/* ensure A20 is on when we come back to protected mode. */

	pushal

	movw	$0x00ff, %cx		# try so many times on failure
	movw	$0x0001, %dx		# DL=enable A20, DH=debug off

	cli	/* yes, keep interrupt off when controlling A20 */
	call	enable_disable_a20
	sti

	//sete	%dl			# DL=1 means success

	popal
#endif /* ! STAGE1_5 */
	/* back to protected mode */
	DATA32	call	EXT_C(real_to_prot)
	.code32

	movzbl	%bl, %eax	/* return value in %eax */

	popl	%esi
	popl	%edi
	popl	%ebx
	popl	%ebp

	ret


/*
 *   int check_int13_extensions (int drive)
 *
 *   Check if LBA is supported for DRIVE. If it is supported, then return
 *   the major version and API support bits of extensions, otherwise zero.
 */

ENTRY(check_int13_extensions)
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%ebx

	/* drive */
	movb	0x8(%ebp), %dl
	/* enter real mode */
	call	EXT_C(prot_to_real)

	.code16

	//sti	#; check_int13_extensions won't require sti
	sti	#; added 2006-11-30

	pushw	%cx
	pushw	%dx

	movb	$0x41, %ah
	movw	$0x55aa, %bx
#if defined(STAGE1_5) || 1
	int	$0x13
#else
	call	safe_int13
#endif

	/* check the result */
	jc	1f
	cmpw	$0xaa55, %bx
	jne	1f

	movb	%ah, %bl	/* save the major version into %bl */

	/* check if AH=0x42 is supported if FORCE_LBA is zero */
	movb	EXT_C(force_lba), %al
	testb	%al, %al
#if 0
	jnz	2f
	andw	$1, %cx
	jnz	2f
#else
	setnz	%al
	orb	%al, %cl
	jmp	2f
#endif

1:
	xorw	%bx, %bx
	xorw	%cx, %cx
2:
	roll	$16, %ebx	#; version number in high word
	movw	%cx, %bx	#; API subset support bitmap in low word

	popw	%dx
	popw	%cx

	/* back to protected mode */
	DATA32	call	EXT_C(real_to_prot)
	.code32

	xchgl	%eax, %ebx	/* return value in %eax */

	popl	%ebx
	popl	%ebp

	ret

	.code32

/*
 *   int get_diskinfo_standard (int drive, unsigned long *cylinders,
 *                              unsigned long *heads, unsigned long *sectors)
 *
 *   if bit 8-15 of drive(dh) != 0 on call, then geometry_tune will be used.
 *
 *   Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
 *   error occurs, then return non-zero, otherwise zero.
 */

ENTRY(get_diskinfo_standard)
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%ebx
	pushl	%edi

	/* Heads */
	movl	0x10(%ebp), %edi
	movl	(%edi), %edi
	movl	%edi, ABS(Heads_passed_in)

	/* Sectors */
	movl	0x14(%ebp), %edi
	movl	(%edi), %edi
	movl	%edi, ABS(Sectors_passed_in)

	/* drive */
	movl	0x8(%ebp), %edx

	/* enter real mode */
	call	EXT_C(prot_to_real)

	.code16

	sti

	testb	%dh, %dh
	jz	2f

	call	geometry_tune

	movb	$1, %bh		/* geometry_tune indicator */
	jmp	1f
2:
	xorw	%cx, %cx
	movb	$0x8, %ah
#if defined(STAGE1_5) || 1
	int	$0x13
#else
	call	safe_int13
#endif
	movb	$0, %bh		/* geometry_tune indicator */
	jc	2f

	/* check if successful */
	testb	%ah, %ah
	jnz	1f		/* Error number in AH */

	/* bogus BIOSes may not return an error number */
	testb	$0x3f, %cl	/* 0 sectors means no disk */
	jnz	1f		/* if non-zero, then succeed */
2:
	/* failure */
	/* XXX 0x60 is one of the unused error numbers */
	movb	$0x60, %ah
1:
	movb	%ah, %bl	/* save return value in %bl */
	/* back to protected mode */
	DATA32	call	EXT_C(real_to_prot)

	.code32

	testb	%bl, %bl	/* check failure */
	jnz	1f		/* failure */

	/* restore %ebp */
	leal	0x8(%esp), %ebp

	/* heads */
	xorl	%eax, %eax
	movb	%dh, %al
	incl	%eax		/* number of heads is counted from zero */
	movl	0x10(%ebp), %edi
	//movl	%eax, (%edi)
	stosl

	/* sectors */
	xorl	%eax, %eax
	movb	%cl, %al
	andb	$0x3f, %al
	movl	0x14(%ebp), %edi
	//movl	%eax, (%edi)
	stosl

	/* cylinders */

	testb	%bh, %bh
	jnz	1f		/* geometry_tune won't touch cylinders */

	xorl	%eax, %eax
	shrb	$6, %cl
	movb	%cl, %ah
	movb	%ch, %al
	incl	%eax		/* number of cylinders is counted from zero */
	movl	0x0C(%ebp), %edi
	//movl	%eax, (%edi)
	stosl
1:
	movzbl	%bl, %eax	/* return value in %eax */

	popl	%edi
	popl	%ebx
	popl	%ebp

	ret

	.code16

geometry_tune:

	/* on call:
	 *		CS	0
	 *
	 * on return:
	 *		AH=0	success, otherwise failure
	 *		CL	max sector number
	 *		DH	max head number
	 *		DS	changed
	 *		ES	changed
	 *		AX	changed
	 *		BX	changed
	 *		CX	changed
	 */

//////////////////////////////////////////////////////////////////////////////

	/* find Max sector by reading each sector on the first track. */

#if 0
	pushal
	pushw	%ds
	pushw	%es
	movw	$0x0e41, %ax
	xorw	%bx, %bx
	int	$0x10
	popw	%es
	popw	%ds
	popal
#endif

	/* try the passed-in value first */
	movw	%cs:ABS(Sectors_passed_in), %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	$0x57E0, %ax	/* Don't use SCRATCHSEG */
	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
#if defined(STAGE1_5) || 1
	int	$0x13
#else
	call	safe_int13
#endif
	popaw
	ret

2:
	/* CX=Max Sector */
	movw	%cx, %cs:ABS(Smax_tuned)

#if 0
	pushal
	pushw	%ds
	pushw	%es
	movw	$0x0e20, %ax
	addb	%cl, %al
	xorw	%bx, %bx
	int	$0x10
	popw	%es
	popw	%ds
	popal
#endif

//////////////////////////////////////////////////////////////////////////////
#if 0
	/* check if we can read sectors across the track boundary */

	/* first, read a track plus one sector */

	movb	$0, %cs:ABS(cross_track)

	movw	$1, %cx		/* sector 1, cylinder 0 */
	movb	$0, %dh		/* head 0 */
	movb	%cs:ABS(Smax_tuned), %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
#if defined(STAGE1_5) || 1
	int	$0x13
#else
	call	safe_int13
#endif
	popaw
	jc	1f		/* cross-track read is not supported */

	/* read again normally, only the track */

	movb	%cs:ABS(Smax_tuned), %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
#if defined(STAGE1_5) || 1
	int	$0x13
#else
	call	safe_int13
#endif
	popaw
	jc	2b		/* failure */

	/* compare the two tracks */

	pushw	%cx
	pushw	%si
	pushw	%di
	movw	%cs:ABS(Smax_tuned), %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:ABS(cross_track)
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	$0x57E0, %ax	/* Don't use SCRATCHSEG */
	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
#if defined(STAGE1_5) || 1
	int	$0x13
#else
	call	safe_int13
#endif
	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:ABS(Hmax_tuned)
#endif
//////////////////////////////////////////////////////////////////////////////

	/* tune Hmax */

	/* First, try the passed-in value */
	movb	%cs:ABS(Heads_passed_in), %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:ABS(Hmax_tuned), %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:ABS(Hmax_tuned)
5:
	/* Hmax is tuned ok. */

	cmpb	$0xFF, %dh
	jne	1f
	/* consider Hmax=0xFF as a failure! Use the passed-in value. */
	movb	%cs:ABS(Heads_passed_in), %dh
	testb	%dh, %dh
	jnz	4f
	decb	%dh
4:
	decb	%dh
	movb	%dh, %cs:ABS(Hmax_tuned)
1:

#if 0
	pushal
	pushw	%ds
	pushw	%es
	movw	$0x0e21, %ax
	xorw	%bx, %bx
	int	$0x10
	popw	%es
	popw	%ds
	popal
#endif

//////////////////////////////////////////////////////////////////////////////

	/* tune Smax */

	/* First, try the passed-in value */
	movb	%cs:ABS(Sectors_passed_in), %cl
//	cmpb	$0, %cs:ABS(cross_track)
//	jnz	4f
//	cmpb	%cs:ABS(Smax_tuned), %cl
//	jnb	1f
//	cmpb	$8, %cl
//	jb	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:ABS(Smax_tuned), %cl
	jb	1b

4:
	/* got Smax=CL */

	movb	%cl, %cs:ABS(Smax_tuned)

	/* Smax is tuned ok. */

#if 0
	pushal
	pushw	%ds
	pushw	%es
	movw	$0x0e21, %ax
	xorw	%bx, %bx
	int	$0x10
	popw	%es
	popw	%ds
	popal
#endif

//////////////////////////////////////////////////////////////////////////////

	movw	%cs:ABS(Smax_tuned), %cx
	movb	%cs:ABS(Hmax_tuned), %dh
	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 */

#if 0
	pushal
	pushw	%ds
	pushw	%es
	movw	$0x0e22, %ax
	xorw	%bx, %bx
	int	$0x10
	popw	%es
	popw	%ds
	popal
#endif

	movb	$1, %cl		/* sector 1=the leading sector */
	movb	$2, %ah		/* READ */
	movb	%cs:ABS(Smax_tuned), %al	/* sectors to read */
	movw	$0x5000, %bx
	movw	%bx, %es
	movw	%bx, %ds
	xorw	%bx, %bx
	pushaw
	movw	%bx, %si
	movw	%bx, %di
#if defined(STAGE1_5) || 1
	int	$0x13
#else
	call	safe_int13
#endif
	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:ABS(Smax_tuned), %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
#if defined(STAGE1_5) || 1
	int	$0x13
#else
	call	safe_int13
#endif
	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:ABS(Smax_tuned), %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
#if defined(STAGE1_5) || 1
	int	$0x13
#else
	call	safe_int13
#endif
	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:ABS(Smax_tuned), %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:ABS(Smax_count)

	movb	$0, %ch		/* cylinder: 0 - 6 */
	movb	$0, %dh		/* head: 0 - Hmax */
6:
	/* read ending sector of this track. */

#if 0
	pushal
	pushw	%ds
	pushw	%es
	movw	$0x0e23, %ax
	xorw	%bx, %bx
	int	$0x10
	popw	%es
	popw	%ds
	popal
#endif

	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
#if defined(STAGE1_5) || 1
	int	$0x13
#else
	call	safe_int13
#endif
	popaw
	//jc	2f		/* failure */
	pushfw			/* save CF */

	/* read beginning sector of the next track. */

	cmpb	%cs:ABS(Hmax_tuned), %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
#if defined(STAGE1_5) || 1
	int	$0x13
#else
	call	safe_int13
#endif
	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:ABS(Smax_count)
	jz	6f

	//cmpb	%cs:ABS(Hmax_tuned), %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

//////////////////////////////////////////////////////////////////////////////

	.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

//////////////////////////////////////////////////////////////////////////////

	.code32


#ifndef STAGE1_5
/* get_code_end() :  return the address of the end of the code
 * This is here so that it can be replaced by asmstub.c.
 */
ENTRY(get_code_end)
	/* will be the end of the bss */
# if defined(HAVE_END_SYMBOL)
	movl	$end, %eax
# elif defined(HAVE_USCORE_END_SYMBOL)
	movl	$_end, %eax
# endif
	shrl	$2, %eax		/* Round up to the next word. */
	incl	%eax
	shll	$2, %eax
	ret
#endif /* ! STAGE1_5 */

/*
 *
 * get_memsize(i) :  return the memory size in KB. i == 0 for conventional
 *		memory, i == 1 for extended memory
 *	BIOS call "INT 12H" to get conventional memory size
 *	BIOS call "INT 15H, AH=88H" to get extended memory size
 *		Both have the return value in AX.
 *
 */

ENTRY(get_memsize)
	pushl	%ebp
	pushl	%ebx

	movl	0xc(%esp), %ebx

	call	EXT_C(prot_to_real)	/* enter real mode */

	.code16

	//sti		/* it is not bad keeping interrupt off */
	sti		/* for hardware interrupt or watchdog */

	cmpb	$0x1, %bl
	//DATA32	je	xext
	je	1f

	int	$0x12
	//DATA32	jmp	xdone
	jmp	2f

//xext:
1:
	movb	$0x88, %ah
#ifdef STAGE1_5
	int	$0x15
#else
	pushfw
	lcall	*ABS(EXT_C(ROM_int15))
#endif

//xdone:
2:
	movw	%ax, %bx

	DATA32	call	EXT_C(real_to_prot)
	.code32

	movw	%bx, %ax
	popl	%ebx
	popl	%ebp
	ret


#ifndef STAGE1_5

/*
 *
 * get_eisamemsize() :  return packed EISA memory map, lower 16 bits is
 *		memory between 1M and 16M in 1K parts, upper 16 bits is
 *		memory above 16M in 64K parts.  If error, return -1.
 *	BIOS call "INT 15H, AH=E801H" to get EISA memory map,
 *		AX = memory between 1M and 16M in 1K parts.
 *		BX = memory above 16M in 64K parts.
 *
 */

ENTRY(get_eisamemsize)
	pushl	%ebp
	pushl	%ebx
	pushl	%ecx
	pushl	%edx

	call	EXT_C(prot_to_real)	/* enter real mode */

	.code16

	//sti		/* it is not bad keeping interrupt off */
	sti		/* for hardware interrupt or watchdog */

	movl	$0xe801, %eax
	//int	$0x15
2:
	pushl	%eax
	pushfw
	lcall	*ABS(EXT_C(ROM_int15))
	jc	1f
	testb	$0x80, %ah
	jnz	1f

	shll	$16, %ebx
	movw	%ax, %bx
	testl	%ebx, %ebx
	jnz	3f
	movw	%cx, %ax
	movw	%dx, %bx
	shll	$16, %ebx
	movw	%ax, %bx
3:
	popl	%eax
	jmp	2f
1:
	popl	%eax
	cmpw	$0xE881, %ax
	movw	$0xE881, %ax
	jne	2b
	//movl	$0xFFFFFFFF, %ebx
	/* call the old function 88h */
	movw	$0x8800, %ax
	pushfw
	lcall	*ABS(EXT_C(ROM_int15))
	movzwl	%ax, %ebx
2:
	xorw	%ax, %ax
	movw	%ax, %ds
	movw	%ax, %es

	DATA32	call	EXT_C(real_to_prot)
	.code32

	movl	%ebx, %eax

	popl	%edx
	popl	%ecx
	popl	%ebx
	popl	%ebp
	ret

/*
 *
 * get_mmap_entry(addr, cont) :  address and old continuation value (zero to
 *		start), for the Query System Address Map BIOS call.
 *
 *  Sets the first 4-byte int value of "addr" to the size returned by
 *  the call.  If the call fails, sets it to zero.
 *
 *	Returns:  new (non-zero) continuation value, 0 if done.
 *
 * NOTE: Currently hard-coded for a maximum buffer length of 1024.
 */

ENTRY(get_mmap_entry)
	pushl	%ebp
	pushl	%ebx
	pushl	%edi
	pushl	%esi

	/* place address (+4) in ES:DI */
	movl	0x14(%esp), %eax
	addl	$4, %eax
	movl	%eax, %edi
	andl	$0xf, %edi
	shrl	$4, %eax
	movl	%eax, %esi

	/* set continuation value */
	movl	0x18(%esp), %ebx

	pushl	%ecx		/* save ECX */

	/* set default maximum buffer size */
	movl	$0x14, %ecx

	/* set EDX to 'SMAP' */
	movl	$0x534d4150, %edx

	call	EXT_C(prot_to_real)	/* enter real mode */

	.code16

	//sti		/* it is not bad keeping interrupt off */
	sti		/* for hardware interrupt or watchdog */

	movw	%si, %es

	/* set additional registers to serve buggy BIOSes. */
	movw	%si, %ds
	movw	%di, %si

	movl	$0xe820, %eax
	//int	$0x15
	pushfw
	lcall	%cs:*ABS(EXT_C(ROM_int15))

	//DATA32	jc	xnosmap
	jnc	1f
	movl	$0, %ebx	/* set end indicator */
1:
	cmpl	$0x534d4150, %eax
	//DATA32	jne	xnosmap
	jne	1f

	/* 20-byte length is currently standard. So others are considered
	 * invalid.
	 */

	cmpl	$0x14, %ecx
	//DATA32	jb	xnosmap
	//
	//cmpl	$0x400, %ecx
	//DATA32	jg	xnosmap
	//DATA32	jmp	xsmap
	je	2f

//xnosmap:
1:
	movl	$0, %ecx
//xsmap:
2:

	xorw	%ax, %ax
	movw	%ax, %ds
	movw	%ax, %es

	DATA32	call	EXT_C(real_to_prot)
	.code32

	popl	%eax		/* OLD ECX */

	/* write length of buffer (zero if error) into "addr" */
	movl	0x14(%esp), %edi
	xchgl	%eax, %ecx
	stosl

	/* set return value to continuation */
	movl	%ebx, %eax

	popl	%esi
	popl	%edi
	popl	%ebx
	popl	%ebp
	ret

/*
 * get_rom_config_table()
 *
 * Get the linear address of a ROM configuration table. Return zero,
 * if fails.
 */

ENTRY(get_rom_config_table)
	pushl	%ebp
	pushl	%ebx
	pushl	%edx

	/* zero %ebx for simplicity */
	xorl	%ebx, %ebx

	call	EXT_C(prot_to_real)

	.code16

	//sti		/* it is not bad keeping interrupt off */
	sti		/* for hardware interrupt or watchdog */

	movb	$0xc0, %ah
	int	$0x15

	jc	no_rom_table
	testb	%ah, %ah
	jnz	no_rom_table

	movw	%es, %dx
	jmp	found_rom_table

no_rom_table:
	xorw	%dx, %dx
	xorw	%bx, %bx

found_rom_table:

	xorw	%ax, %ax
	movw	%ax, %ds
	movw	%ax, %es

	DATA32	call	EXT_C(real_to_prot)
	.code32

	/* compute the linear address */
	xorl	%eax, %eax
	movw	%dx, %ax
	shll	$4, %eax
	addl	%ebx, %eax

	popl	%edx
	popl	%ebx
	popl	%ebp
	ret


/*
 * int get_vbe_controller_info (struct vbe_controller *controller_ptr)
 *
 * Get VBE controller information.
 */

ENTRY(get_vbe_controller_info)
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%edi
	pushl	%ebx
	pushl	%esi

	/* Convert the linear address to segment:offset */
	movl	8(%ebp), %eax
	movl	%eax, %edi
	andl	$0x0000000f, %edi
	shrl	$4, %eax
	movl	%eax, %ebx

	call	EXT_C(prot_to_real)

	.code16

	//sti		/* it is not bad keeping interrupt off */
	sti		/* for hardware interrupt or watchdog */

	movw	%bx, %es

	/* set additional registers to serve buggy BIOSes. */
	movw	%bx, %ds
	movw	%di, %si

	movw	$0x4F00, %ax
	int	$0x10

	movw	%ax, %bx

	xorw	%ax, %ax
	movw	%ax, %ds
	movw	%ax, %es

	DATA32	call	EXT_C(real_to_prot)
	.code32

	movzwl	%bx, %eax

	popl	%esi
	popl	%ebx
	popl	%edi
	popl	%ebp
	ret


/*
 * int get_vbe_mode_info (int mode_number, struct vbe_mode *mode_ptr)
 *
 * Get VBE mode information.
 */

ENTRY(get_vbe_mode_info)
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%edi
	pushl	%ebx

	/* Convert the linear address to segment:offset */
	movl	0xc(%ebp), %eax
	movl	%eax, %edi
	andl	$0x0000000f, %edi
	shrl	$4, %eax
	movl	%eax, %ebx

	/* Save the mode number in %cx */
	movl	0x8(%ebp), %ecx

	call	EXT_C(prot_to_real)

	.code16

	//sti		/* it is not bad keeping interrupt off */
	sti		/* for hardware interrupt or watchdog */

	movw	%bx, %es
	movw	$0x4F01, %ax
	int	$0x10

	movw	%ax, %bx
	DATA32	call	EXT_C(real_to_prot)
	.code32

	movzwl	%bx, %eax

	popl	%ebx
	popl	%edi
	popl	%ebp
	ret


/*
 * int set_vbe_mode (int mode_number)
 *
 * Set VBE mode. Don't support user-specified CRTC information.
 */

ENTRY(set_vbe_mode)
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%ebx

	/* Save the mode number in %bx */
	movl	0x8(%ebp), %ebx
	/* Clear bit D11 */
	andl	$0xF7FF, %ebx

	call	EXT_C(prot_to_real)

	.code16

	//sti		/* it is not bad keeping interrupt off */
	sti		/* for hardware interrupt or watchdog */

	movw	$0x4F02, %ax
	int	$0x10

	movw	%ax, %bx
	DATA32	call	EXT_C(real_to_prot)
	.code32

	movzwl	%bx, %eax

	popl	%ebx
	popl	%ebp
	ret


#ifdef SUPPORT_GFX

/*
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 * graphical menu functions
 *
 */

/*
 * int gfx_init_v1 (gfx_data_v1_t *gfx_data)
 *
 * init gfx things
 *
 * return vales:
 *   0: ok
 *   1: failed
 *   sets gfx_data->ok
 */

ENTRY(gfx_init_v1)
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%edi
	pushl	%esi
	pushl	%ebx

	movl	8(%ebp),%edx
	movl	%edx,%edi
	andl	$0xf,%edi
	shrl	$4,%edx

	pushl	%ebp

	call	EXT_C(prot_to_real)
	.code16

	pushw	%ds

	movw	%dx,%ds
	leal	gfx_ofs_v1_sys_cfg(%di),%esi
	movl	gfx_ofs_v1_mem_file(%di),%eax
	movl	gfx_ofs_v1_mem_cur(%di),%ebx
	movl	gfx_ofs_v1_mem_max(%di),%ecx
	movw	%ds,%dx

	/* basically just a lcall, but we need %edi */
	pushw	%cs
	pushw	$gfx_init_50_v1
	pushl	gfx_ofs_v1_jmp_table + 4 * 0 (%di)

	movl	gfx_ofs_v1_mem_align(%di),%edi

	lret

gfx_init_50_v1:
	movl	$0,%ebx
	adcl	$0,%ebx

	popw	%ds

	DATA32	call	EXT_C(real_to_prot)
	.code32

	popl	%ebp

	movl	%ebx,%eax
	negl	%ebx
	incl	%ebx
	movl	8(%ebp),%edx
	movl	%ebx,gfx_ofs_v1_ok(%edx)

	popl	%ebx
	popl	%esi
	popl	%edi

	popl	%ebp
	ret


/*
 * int gfx_done_v1 (gfx_data_v1_t *gfx_data)
 *
 * shut down gfx things
 *
 * return vales:
 *   always 0
 *   sets gfx_data->ok
 */

ENTRY(gfx_done_v1)
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%edi
	pushl	%esi
	pushl	%ebx

	movl	8(%ebp),%edx
	movl	%edx,%ebx
	andl	$0xf,%ebx
	shrl	$4,%edx

	pushl	%ebp

	call	EXT_C(prot_to_real)
	.code16

	pushw	%ds

	movw	%dx,%ds

	lcall	*gfx_ofs_v1_jmp_table + 4 * 1 (%bx)

	popw	%ds

	DATA32	call	EXT_C(real_to_prot)
	.code32

	popl	%ebp

	xorl	%eax,%eax
	movl	8(%ebp),%edx
	movl	%eax,gfx_ofs_v1_ok(%edx)

	popl	%ebx
	popl	%esi
	popl	%edi

	popl	%ebp
	ret


/*
 * int gfx_input_v1 (gfx_data_v1_t *gfx_data, int *menu_entry)
 *
 * let user enter a command line
 *
 * uses gfx_data->cmdline as buffer
 *
 * return values:
 *   1: abort
 *   2: boot
 *   menu_entry: selected entry
 */

ENTRY(gfx_input_v1)
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%edi
	pushl	%esi
	pushl	%ebx

	movl	8(%ebp),%edx
	movl	%edx,%ebx
	andl	$0xf,%ebx
	shrl	$4,%edx

	pushl	%ebp

	call	EXT_C(prot_to_real)
	.code16

	pushw	%ds

	movw	%dx,%ds
	shll	$4,%edx
	movl	gfx_ofs_v1_cmdline(%bx),%edi
	subl	%edx,%edi
	movw	gfx_ofs_v1_cmdline_len(%bx),%cx
	movw	gfx_ofs_v1_timeout(%bx),%ax
	imulw	$18,%ax

	pushl	%ebp
	lcall	*gfx_ofs_v1_jmp_table + 4 * 2 (%bx)
	popl	%ebp
	movl	%eax,%ecx

	popw	%ds

	DATA32	call	EXT_C(real_to_prot)
	.code32

	popl	%ebp

	movl	12(%ebp),%edx
	movl	%ebx,(%edx)

	movl	%ecx,%eax

	popl	%ebx
	popl	%esi
	popl	%edi

	popl	%ebp
	ret


/*
 * int gfx_setup_menu_v1 (gfx_data_v1_t *gfx_data)
 *
 * draw boot menu
 *
 * return values:
 *   always 0
 */

/* menu entry descriptor */
#define menu_v1_entries		0
#define menu_v1_default		2	/* seg:ofs */
#define menu_v1_ent_list	6	/* seg:ofs */
#define menu_v1_ent_size	10
#define menu_v1_arg_list	12	/* seg:ofs */
#define menu_v1_arg_size	16
#define sizeof_menu_v1_desc	18

ENTRY(gfx_setup_menu_v1)
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%edi
	pushl	%esi
	pushl	%ebx

	movl	8(%ebp),%edx
	movl	%edx,%ebx
	andl	$0xf,%ebx
	shrl	$4,%edx

	call	EXT_C(prot_to_real)
	.code16

	pushw	%ds

	movw	%dx,%ds
	shll	$4,%edx

	subw	$sizeof_menu_v1_desc,%sp
	movw	%sp,%bp

	movl	gfx_ofs_v1_menu_entries(%bx),%eax
	movw	%ax,menu_v1_entries(%bp)

	movl	gfx_ofs_v1_menu_default_entry(%bx),%eax
	subl	%edx,%eax
	movw	%ax,menu_v1_default(%bp)
	movw	%ds,menu_v1_default+2(%bp)

	movl	gfx_ofs_v1_menu_list(%bx),%eax
	subl	%edx,%eax
	movw	%ax,menu_v1_ent_list(%bp)
	movw	%ds,menu_v1_ent_list+2(%bp)

	movl	gfx_ofs_v1_menu_entry_len(%bx),%eax
	movw	%ax,menu_v1_ent_size(%bp)

	movl	gfx_ofs_v1_args_list(%bx),%eax
	subl	%edx,%eax
	movw	%ax,menu_v1_arg_list(%bp)
	movw	%ds,menu_v1_arg_list+2(%bp)

	movl	gfx_ofs_v1_args_entry_len(%bx),%eax
	movw	%ax,menu_v1_arg_size(%bp)

	movw	%bp,%si
	pushw	%ss
	popw	%es

	lcall	%ds: *gfx_ofs_v1_jmp_table + 4 * 3 (%bx)

	addw	$sizeof_menu_v1_desc,%sp

	popw	%ds

	DATA32	call	EXT_C(real_to_prot)
	.code32

	xorl	%eax,%eax

	popl	%ebx
	popl	%esi
	popl	%edi

	popl	%ebp
	ret


/*
 * int gfx_init_v2 (gfx_data_v2_t *gfx_data)
 *
 * init gfx things
 *
 * return vales:
 *   0: ok
 *   1: failed
 *   sets gfx_data->ok
 */

ENTRY(gfx_init_v2)
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%edi
	pushl	%esi
	pushl	%ebx

	movl	8(%ebp),%edx
	movl	%edx,%edi
	leal	gfx_ofs_v2_sys_cfg(%edx),%esi
	andl	$0xf,%edi
	shrl	$4,%edx

	pushl	%ebp

	call	EXT_C(prot_to_real)
	.code16

	pushw	%ds
	movw	%dx,%ds

	lcall	*gfx_ofs_v2_jmp_table + 4 * 0 (%di)

	sbbl	%ebx,%ebx
	negl	%ebx

	popw	%ds

	DATA32	call	EXT_C(real_to_prot)
	.code32

	popl	%ebp

	movl	%ebx,%eax
	xorl	$1,%ebx
	movl	8(%ebp),%edx
	movl	%ebx,gfx_ofs_v2_ok(%edx)

	popl	%ebx
	popl	%esi
	popl	%edi

	popl	%ebp
	ret


/*
 * int gfx_done_v2 (gfx_data_v2_t *gfx_data)
 *
 * shut down gfx things
 *
 * return vales:
 *   always 0
 *   sets gfx_data->ok
 */

ENTRY(gfx_done_v2)
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%edi
	pushl	%esi
	pushl	%ebx

	movl	8(%ebp),%edx
	movl	%edx,%ebx
	andl	$0xf,%ebx
	shrl	$4,%edx

	pushl	%ebp

	call	EXT_C(prot_to_real)
	.code16

	pushw	%ds

	movw	%dx,%ds

	lcall	*gfx_ofs_v2_jmp_table + 4 * 1 (%bx)

	popw	%ds

	DATA32	call	EXT_C(real_to_prot)
	.code32

	popl	%ebp

	xorl	%eax,%eax
	movl	8(%ebp),%edx
	movl	%eax,gfx_ofs_v2_ok(%edx)

	popl	%ebx
	popl	%esi
	popl	%edi

	popl	%ebp
	ret


/*
 * int gfx_input_v2 (gfx_data_v2_t *gfx_data, int *menu_entry)
 *
 * let user enter a command line
 *
 * uses gfx_data->cmdline as buffer
 *
 * return values:
 *   1: abort
 *   2: boot
 *   menu_entry: selected entry
 */

ENTRY(gfx_input_v2)
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%edi
	pushl	%esi
	pushl	%ebx

	movl	8(%ebp),%edx
	movl	%edx,%ebx
	leal	gfx_ofs_v2_sys_cfg(%edx),%esi
	andl	$0xf,%ebx
	shrl	$4,%edx

	pushl	%ebp

	call	EXT_C(prot_to_real)
	.code16

	pushw	%ds

	movw	%dx,%ds

	movl	gfx_ofs_v2_cmdline(%bx),%edi
	movl	gfx_ofs_v2_cmdline_len(%bx),%ecx
	movl	gfx_ofs_v2_timeout(%bx),%eax
	imull	$18,%eax

	lcall	*gfx_ofs_v2_jmp_table + 4 * 2 (%bx)

	movl	%eax,%ecx

	popw	%ds

	DATA32	call	EXT_C(real_to_prot)
	.code32

	popl	%ebp

	movl	12(%ebp),%edx
	movl	%ebx,(%edx)

	movl	%ecx,%eax

	popl	%ebx
	popl	%esi
	popl	%edi

	popl	%ebp
	ret


/*
 * int gfx_setup_menu (gfx_data_t *gfx_data)
 *
 * draw boot menu
 *
 * return values:
 *   always 0
 */

/* menu entry descriptor */
#define menu_v2_entries		0
#define menu_v2_default		2	/* seg:ofs */
#define menu_v2_ent_list		6	/* seg:ofs */
#define menu_v2_ent_size		10
#define menu_v2_arg_list		12	/* seg:ofs */
#define menu_v2_arg_size		16
#define sizeof_menu_v2_desc	18

ENTRY(gfx_setup_menu_v2)
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%edi
	pushl	%esi
	pushl	%ebx

	movl	8(%ebp),%edx
	movl	%edx,%ebx
	andl	$0xf,%ebx
	shrl	$4,%edx

	call	EXT_C(prot_to_real)
	.code16

	pushw	%ds

	movw	%dx,%ds
	shll	$4,%edx

	subw	$sizeof_menu_v2_desc,%sp
	movw	%sp,%bp

	movl	gfx_ofs_v2_menu_entries(%bx),%eax
	movw	%ax,menu_v2_entries(%bp)

	movl	gfx_ofs_v2_menu_default_entry(%bx),%eax
	subl	%edx,%eax
	movw	%ax,menu_v2_default(%bp)
	movw	%ds,menu_v2_default+2(%bp)

	movl	gfx_ofs_v2_menu_list(%bx),%eax
	subl	%edx,%eax
	movw	%ax,menu_v2_ent_list(%bp)
	movw	%ds,menu_v2_ent_list+2(%bp)

	movl	gfx_ofs_v2_menu_entry_len(%bx),%eax
	movw	%ax,menu_v2_ent_size(%bp)

	movl	gfx_ofs_v2_args_list(%bx),%eax
	subl	%edx,%eax
	movw	%ax,menu_v2_arg_list(%bp)
	movw	%ds,menu_v2_arg_list+2(%bp)

	movl	gfx_ofs_v2_args_entry_len(%bx),%eax
	movw	%ax,menu_v2_arg_size(%bp)

	movl	%ss,%esi
	shll	$4,%esi
	addl	%ebp,%esi

	lcall	%ds: *gfx_ofs_v2_jmp_table + 4 * 3 (%bx)

	addw	$sizeof_menu_v2_desc,%sp

	popw	%ds

	DATA32	call	EXT_C(real_to_prot)
	.code32

	xorl	%eax,%eax

	popl	%ebx
	popl	%esi
	popl	%edi

	popl	%ebp
	ret


/*
 *
 * end graphics stuff
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */
#endif /* SUPPORT_GFX */


/*
 * grub2_gate_a20(int on)
 *
 * Gate address-line 20 for high memory.
 *
 * This routine is probably overconservative in what it does, but so what?
 *
 * It also eats any keystrokes in the keyboard buffer.  :-(
 */

ENTRY(grub2_gate_a20)

	.code32

	//movl	%eax, %edx
	movl	4(%esp), %edx		/* the value of `on' */

gate_a20_test_current_state:
	/* first of all, test if already in a good state */
	call	gate_a20_check_state
	cmpb	%al, %dl
	jnz	gate_a20_try_bios
	ret

gate_a20_try_bios:
	/* second, try a BIOS call */
	pushl	%ebp
	call	EXT_C(prot_to_real)

	.code16
	sti		/* for hardware interrupt or watchdog */
	movw	$0x2400, %ax
	testb	%dl, %dl
	jz	1f
	incw	%ax
1:	int	$0x15

	DATA32	call	EXT_C(real_to_prot)
	.code32

	popl	%ebp
	call	gate_a20_check_state
	cmpb	%al, %dl
	jnz	gate_a20_try_keyboard_controller
	ret

gate_a20_flush_keyboard_buffer:
	inb	$0x64
	andb	$0x02, %al
	jnz	gate_a20_flush_keyboard_buffer
2:
	inb	$0x64
	andb	$0x01, %al
	jz	3f
	inb	$0x60
	jmp	2b
3:
	ret

gate_a20_try_keyboard_controller:
	/* third, try the keyboard controller */
	call    gate_a20_flush_keyboard_buffer

	movb	$0xd1, %al
	outb	$0x64
4:
	inb	$0x64
	andb	$0x02, %al
	jnz	4b

	movb	$0xdd, %al
	testb	%dl, %dl
	jz	5f
	orb	$0x02, %al
5:	outb	$0x60
	call    gate_a20_flush_keyboard_buffer

	/* output a dummy command (USB keyboard hack) */
	movb	$0xff, %al
	outb	$0x64
	call    gate_a20_flush_keyboard_buffer

	call	gate_a20_check_state
	cmpb	%al, %dl
	jnz	gate_a20_try_system_control_port_a
	ret

gate_a20_try_system_control_port_a:
	/* fourth, try the system control port A */
	inb	$0x92
	andb	$(~0x03), %al
	testb	%dl, %dl
	jz	6f
	orb	$0x02, %al
6:	outb	$0x92

	/* When turning off Gate A20, do not check the state strictly,
	   because a failure is not fatal usually, and Gate A20 is always
	   on some modern machines.  */
	testb	%dl, %dl
	jz	7f
	call	gate_a20_check_state
	cmpb	%al, %dl
	/* everything failed, so restart from the beginning */
	jnz	gate_a20_try_bios
7:	ret

gate_a20_check_state:
	/* iterate the checking for a while */
	movl	$100, %ecx
1:
	call	3f
	cmpb	%al, %dl
	jz	2f
	loop	1b
2:
	ret
3:
	pushl	%ebx
	pushl	%ecx
	xorl	%eax, %eax
	/* compare the byte at 0x8000 with that at 0x108000 */
	movl	$0x8000/*GRUB_BOOT_MACHINE_KERNEL_ADDR*/, %ebx
	pushl	%ebx				# EBX=0x8000
	/* save the original byte in CL */
	movb	(%ebx), %cl			# CL=byte at 0x8000
	/* store the value at 0x108000 in AL */
	addl	$0x100000, %ebx			# EBX=0x108000
	movb	(%ebx), %al			# AL=byte at 0x108000
	/* try to set one less value at 0x8000 */
	popl	%ebx				# EBX=0x8000
	movb	%al, %ch			# CH=AL
	decb	%ch				# CH=AL-1
	movb	%ch, (%ebx)			# byte at 0x8000=CH
	/* serialize */
	outb	%al, $0x80
	outb	%al, $0x80
	/* obtain the value at 0x108000 in CH */
	pushl	%ebx				# EBX=0x8000
	addl	$0x100000, %ebx			# EBX=0x108000
	movb	(%ebx), %ch			# CH=byte at 0x108000
	/* this result is 1 if A20 is on or 0 if it is off */
	subb	%ch, %al			# AL-=CH
	xorb	$1, %al
	/* restore the original */
	popl	%ebx				# EBX=0x8000
	movb	%cl, (%ebx)			# restore it
	popl	%ecx
	popl	%ebx
	ret

/*
 * int gateA20(int linear)
 *
 * Gate address-line 20 for high memory.
 *
 * This routine is probably overconservative in what it does, but so what?
 *
 * It also eats any keystrokes in the keyboard buffer.  :-(
 *
 *	on call:	linear=0 for a20 off and 1 for on
 *
 *	return value:	0 for failure and 1 for success
 *
 */

ENTRY(gateA20)

	.code32

	pushl	%ebp
	movl	8(%esp), %edx		/* the value of `linear' */

#if 1
	/* first, check if A20 status is already what we desired. */

	/* disable CPU cache for the test to work reliably. */
	movl	%cr0, %eax
	pushl	%eax			/* save old cr0 */
//	andl	$0x00000011, %eax
	orl	$0x60000000, %eax	/* set CD and NW */
	movl	%eax, %cr0
	movl	%cr0, %eax
	testl	$0x60000000, %eax	/* check if we can use wbinvd. */
	jz	1f			/* CPU has no wbinvd instruction. */
	wbinvd
	andl	$0xDFFFFFFF, %eax	/* clear NW */
	movl	%eax, %cr0
1:
	movl	0x00000000, %eax
	pushl	%eax			/* save old int0 vector */

	cmpl	0x00100000, %eax
	jne	1f			/* A20 is on */

	notl	0x00000000
	movl	0x00000000, %eax
	cmpl	0x00100000, %eax
	notl	0x00000000		/* logical `NOT' won't touch flags */
1:
	/* ZF=0(means not equal) for A20 on, ZF=1(means equal) for A20 off. */
	sete	%al			/* save ZF to AL */
	testl	%edx, %edx
	sete	%ah			/* save ZF to AH */
	cmpb	%al, %ah

	/* ZF=1(means equal) for desired and we needn't do anything. */

	popl	%eax			/* restore int0 vector */
	movl	%eax, 0x00000000
	popl	%eax			/* restore cr0 */
	movl	%eax, %cr0
	movl	$1, %eax		/* return value 1 means success */

	jz	1f			/* success */

#endif

	/* failure, then call a real-mode function enable_disable_a20 */

	call	EXT_C(prot_to_real)

	.code16

	sti		/* for hardware interrupt or watchdog */

	movw	$0x00ff, %cx		# try so many times on failure
	movb	$0x01, %dh		# with a20 debug on

	cli	/* yes, keep interrupt off when controlling A20 */
	call	enable_disable_a20
	sti		/* for hardware interrupt or watchdog */

	sete	%dl			# DL=1 means success

#if 0	/* comment out to avoid hanging ONDA C51G board. */
	pushal

#if 0
	# qemu arrow keys will not work if we turn on NumLock in this way.

	/* Turn on Number Lock */
	orb	$0x20, 0x417
#endif

	/* reset mouse */
	movw	$0xC201, %ax
	int	$0x15

	/* disable mouse */
	movw	$0xC200, %ax
	xorw	%bx, %bx	/* BH=0 means disable */
	int	$0x15

	popal
#endif

	DATA32	call	EXT_C(real_to_prot)

	.code32

	movzbl	%dl, %eax

1:
	popl	%ebp
	ret

	.code32

/*
 * linux_boot()
 *
 * Does some funky things (including on the stack!), then jumps to the
 * entry point of the Linux setup code.
 */

VARIABLE(linux_text_len)
	.long	0

VARIABLE(linux_data_tmp_addr)
	.long	0

VARIABLE(linux_data_real_addr)
	.long	0

ENTRY(linux_boot)
	/* don't worry about saving anything, we're committed at this point */
	cld	/* forward copying */

	/* copy kernel */
	movl	EXT_C(linux_text_len), %ecx
	addl	$3, %ecx
	shrl	$2, %ecx
	movl	$LINUX_BZIMAGE_ADDR, %esi	# 0x100000
	movl	$LINUX_ZIMAGE_ADDR, %edi	# 0x10000

	rep movsl

ENTRY(big_linux_boot)
	movl	EXT_C(linux_data_real_addr), %ebx

	/* copy the real mode part */
	movl	EXT_C(linux_data_tmp_addr), %esi
	movl	%ebx, %edi
	movl	$LINUX_SETUP_MOVE_SIZE, %ecx	# 0x9100
	cld
	rep movsb

	/* change %ebx to the segment address */
	shrl	$4, %ebx	#; CS of LINUX SETUP, high word = 0
	movl	%ebx, %eax
	addl	$0x20, %eax	#; one sector
	movw	%ax, 1f		// linux_setup_seg

#;	/* XXX new stack pointer in safe area for calling functions */
#;	movl	$0x4000, %esp
#;	call	EXT_C(stop_floppy)

	/* final setup for linux boot */

	call	EXT_C(prot_to_real)

	.code16

	/* final setup for linux boot */
	cli
	movw	%bx, %ss
	movw	$LINUX_SETUP_STACK, %sp		# 0x9000

	movw	%bx, %ds
	movw	%bx, %es
	movw	%bx, %fs
	movw	%bx, %gs

#;	/* Reset floppy. Not required. */
#;	xorw	%ax, %ax
#;	xorb	%dl, %dl
#;	int	$0x13

	/* jump to start */
	/* ljmp */
	.byte	0xea
	.word	0
1:				//linux_setup_seg:
	.word	0


	.code32


/*
 * multi_boot(int start, int mb_info)
 *
 *  This starts a kernel in the manner expected of the multiboot standard.
 */

ENTRY(multi_boot)
	/* no need to save anything */
	call	EXT_C(stop_floppy)

	movl	$0x2BADB002, %eax
	movl	0x8(%esp), %ebx

	/* boot kernel here (absolute address call) */
	call	*0x4(%esp)

	/* error */
	call	EXT_C(stop)

#endif /* ! STAGE1_5 */

#ifndef STAGE1_5
/*
 * void toggle_blinking (void)
 * BIOS call "INT 10H Function 1003h" to toggle intensity/blinking bit
 *	Call with	%ax = 0x1003
 *			%bl = new state:
 *				0x00 enable background intensity
 *				0x01 enable blinking
 *			%bh = 0x00 to avoid problems on some adapters
 *      Returns         nothing
 */


ENTRY(toggle_blinking)
	push	%ebp
	push	%ebx                    /* save EBX */

	movl	EXT_C(blinking), %ebx
	call	EXT_C(prot_to_real)

	.code16

	//sti		/* it is not bad keeping interrupt off */
	sti		/* for hardware interrupt or watchdog */

        xorb	%bh, %bh
	testb	%bl, %bl
	setnz	%bl
	movw	$0x1003, %ax
	int	$0x10

	DATA32	call	EXT_C(real_to_prot)
	.code32

	pop	%ebx
	pop	%ebp
	ret
#endif /* ! STAGE1_5 */


/*
 * void console_putchar (int c)
 *
 * Put the character C on the console. Because GRUB wants to write a
 * character with an attribute, this implementation is a bit tricky.
 * If C is a control character (CR, LF, BEL, BS), use INT 10, AH = 0Eh
 * (TELETYPE OUTPUT). Otherwise, save the original position, put a space,
 * save the current position, restore the original position, write the
 * character and the attribute, and restore the current position.
 *
 * The reason why this is so complicated is that there is no easy way to
 * get the height of the screen, and the TELETYPE OUPUT BIOS call doesn't
 * support setting a background attribute.
 */
ENTRY(console_putchar)
	movl	0x4(%esp), %edx
	pusha
#ifdef STAGE1_5
	movb	$0x07, %bl
#else
	movl	EXT_C(console_current_color), %ebx
#endif

	call	EXT_C(prot_to_real)
	.code16

	//sti		/* it is not bad keeping interrupt off */
	sti		/* for hardware interrupt or watchdog */

	movb	%dl, %al
	xorb	%bh, %bh

#ifndef STAGE1_5
	/* use teletype output if control character */
	cmpb	$0x07, %al
	je	1f
	cmpb	$0x08, %al
	je	1f
	cmpb	$0x0a, %al
	je	1f
	cmpb	$0x0d, %al
	je	1f

	/* save the character and the attribute on the stack */
	pushw	%ax
	pushw	%bx

	/* get the current position */
	movb	$0x03, %ah
	int	$0x10

	/* check the column with the width */
	cmpb	$79, %dl
	jb	2f

	/* print CR and LF, if next write will exceed the width */
	movw	$0x0e0d, %ax
	int	$0x10
	movw	$0x0e0a, %ax
	int	$0x10

	/* get the current position */
	movb	$0x03, %ah
	int	$0x10

2:
	/* restore the character and the attribute */
	popw	%bx
	popw	%ax

	/* write the character with the attribute */
	movb	$0x09, %ah
	movw	$1, %cx
	int	$0x10

	/* move the cursor forward */
	incb	%dl
	movb	$0x02, %ah
	int	$0x10

	jmp	3f
#endif /* ! STAGE1_5 */

1:	movb	$0x0e, %ah
	int	$0x10

3:	DATA32	call	EXT_C(real_to_prot)
	.code32

	popa
	ret


#ifndef STAGE1_5

#if 0
/* this table is used in translate_keycode below */
translation_table:
	.word	KEY_LEFT, 2
	.word	KEY_RIGHT, 6
	.word	KEY_UP, 16
	.word	KEY_DOWN, 14
	.word	KEY_HOME, 1
	.word	KEY_END, 5
	.word	KEY_DC, 4
	.word	KEY_BACKSPACE, 8
	.word	KEY_PPAGE, 7
	.word	KEY_NPAGE, 3
	.word	0

/*
 * translate_keycode translates the key code %dx to an ascii code.
 */
	.code16

translate_keycode:
	pushw	%bx
	pushw	%si

	movw	$ABS(translation_table), %si

1:	lodsw
	/* check if this is the end */
	testw	%ax, %ax
	jz	2f
	/* load the ascii code into %ax */
	movw	%ax, %bx
	lodsw
	/* check if this matches the key code */
	cmpw	%bx, %dx
	jne	1b
	/* translate %dx, if successful */
	movw	%ax, %dx

2:	popw	%si
	popw	%bx
	ret
#endif

	.code32


/*
 * remap_ascii_char remaps the ascii code %dl to another if the code is
 * contained in ASCII_KEY_MAP.
 */
	.code16

remap_ascii_char:
	pushw	%si

	movw	$ABS(EXT_C(ascii_key_map)), %si
1:
	lodsw
	/* check if this is the end */
	testw	%ax, %ax
	jz	2f
	/* check if this matches the ascii code */
	cmpb	%al, %dl
	jne	1b
	/* if so, perform the mapping */
	movb	%ah, %dl
2:
	/* restore %si */
	popw	%si

	ret

	.code32

	.align	4
ENTRY(ascii_key_map)
	.space	(KEY_MAP_SIZE + 1) * 2


/*
 * int console_getkey (void)
 * BIOS call "INT 16H Function 00H" to read character from keyboard
 *	Call with	%ah = 0x0
 *	Return:		%ah = keyboard scan code
 *			%al = ASCII character
 */

ENTRY(console_getkey)
	push	%ebp

	call	EXT_C(prot_to_real)

	.code16

	sti		/* getkey needs interrupt on */

	#; work around for Apple BIOS getkey bug
	#; check the keyboard buffer, until there is a keypress.
1:
	movb	$0x01, %ah		#; check key
	int	$0x16
	jz	1b			#; no keypress

	xorw	%ax, %ax
	int	$0x16

	movw	%ax, %dx		/* real_to_prot uses %eax */
	//call	translate_keycode
	call	remap_ascii_char

	DATA32	call	EXT_C(real_to_prot)
	.code32

	movw	%dx, %ax

	pop	%ebp
	ret


/*
 * int console_checkkey (void)
 *	if there is a character pending, return it; otherwise return -1
 * BIOS call "INT 16H Function 01H" to check whether a character is pending
 *	Call with	%ah = 0x1
 *	Return:
 *		If key waiting to be input:
 *			%ah = keyboard scan code
 *			%al = ASCII character
 *			Zero flag = clear
 *		else
 *			Zero flag = set
 */
ENTRY(console_checkkey)
	push	%ebp
	xorl	%edx, %edx

	call	EXT_C(prot_to_real)	/* enter real mode */

	.code16

	sti		/* checkkey needs interrupt on */

	movb	$0x1, %ah
	int	$0x16

	DATA32	jz	notpending

	movw	%ax, %dx
	//call	translate_keycode
	call	remap_ascii_char
	DATA32	jmp	pending

notpending:
	movl	$0xFFFFFFFF, %edx

pending:
	DATA32	call	EXT_C(real_to_prot)
	.code32

	mov	%edx, %eax

	pop	%ebp
	ret


/*
 * int console_getxy (void)
 * BIOS call "INT 10H Function 03h" to get cursor position
 *	Call with	%ah = 0x03
 *			%bh = page
 *      Returns         %ch = starting scan line
 *                      %cl = ending scan line
 *                      %dh = row (0 is top)
 *                      %dl = column (0 is left)
 */


ENTRY(console_getxy)
	push	%ebp
	push	%ebx                    /* save EBX */

	call	EXT_C(prot_to_real)

	.code16

	//sti		/* it is not bad keeping interrupt off */
	sti		/* for hardware interrupt or watchdog */

        xorb	%bh, %bh                /* set page to 0 */
	movb	$0x3, %ah
	int	$0x10			/* get cursor position */

	DATA32	call	EXT_C(real_to_prot)
	.code32

	movb	%dl, %ah
	movb	%dh, %al

	pop	%ebx
	pop	%ebp
	ret


/*
 * void console_gotoxy(int x, int y)
 * BIOS call "INT 10H Function 02h" to set cursor position
 *	Call with	%ah = 0x02
 *			%bh = page
 *                      %dh = row (0 is top)
 *                      %dl = column (0 is left)
 */


ENTRY(console_gotoxy)
	push	%ebp
	push	%ebx                    /* save EBX */

	movb	0xc(%esp), %dl           /* %dl = x */
	movb	0x10(%esp), %dh          /* %dh = y */

	call	EXT_C(prot_to_real)

	.code16

	//sti		/* it is not bad keeping interrupt off */
	sti		/* for hardware interrupt or watchdog */

        xorb	%bh, %bh                /* set page to 0 */
	movb	$0x2, %ah
	int	$0x10			/* set cursor position */

	DATA32	call	EXT_C(real_to_prot)
	.code32

	pop	%ebx
	pop	%ebp
	ret


/*
 * void console_cls (void)
 * BIOS call "INT 10H Function 09h" to write character and attribute
 *	Call with	%ah = 0x09
 *                      %al = (character)
 *                      %bh = (page number)
 *                      %bl = (attribute)
 *                      %cx = (number of times)
 */


ENTRY(console_cls)
	push	%ebp
	push	%ebx                    /* save EBX */

	call	EXT_C(prot_to_real)

	.code16

	//sti		/* it is not bad keeping interrupt off */
	sti		/* for hardware interrupt or watchdog */

	/* move the cursor to the beginning */
	movb	$0x02, %ah
	xorb	%bh, %bh
	xorw	%dx, %dx
	int	$0x10

	/* write spaces to the entire screen */
	movw	$0x0920, %ax
	movw	$0x07, %bx
	movw	$(80 * 25), %cx
        int	$0x10

	/* move back the cursor */
	movb	$0x02, %ah
	int	$0x10

	DATA32	call	EXT_C(real_to_prot)
	.code32

	pop	%ebx
	pop	%ebp
	ret


/*
 * int console_setcursor (int on)
 * BIOS call "INT 10H Function 01h" to set cursor type
 *      Call with       %ah = 0x01
 *                      %ch = cursor starting scanline
 *                      %cl = cursor ending scanline
 */

console_cursor_state:
	.byte	1
console_cursor_shape:
	.word	0

ENTRY(console_setcursor)
	push	%ebp
	push	%ebx

	/* check if the standard cursor shape has already been saved */
	movw	console_cursor_shape, %ax
	testw	%ax, %ax
	jne	1f

	call	EXT_C(prot_to_real)

	.code16

	//sti		/* it is not bad keeping interrupt off */
	sti		/* for hardware interrupt or watchdog */

	movb	$0x03, %ah
	xorb	%bh, %bh
	int	$0x10

	DATA32	call	EXT_C(real_to_prot)
	.code32

	movw	%cx, console_cursor_shape
1:
	/* set %cx to the designated cursor shape */
	movw	$0x2000, %cx
	movl	0xc(%esp), %ebx
	testl	%ebx, %ebx
	jz	2f
	movw	console_cursor_shape, %cx
2:
	call	EXT_C(prot_to_real)

	.code16

	//sti		/* it is not bad keeping interrupt off */
	sti		/* for hardware interrupt or watchdog */

	movb    $0x1, %ah
	int     $0x10

	DATA32	call	EXT_C(real_to_prot)
	.code32

	movzbl	console_cursor_state, %eax
	movb	%bl, console_cursor_state

	pop	%ebx
	pop	%ebp
	ret

/* graphics mode functions */
#ifdef SUPPORT_GRAPHICS
VARIABLE(cursorX)
	.word	0
VARIABLE(cursorY)
	.word	0
VARIABLE(cursorCount)
	.word	0
VARIABLE(cursorBuf)
	.byte	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0


/*
 * int set_videomode(mode)
 * BIOS call "INT 10H Function 0h" to set video mode
 *	Call with	%ah = 0x0
 *			%al = video mode
 *      Returns old videomode.
 */
ENTRY(set_videomode)
	push	%ebp
	push	%ebx
	push	%ecx

	movb	0x10(%esp), %cl

	call	EXT_C(prot_to_real)

	.code16

	//sti		/* it is not bad keeping interrupt off */
	sti		/* for hardware interrupt or watchdog */

	xorw	%bx, %bx
	movb	$0xf, %ah
	int	$0x10			/* Get Current Video mode */
	movb	%al, %ch
	xorb	%ah, %ah
	movb	%cl, %al
	int	$0x10			/* Set Video mode */

	DATA32	call	EXT_C(real_to_prot)
	.code32

	xorb	%ah, %ah
	movb	%ch, %al

	pop	%ecx
	pop	%ebx
	pop	%ebp
	ret


/*
 * unsigned char * graphics_get_font()
 * BIOS call "INT 10H Function 11h" to set font
 *      Call with       %ah = 0x11
 */
ENTRY(graphics_get_font)
	push	%ebp
	push	%ebx
	push	%ecx
	push	%edx

	call	EXT_C(prot_to_real)

	.code16

	//sti		/* it is not bad keeping interrupt off */
	sti		/* for hardware interrupt or watchdog */

	movw	$0x1130, %ax
	movb	$6, %bh		/* font 8x16 */
	int	$0x10
	movw	%bp, %dx
	movw	%es, %cx

	DATA32	call	EXT_C(real_to_prot)
	.code32

	xorl	%eax, %eax
	movw	%cx, %ax
	shll	$4, %eax
	movw	%dx, %ax

	pop	%edx
	pop	%ecx
	pop	%ebx
	pop	%ebp
	ret



/*
 * graphics_set_palette(index, red, green, blue)
 * BIOS call "INT 10H Function 10h" to set individual dac register
 *	Call with	%ah = 0x10
 *			%bx = register number
 *			%ch = new value for green (0-63)
 *			%cl = new value for blue (0-63)
 *			%dh = new value for red (0-63)
 */

ENTRY(graphics_set_palette)
	push	%ebp
	push	%eax
	push	%ebx
	push	%ecx
	push	%edx

	movw	$0x3c8, %bx		/* address write mode register */

	/* wait vertical retrace */

	movw	$0x3da, %dx
l1b:	inb	%dx, %al	/* wait vertical active display */
	test	$8, %al
	jnz	l1b

l2b:	inb	%dx, %al	/* wait vertical retrace */
	test	$8, %al
	jnz	l2b

	mov	%bx, %dx
	movb	0x18(%esp), %al		/* index */
	outb	%al, %dx
	inc	%dx

	movb	0x1c(%esp), %al		/* red */
	outb	%al, %dx

	movb	0x20(%esp), %al		/* green */
	outb	%al, %dx

	movb	0x24(%esp), %al		/* blue */
	outb	%al, %dx

	movw	0x18(%esp), %bx

	call	EXT_C(prot_to_real)

	.code16

	//sti		/* it is not bad keeping interrupt off */
	sti		/* for hardware interrupt or watchdog */

	movb	%bl, %bh
	movw	$0x1000, %ax
	int	$0x10

	DATA32	call	EXT_C(real_to_prot)
	.code32

	pop	%edx
	pop	%ecx
	pop	%ebx
	pop	%eax
	pop	%ebp
	ret

#endif /* SUPPORT_GRAPHICS */

/*
 * getrtsecs()
 *	if a seconds value can be read, read it and return it (BCD),
 *      otherwise return 0xFF
 * BIOS call "INT 1AH Function 02H" to check whether a character is pending
 *	Call with	%ah = 0x2
 *	Return:
 *		If RT Clock can give correct values
 *			%ch = hour (BCD)
 *			%cl = minutes (BCD)
 *                      %dh = seconds (BCD)
 *                      %dl = daylight savings time (00h std, 01h daylight)
 *			Carry flag = clear
 *		else
 *			Carry flag = set
 *                         (this indicates that the clock is updating, or
 *                          that it isn't running)
 */
ENTRY(getrtsecs)
	push	%ebp

	call	EXT_C(prot_to_real)	/* enter real mode */

	.code16

	#;sti		/* getrtsecs needs interrupt on */
	sti	#; added 2006-11-30

#if 0
	movb	$0x2, %ah

	/* Ralf Brown's Interrupt List says:
	 *
	 * BUG: Some BIOSes leave CF unchanged if successful,
	 * so CF should be cleared before calling this function
	 */

	clc
	int	$0x1a

	DATA32	jnc	gottime
	movb	$0xff, %dh

gottime:
#else
	/* The call int1A/ah=02 could fail all the time.
	 * So we should avoid using it. Instead, we use ticks at 0040:006C.
	 *					- Tinybit 2007-04-21
	 */
	pushl	%ecx
	movl	0x46C, %eax
	movl	$5, %ecx	/* 5 seconds */
	mull	%ecx
	xorl	%edx, %edx
	movl	$91, %ecx	/* 91 ticks = 5 seconds */
	divl	%ecx
	xorl	%edx, %edx
	movl	$60, %ecx
	divl	%ecx		/* EDX=seconds (0 .. 59) */
	movb	%dl, %dh
	popl	%ecx

#endif
	DATA32	call	EXT_C(real_to_prot)
	.code32

	movb	%dh, %al

	pop	%ebp
	ret


/*
 *  void get_datetime(unsigned long *date, unsigned long *time);
 */
ENTRY(get_datetime)
	pushl	%ebp
	call	EXT_C(prot_to_real)

	.code16

	sti		/* for hardware interrupt or watchdog */
	movb	$2, %ah
	clc
	int	$0x1a
	jc	2f

	pushw	%cx
	pushw	%dx

	movb	$4, %ah
	clc
	int	$0x1a
	jc	3f

	pushw	%cx
	pushw	%dx
	popl	%edx
	popl	%ecx
	jmp	1f

3:
	popl	%eax

2:
	xorl	%ecx, %ecx
	xorl	%edx, %edx

1:
	DATA32	call	EXT_C(real_to_prot)
	.code32

	movl	%esp, %ebp
	movl	8(%ebp), %eax
	movl	%edx, (%eax)
	movl	12(%ebp), %eax
	movl	%ecx, (%eax)

	popl	%ebp
	ret

#if 0
	/* This BIOS call should NOT be called since it will clear the byte at 0040:0070. */

/*
 * currticks()
 *	return the real time in ticks, of which there are about
 *	18-20 per second
 */
ENTRY(currticks)
	pushl	%ebp

	call	EXT_C(prot_to_real)	/* enter real mode */

	.code16

	#;sti		/* currticks needs interrupt on */
	sti	#; added 2006-11-30

	/* %ax is already zero */
        int	$0x1a

	DATA32	call	EXT_C(real_to_prot)
	.code32

	movl	%ecx, %eax
	shll	$16, %eax
	movw	%dx, %ax

	popl	%ebp
	ret
#endif

#endif /* STAGE1_5 */

/*
 *  This is the area for all of the special variables.
 */

	.align	4

#;protstack:
#;	.long	PROTSTACKINIT

//VARIABLE(install_second_sector)
//	.long	0

	/* an address can only be long-jumped to if it is in memory, this
	   is used by multiple routines */
offset:
	.long	0x8000
segment:
	.word	0

#if 0
VARIABLE(apm_bios_info)
	.word	0	/* version */
	.word	0	/* cseg */
	.long	0	/* offset */
	.word	0	/* cseg_16 */
	.word	0	/* dseg_16 */
	.word	0	/* cseg_len */
	.word	0	/* cseg_16_len */
	.word	0	/* dseg_16_len */
#endif

/*
 * This is the Global Descriptor Table
 *
 *  An entry, a "Segment Descriptor", looks like this:
 *
 * 31          24         19   16                 7           0
 * ------------------------------------------------------------
 * |             | |B| |A|       | |   |1|0|E|W|A|            |
 * | BASE 31..24 |G|/|0|V| LIMIT |P|DPL|  TYPE   | BASE 23:16 |
 * |             | |D| |L| 19..16| |   |1|1|C|R|A|            |
 * ------------------------------------------------------------
 * |                             |                            |
 * |        BASE 15..0           |       LIMIT 15..0          |
 * |                             |                            |
 * ------------------------------------------------------------
 *
 *  Note the ordering of the data items is reversed from the above
 *  description.
 */

	.align	16
gdt:
	.word	0, 0
	.byte	0, 0, 0, 0

	/* code segment */
	.word	0xFFFF, 0
	.byte	0, 0x9A, 0xCF, 0

	/* data segment */
	.word	0xFFFF, 0
	.byte	0, 0x92, 0xCF, 0

	/* 16 bit real mode CS */
	.word	0xFFFF, 0
	.byte	0, 0x9E, 0, 0

	/* 16 bit real mode DS */
	.word	0xFFFF, 0
	.byte	0, 0x92, 0, 0


/* this is the GDT descriptor */
gdtdesc:
	.word	0x27			/* limit */
	.long	gdt			/* addr */



	.code32

/* this code will be moved to and get executed at HMA_ADDR=0x2B0000 */

/* our gdt starts at HMA_ADDR=0x2B0000 */

ENTRY(HMA_start)

	/* the first entry of GDT, i.e., the default null entry,
	 * can be any value. it never get used. So we use these
	 * 8 bytes for our jmp and GDT descriptor.
	 */

	. = EXT_C(HMA_start) + 0	/* GDT entry: default null */

	jmp	1f		/* two-byte short jmp */

	. = EXT_C(HMA_start) + 2

	/* 6-byte GDT descriptor */
gdtdescHMA:
	.word	0x27		/* limit */
	.long	HMA_ADDR	/* linear base address */

	. = EXT_C(HMA_start) + 8	/* GDT entry: unused */

	/* code segment, although it is no use here for now */
	.word	0xFFFF, 0
	.byte	0, 0x9A, 0xCF, 0

	. = EXT_C(HMA_start) + 0x10	/* GDT entry: unused */

	/* data segment, although it is no use here for now */
	.word	0xFFFF, 0
	.byte	0, 0x92, 0xCF, 0

	. = EXT_C(HMA_start) + 0x18	/* GDT entry: 16-bit code */

	/* 16-bit code segment base=0x2B0000 */
	.word	0xFFFF, 0x0000
	.byte	0x2B, 0x9E, 0, 0

	. = EXT_C(HMA_start) + 0x20	/* GDT entry: 16-bit data */

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

1:
	/* set up to pass boot drive */
	movb	EXT_C(boot_drive), %dl

	/* check if the --ebx option is given. */
	movl	(EXT_C(chain_ebx_set) - EXT_C(HMA_start) + HMA_ADDR), %eax
	testl	%eax, %eax
	jz	1f
	movl	(EXT_C(chain_ebx) - EXT_C(HMA_start) + HMA_ADDR), %ebx
1:

	/* check if the --edx option is given. */
	movl	(EXT_C(chain_edx_set) - EXT_C(HMA_start) + HMA_ADDR), %eax
	testl	%eax, %eax
	jz	1f
	movl	(EXT_C(chain_edx) - EXT_C(HMA_start) + HMA_ADDR), %edx
1:

	/* move new loader from extended memory to conventional memory.
	 * this will overwrite our GRUB code, data and stack, so we should not
	 * use instuctions like push/pop/call/ret, and we should not use
	 * functions like gateA20().
	 */

	/* the new loader is currently at 0x200000 */
	movl	$0x00200000, %esi
	xorl	%eax, %eax
	xorl	%edi, %edi
	movw	(EXT_C(chain_load_segment) - EXT_C(HMA_start) + HMA_ADDR), %di
	shll	$4, %edi
	movw	(EXT_C(chain_load_offset) - EXT_C(HMA_start) + HMA_ADDR), %ax
	addl	%eax, %edi
	//movl	$0x00007c00, %edi
	movl	(EXT_C(chain_load_length) - EXT_C(HMA_start) + HMA_ADDR), %ecx
	cld
	repz movsb

	/* switch to real mode */

	/* set new GDT */
	lgdt	(gdtdescHMA - EXT_C(HMA_start) + HMA_ADDR)

	/* set up segment limits */
	movw	$PSEUDO_RM_DSEG, %ax
	movw	%ax, %ds
	movw	%ax, %es
	movw	%ax, %fs
	movw	%ax, %gs
	movw	%ax, %ss

	movl	$0x200, %esp	/* points to end of interrupt vector table */
				/* SS base=0x200, so SS:SP=physical 0x400 */

	/* jump to a 16 bit segment, this might be an extra step:
	 * set up CS limit, also clear high word of EIP
	 */
	ljmp	$PSEUDO_RM_CSEG, $(1f - EXT_C(HMA_start))
1:
	.code16

	/* clear the PE bit of CR0 */
	movl	%cr0, %eax
	//andl	$CR0_PE_OFF, %eax
	andl 	$0x0000FFFE, %eax
	movl	%eax, %cr0

	/* setup DS, ES, SS, FS and GS before loading CS */
	xorl	%eax, %eax
	movw	%ax, %ds
	movw	%ax, %es
	movw	%ax, %ss
	movl	$0x400, %esp
	movw	%ax, %fs
	movw	%ax, %gs

	/* flush prefetch queue, reload %cs */

	.byte	0xEA		/* ljmp 0000:7c00 */
VARIABLE(chain_boot_IP)
	.word	0x7c00		/* offset */
VARIABLE(chain_boot_CS)
	.word	0x0000		/* segment */

VARIABLE(chain_load_offset)
	.word	0x7c00
VARIABLE(chain_load_segment)
	.word	0x0000
VARIABLE(chain_load_length)
	.long	0x200
VARIABLE(chain_ebx)
	.long	0
VARIABLE(chain_ebx_set)
	.long	0
VARIABLE(chain_edx)
	.long	0
VARIABLE(chain_edx_set)
	.long	0
VARIABLE(chain_enable_gateA20)
	.long	0

	/* max length of code is 1 sector */
	. = . - (. - EXT_C(HMA_start))/0x201

	/* ensure this resides in the first 64KB */
	. = . - (ABS(.) / 0x10001)

	.code32


