/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
 *
 *  GRUB 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  GRUB 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 GRUB.  If not, see .
 */
/*
 * 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 }
 */
/*
 * Note: GRUB is compiled with the options -mrtd and -mregparm=3.
 *       So the first three arguments are passed in %eax, %edx, and %ecx,
 *       respectively, and if a function has a fixed number of arguments
 *       and the number is greater than three, the function must return
 *       with "ret $N" where N is ((the number of arguments) - 3) * 4.
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define ABS(x)	((x) - LOCAL (base) + GRUB_BOOT_MACHINE_KERNEL_ADDR + 0x200)
	.file	"startup.S"
	.text
	/* Tell GAS to generate 16-bit instructions so that this code works
	   in real mode. */
	.code16
	.globl	start, _start
start:
_start:
LOCAL (base):
	/*
	 *  Guarantee that "main" is loaded at 0x0:0x8200.
	 */
#ifdef __APPLE__
	ljmp $0, $(ABS(LOCAL (codestart)) - 0x10000)
#else
	ljmp $0, $ABS(LOCAL (codestart))
#endif
	/*
	 *  Compatibility version number
	 *
	 *  These MUST be at byte offset 6 and 7 of the executable
	 *  DO NOT MOVE !!!
	 */
	. = _start + 0x6
	.byte	GRUB_BOOT_VERSION_MAJOR, GRUB_BOOT_VERSION_MINOR
	/*
	 *  This is a special data area 8 bytes from the beginning.
	 */
	. = _start + 0x8
VARIABLE(grub_total_module_size)
	.long	0
VARIABLE(grub_kernel_image_size)
	.long	0
VARIABLE(grub_compressed_size)
	.long	0
VARIABLE(grub_install_dos_part)
	.long	0xFFFFFFFF
VARIABLE(grub_install_bsd_part)
	.long	0xFFFFFFFF
#ifdef APPLE_CC
bss_start:
	.long 0
bss_end:
	.long 0
#endif
/*
 * Support for booting GRUB from a Multiboot boot loader (e.g. GRUB itself).
 * This uses the a.out kludge to load raw binary to the area starting at 1MB,
 * and relocates itself after loaded.
 */
	.p2align	2	/* force 4-byte alignment */
multiboot_header:
	/* magic */
	.long	0x1BADB002
	/* flags */
	.long	(1 << 16)
	/* checksum */
	.long	-0x1BADB002 - (1 << 16)
	/* header addr */
	.long	multiboot_header - _start + 0x100000 + 0x200
	/* load addr */
	.long	0x100000
	/* load end addr */
	.long	0
	/* bss end addr */
	.long	0
	/* entry addr */
	.long	multiboot_entry - _start + 0x100000 + 0x200
multiboot_entry:
	.code32
	/* obtain the boot device */
	movl	12(%ebx), %edx
	movl	$GRUB_MEMORY_MACHINE_PROT_STACK, %ebp
	movl	%ebp, %esp
	/* relocate the code */
	movl	$(GRUB_KERNEL_MACHINE_RAW_SIZE + 0x200), %ecx
	addl	EXT_C(grub_compressed_size) - _start + 0x100000 + 0x200, %ecx
	movl	$0x100000, %esi
	movl	$GRUB_BOOT_MACHINE_KERNEL_ADDR, %edi
	cld
	rep
	movsb
	/* jump to the real address */
	movl	$multiboot_trampoline, %eax
	jmp	*%eax
multiboot_trampoline:
	/* fill the boot information */
	movl	%edx, %eax
	shrl	$8, %eax
	xorl	%ebx, %ebx
	cmpb	$0xFF, %ah
	je	1f
	movb	%ah, %bl
	movl	%ebx, EXT_C(grub_install_dos_part)
1:
	cmpb	$0xFF, %al
	je	2f
	movb	%al, %bl
	movl	%ebx, EXT_C(grub_install_bsd_part)
2:
	shrl	$24, %edx
        movb    $0xFF, %dh
	/* enter the usual booting */
	call	prot_to_real
	.code16
/* the real mode code continues... */
LOCAL (codestart):
	cli		/* we're not safe here! */
	/* set up %ds, %ss, and %es */
	xorw	%ax, %ax
	movw	%ax, %ds
	movw	%ax, %ss
	movw	%ax, %es
	/* set up the real mode/BIOS stack */
	movl	$GRUB_MEMORY_MACHINE_REAL_STACK, %ebp
	movl	%ebp, %esp
	sti		/* we're safe again */
	/* save the boot drive */
	ADDR32	movb	%dl, EXT_C(grub_boot_drive)
	/* reset disk system (%ah = 0) */
	int	$0x13
	/* transition to protected mode */
	DATA32	call real_to_prot
	/* The ".code32" directive takes GAS out of 16-bit mode. */
	.code32
	incl	%eax
	call	grub_gate_a20
#ifdef ENABLE_LZMA
	movl	$GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR, %edi
	movl	$(_start + GRUB_KERNEL_MACHINE_RAW_SIZE), %esi
	pushl	%edi
	pushl	%esi
	movl	EXT_C(grub_kernel_image_size), %ecx
	addl	EXT_C(grub_total_module_size), %ecx
	subl	$GRUB_KERNEL_MACHINE_RAW_SIZE, %ecx
	pushl	%ecx
	leal	(%edi, %ecx), %ebx
	call	_LzmaDecodeA
	/* _LzmaDecodeA clears DF, so no need to run cld */
	popl	%ecx
	popl	%edi
	popl	%esi
#endif
	/* copy back the decompressed part (except the modules) */
	subl	EXT_C(grub_total_module_size), %ecx
	rep
	movsb
#if 0
	/* copy modules before cleaning out the bss */
	movl	EXT_C(grub_total_module_size), %ecx
	movl	EXT_C(grub_kernel_image_size), %esi
	addl	%ecx, %esi
	addl	$_start, %esi
	decl	%esi
	movl	$END_SYMBOL, %edi
	addl	%ecx, %edi
	decl	%edi
	std
	rep
	movsb
#endif
#ifdef APPLE_CC
	/* clean out the bss */
	bss_start_abs = ABS (bss_start)
	bss_end_abs = ABS (bss_end)
	movl    bss_start_abs, %edi
	/* compute the bss length */
	movl	bss_end_abs, %ecx
	subl	%edi, %ecx
#else
	/* clean out the bss */
	movl	$BSS_START_SYMBOL, %edi
	/* compute the bss length */
	movl	$END_SYMBOL, %ecx
	subl	%edi, %ecx
#endif
	/* clean out */
	xorl	%eax, %eax
	cld
	rep
	stosb
	/*
	 *  Call the start of main body of C code.
	 */
	call EXT_C(grub_main)
/*
 *  This is the area for all of the special variables.
 */
VARIABLE(grub_boot_drive)
	.byte	0
	.p2align	2	/* force 4-byte alignment */
#include "../realmode.S"
/*
 * grub_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.  :-(
 */
grub_gate_a20:	
	movl	%eax, %edx
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	prot_to_real
	.code16
	movw	$0x2400, %ax
	testb	%dl, %dl
	jz	1f
	incw	%ax
1:	int	$0x15
	DATA32	call	real_to_prot
	.code32
	popl	%ebp
	call	gate_a20_check_state
	cmpb	%al, %dl
	jnz	gate_a20_try_system_control_port_a
	ret
gate_a20_try_system_control_port_a:
	/*
	 * In macbook, the keyboard test would hang the machine, so we move
	 * this forward.
	 */
	/* 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
	jnz	gate_a20_try_keyboard_controller
7:	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
	/* everything failed, so restart from the beginning */
	jnz	gate_a20_try_bios
	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	$GRUB_BOOT_MACHINE_KERNEL_ADDR, %ebx
	pushl	%ebx
	/* save the original byte in CL */
	movb	(%ebx), %cl
	/* store the value at 0x108000 in AL */
	addl	$0x100000, %ebx
	movb	(%ebx), %al
	/* try to set one less value at 0x8000 */
	popl	%ebx
	movb	%al, %ch
	decb	%ch
	movb	%ch, (%ebx)
	/* serialize */
	outb	%al, $0x80
	outb	%al, $0x80
	/* obtain the value at 0x108000 in CH */
	pushl	%ebx
	addl	$0x100000, %ebx
	movb	(%ebx), %ch
	/* this result is 1 if A20 is on or 0 if it is off */
	subb	%ch, %al
	xorb	$1, %al
	/* restore the original */
	popl	%ebx
	movb	%cl, (%ebx)
	popl	%ecx
	popl	%ebx
	ret
#ifdef ENABLE_LZMA
#include "lzma_decode.S"
#endif
/*
 * The code beyond this point is compressed.  Assert that the uncompressed
 * code fits GRUB_KERNEL_MACHINE_RAW_SIZE.
 */
	. = _start + GRUB_KERNEL_MACHINE_RAW_SIZE
	. = _start + GRUB_KERNEL_MACHINE_PREFIX
VARIABLE(grub_prefix)
	/* to be filled by grub-mkimage */
	/*
	 *  Leave some breathing room for the prefix.
	 */
	. = _start + GRUB_KERNEL_MACHINE_PREFIX_END
/*
 * grub_exit()
 *
 * Exit the system.
 */
FUNCTION(grub_exit)
	call	prot_to_real
	.code16
	/* Tell the BIOS a boot failure. If this does not work, reboot.  */
	int	$0x18
	jmp	cold_reboot
	.code32
/*
 *  void grub_chainloader_real_boot (int drive, void *part_addr)
 *
 *  This starts another boot loader.
 */
FUNCTION(grub_chainloader_real_boot)
	pushl	%edx
	pushl	%eax
	/* Turn off Gate A20 */
	xorl	%eax, %eax
	call	grub_gate_a20
	/* set up to pass boot drive */
	popl	%edx
	/* ESI must point to a partition table entry */
	popl	%esi
	call	prot_to_real
	.code16
	ljmp	$0, $GRUB_MEMORY_MACHINE_BOOT_LOADER_ADDR
	.code32
/*
 * void grub_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 OUTPUT BIOS call doesn't
 * support setting a background attribute.
 */
FUNCTION(grub_console_putchar)
	/* Retrieve the base character.  */
	movl	0(%edx), %edx
	pusha
	movb	EXT_C(grub_console_cur_color), %bl
	call	prot_to_real
	.code16
	movb	%dl, %al
	xorb	%bh, %bh
	/* use teletype output if control character */
	cmpb	$0x7, %al
	je	1f
	cmpb	$0x8, %al
	je	1f
	cmpb	$0xa, %al
	je	1f
	cmpb	$0xd, %al
	je	1f
	/* save the character and the attribute on the stack */
	pushw	%ax
	pushw	%bx
	/* get the current position */
	movb	$0x3, %ah
	int	$0x10
	/* check the column with the width */
	cmpb	$79, %dl
	jl	2f
	/* print CR and LF, if next write will exceed the width */
	movw	$0x0e0d, %ax
	int	$0x10
	movb	$0x0a, %al
	int	$0x10
	/* get the current position */
	movb	$0x3, %ah
	int	$0x10
2:
	/* restore the character and the attribute */
	popw	%bx
	popw	%ax
	/* write the character with the attribute */
	movb	$0x9, %ah
	movw	$1, %cx
	int	$0x10
	/* move the cursor forward */
	incb	%dl
	movb	$0x2, %ah
	int	$0x10
	jmp	3f
1:	movw	$1, %bx
	movb	$0xe, %ah
	int	$0x10
3:	DATA32	call	real_to_prot
	.code32
	popa
	ret
/*
 * int grub_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
 */
/* this table is used in translate_keycode below */
LOCAL (translation_table):
	.word	GRUB_CONSOLE_KEY_LEFT, GRUB_TERM_LEFT
	.word	GRUB_CONSOLE_KEY_RIGHT, GRUB_TERM_RIGHT
	.word	GRUB_CONSOLE_KEY_UP, GRUB_TERM_UP
	.word	GRUB_CONSOLE_KEY_DOWN, GRUB_TERM_DOWN
	.word	GRUB_CONSOLE_KEY_HOME, GRUB_TERM_HOME
	.word	GRUB_CONSOLE_KEY_END, GRUB_TERM_END
	.word	GRUB_CONSOLE_KEY_DC, GRUB_TERM_DC
	.word	GRUB_CONSOLE_KEY_BACKSPACE, GRUB_TERM_BACKSPACE
	.word	GRUB_CONSOLE_KEY_PPAGE, GRUB_TERM_PPAGE
	.word	GRUB_CONSOLE_KEY_NPAGE, GRUB_TERM_NPAGE
	.word	0
/*
 * translate_keycode translates the key code %dx to an ascii code.
 */
	.code16
translate_keycode:
	pushw	%bx
	pushw	%si
#ifdef __APPLE__
	movw	$(ABS(LOCAL (translation_table)) - 0x10000), %si
#else
	movw	$ABS(LOCAL (translation_table)), %si
#endif
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
	.code32
FUNCTION(grub_console_getkey)
	pushl	%ebp
	call	prot_to_real
	.code16
	/*
	 * Due to a bug in apple's bootcamp implementation, INT 16/AH = 0 would
	 * cause the machine to hang at the second keystroke. However, we can
	 * work around this problem by ensuring the presence of keystroke with
	 * INT 16/AH = 1 before calling INT 16/AH = 0.
	 */
1:
	movb	$1, %ah
	int	$0x16
	jnz	2f
	hlt
	jmp	1b
2:
	movb	$0, %ah
	int	$0x16
	movw	%ax, %dx		/* real_to_prot uses %eax */
	call	translate_keycode
	DATA32	call	real_to_prot
	.code32
	movw	%dx, %ax
	popl	%ebp
	ret
/*
 * int grub_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
 */
FUNCTION(grub_console_checkkey)
	pushl	%ebp
	xorl	%edx, %edx
	call	prot_to_real	/* enter real mode */
	.code16
	movb	$0x1, %ah
	int	$0x16
	jz	notpending
	movw	%ax, %dx
	DATA32	jmp	pending
notpending:
	decl	%edx
pending:
	DATA32	call	real_to_prot
	.code32
	movl	%edx, %eax
	popl	%ebp
	ret
/*
 * grub_uint16_t grub_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)
 */
FUNCTION(grub_console_getxy)
	pushl	%ebp
	pushl	%ebx                    /* save EBX */
	call	prot_to_real
	.code16
        xorb	%bh, %bh                /* set page to 0 */
	movb	$0x3, %ah
	int	$0x10			/* get cursor position */
	DATA32	call	real_to_prot
	.code32
	movb	%dl, %ah
	movb	%dh, %al
	popl	%ebx
	popl	%ebp
	ret
/*
 * void grub_console_gotoxy(grub_uint8_t x, grub_uint8_t 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)
 */
FUNCTION(grub_console_gotoxy)
	pushl	%ebp
	pushl	%ebx                    /* save EBX */
	movb	%cl, %dh	/* %dh = y */
	/* %dl = x */
	call	prot_to_real
	.code16
        xorb	%bh, %bh                /* set page to 0 */
	movb	$0x2, %ah
	int	$0x10			/* set cursor position */
	DATA32	call	real_to_prot
	.code32
	popl	%ebx
	popl	%ebp
	ret
/*
 * void grub_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)
 */
FUNCTION(grub_console_cls)
	pushl	%ebp
	pushl	%ebx                    /* save EBX */
	call	prot_to_real
	.code16
	/* 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	real_to_prot
	.code32
	popl	%ebx
	popl	%ebp
	ret
/*
 * void grub_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
FUNCTION(grub_console_setcursor)
	pushl	%ebp
	pushl	%ebx
	/* push ON */
	pushl	%edx
	/* check if the standard cursor shape has already been saved */
	movw	console_cursor_shape, %ax
	testw	%ax, %ax
	jne	1f
	call	prot_to_real
	.code16
	movb	$0x03, %ah
	xorb	%bh, %bh
	int	$0x10
	DATA32	call	real_to_prot
	.code32
	movw	%cx, console_cursor_shape
1:
	/* set %cx to the designated cursor shape */
	movw	$0x2000, %cx
	popl	%eax
	testl	%eax, %eax
	jz	2f
	movw	console_cursor_shape, %cx
2:
	call	prot_to_real
	.code16
	movb    $0x1, %ah
	int     $0x10
	DATA32	call	real_to_prot
	.code32
	popl	%ebx
	popl	%ebp
	ret
/*
 * grub_get_rtc()
 *	return the real time in ticks, of which there are about
 *	18-20 per second
 */
FUNCTION(grub_get_rtc)
	pushl	%ebp
	call	prot_to_real	/* enter real mode */
	.code16
	/* %ax is already zero */
        int	$0x1a
	DATA32	call	real_to_prot
	.code32
	movl	%ecx, %eax
	shll	$16, %eax
	movw	%dx, %ax
	popl	%ebp
	ret
/*
 * int grub_pxe_call (int func, void* data, grub_uint32_t pxe_rm_entry);
 */
FUNCTION(grub_pxe_call)
	pushl	%ebp
	movl	%esp, %ebp
	pushl	%esi
	pushl	%edi
	pushl	%ebx
	movl	%ecx, %ebx
	movl	%eax, %ecx
	movl	%edx, %eax
	andl	$0xF, %eax
	shrl	$4, %edx
	shll	$16, %edx
	addl	%eax, %edx
	call    prot_to_real
	.code16
	pushl	%ebx
	pushl	%edx
	pushw	%cx
	movw	%sp, %bx
	lcall	*%ss:6(%bx)
	cld
	addw	$10, %sp
	movw	%ax, %cx
	DATA32  call	real_to_prot
	.code32
	movzwl	%cx, %eax
	popl	%ebx
	popl	%edi
	popl	%esi
	popl	%ebp
	ret
FUNCTION(grub_bios_interrupt)
	pushl    %ebp
	pushl    %ecx
	pushl    %eax
	pushl    %ebx
	pushl    %esi
	pushl    %edi	
	pushl    %edx
	
	movb     %al, intno
	movl	 (%edx), %eax
	movl	 %eax, LOCAL(bios_register_eax)
	movw	 4(%edx), %ax
	movw	 %ax, LOCAL(bios_register_es)
	movw	 6(%edx), %ax
	movw	 %ax, LOCAL(bios_register_ds)
	movw	 8(%edx), %ax
	movw	 %ax, LOCAL(bios_register_flags)
	movl 	12(%edx), %ebx
	movl 	16(%edx), %ecx
	movl 	20(%edx), %edi
	movl 	24(%edx), %esi
	movl 	28(%edx), %edx
	call    prot_to_real
	.code16
	mov	%ds, %ax
	push	%ax
	/* movw imm16, %ax*/
	.byte	0xb8
LOCAL(bios_register_es):
	.short 	0
	movw	%ax, %es
	/* movw imm16, %ax*/
	.byte	0xb8
LOCAL(bios_register_ds):
	.short 	0
	movw	%ax, %ds
	/* movw imm16, %ax*/
	.byte	0xb8
LOCAL(bios_register_flags):
	.short 	0
	push	%ax
	popf
	/* movl imm32, %eax*/
	.byte	0x66, 0xb8
LOCAL(bios_register_eax):
	.long 	0
	
	/* int imm8.  */
	.byte   0xcd
intno:	
	.byte   0
	movl 	%eax, %cs:LOCAL(bios_register_eax)
	movw	%ds, %ax
	movw 	%ax, %cs:LOCAL(bios_register_ds)
	pop 	%ax
	mov	%ax, %ds
	pushf
	pop	%ax
	movw	%ax, LOCAL(bios_register_flags)
	mov 	%es, %ax
	movw	%ax, LOCAL(bios_register_es)
	DATA32  call	real_to_prot
	.code32
	popl    %eax
	movl 	%ebx, 12(%eax)
	movl 	%ecx, 16(%eax)
	movl 	%edi, 20(%eax)
	movl 	%esi, 24(%eax)
	movl 	%edx, 28(%eax)
	movl     %eax, %edx
	movl	 LOCAL(bios_register_eax), %eax
	movl	 %eax, (%edx)
	movw	 LOCAL(bios_register_es), %ax
	movw	 %ax, 4(%edx)
	movw	 LOCAL(bios_register_ds), %ax
	movw	 %ax, 6(%edx)
	movw	 LOCAL(bios_register_flags), %ax
	movw	 %ax, 8(%edx)
	popl 	%edi
	popl 	%esi
	popl    %ebx
	popl    %eax
	popl    %ecx
	popl    %ebp
	ret