diff options
Diffstat (limited to 'grub-core/kern/i386/pc/startup.S')
-rw-r--r-- | grub-core/kern/i386/pc/startup.S | 1011 |
1 files changed, 1011 insertions, 0 deletions
diff --git a/grub-core/kern/i386/pc/startup.S b/grub-core/kern/i386/pc/startup.S new file mode 100644 index 0000000..e78a0aa --- /dev/null +++ b/grub-core/kern/i386/pc/startup.S @@ -0,0 +1,1011 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + + +/* + * 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 <config.h> +#include <grub/symbol.h> +#include <grub/boot.h> +#include <grub/machine/boot.h> +#include <grub/machine/memory.h> +#include <grub/machine/console.h> +#include <grub/cpu/linux.h> +#include <grub/machine/kernel.h> +#include <grub/term.h> +#include <multiboot.h> +#include <multiboot2.h> + +#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 +reed_solomon_redundancy: + .long 0 + +#ifdef APPLE_CC +bss_start: + .long 0 +bss_end: + .long 0 +#endif +/* + * This is the area for all of the special variables. + */ + +VARIABLE(grub_boot_drive) + .byte 0 + +/* 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 + + movl EXT_C(grub_compressed_size), %edx + addl $(GRUB_KERNEL_MACHINE_RAW_SIZE - GRUB_KERNEL_I386_PC_NO_REED_SOLOMON_PART), %edx + movl reed_solomon_redundancy, %ecx + leal _start + GRUB_KERNEL_I386_PC_NO_REED_SOLOMON_PART, %eax + call EXT_C (grub_reed_solomon_recover) + jmp post_reed_solomon + +#include <rs_decoder.S> + + .text + + . = _start + GRUB_KERNEL_I386_PC_NO_REED_SOLOMON_PART +/* + * 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 + jmp LOCAL (codestart) + +post_reed_solomon: + +#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) + +#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 + + +LOCAL(bypass_table): + .word 0x011b, 0x0f00 | '\t', 0x0e00 | '\b', 0x1c00 | '\r' + .word 0x1c00 | '\n' +LOCAL(bypass_table_end): + +/* + * int grub_console_getkey (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 + * BIOS call "INT 16H Function 00H" to read character from keyboard + * Call with %ah = 0x0 + * Return: %ah = keyboard scan code + * %al = ASCII character + */ + +FUNCTION(grub_console_getkey) + pushl %ebp + pushl %edi + + 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. + */ + + movb $1, %ah + int $0x16 + jz notpending + + movb $0, %ah + int $0x16 + + xorl %edx, %edx + movw %ax, %dx /* real_to_prot uses %eax */ + + DATA32 call real_to_prot + .code32 + + movl $0xff, %eax + testl %eax, %edx + jz 1f + + andl %edx, %eax + cmpl $0x20, %eax + jae 2f + movl %edx, %eax + leal LOCAL(bypass_table), %edi + movl $((LOCAL(bypass_table_end) - LOCAL(bypass_table)) >> 1), %ecx + repne scasw + jz 3f + + andl $0xff, %eax + addl $(('a' - 1) | GRUB_TERM_CTRL), %eax + jmp 2f +3: + andl $0xff, %eax + jmp 2f + +1: movl %edx, %eax + shrl $8, %eax + orl $GRUB_TERM_EXTENDED, %eax +2: + popl %edi + popl %ebp + ret + +notpending: + .code16 + DATA32 call real_to_prot + .code32 +#if GRUB_TERM_NO_KEY != 0 +#error Fix this asm code +#endif + jmp 2b + + +/* + * 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 + + cmp %cl, %ch + jb 3f + movw $0x0d0e, %cx +3: + 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 |