/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1996 Erich Boleyn * * 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. */ #define ASM_FILE #include "shared.h" .file "asm.S" .text /* Tell GAS to generate 16-bit instructions so that this code works in real mode. */ .code16 ENTRY(start) /* * Guarantee that "start" is loaded at 0x0:0x8000. */ ljmp $0, $(codestart - EXT_C(start) + 0x8000) /* * Compatibility version number * * These MUST be at byte offset 6 and 7 of the executable * DO NOT MOVE !!! */ . = EXT_C(start) + 0x6 .byte COMPAT_VERSION_MAJOR, COMPAT_VERSION_MINOR /* * This is a special data area 8 bytes from the beginning. */ . = EXT_C(start) + 0x8 VARIABLE(install_partition) .long 0x020000 VARIABLE(version_string) .string "0.5" VARIABLE(config_file) #ifndef CONFIG_FILE_ASM .string "/boot/grub/menu.lst" #else /* CONFIG_FILE_ASM */ CONFIG_FILE_ASM #endif /* CONFIG_FILE_ASM */ /* * Leave some breathing room for the config file name. */ . = EXT_C(start) + 0x70 /* the real mode code continues... */ 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 $STACKOFF, %ebp movl %ebp, %esp sti /* we're safe again */ /* save boot drive reference */ addr32 movb %dl, EXT_C(boot_drive) /* reset disk system (%ah = 0) */ int $0x13 /* transition to protected mode */ data32 call EXT_C(real_to_prot) /* The ".code32" directive takes GAS out of 16-bit mode. */ .code32 /* * 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) /* * 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. */ ENTRY(hard_stop) hlt jmp EXT_C(hard_stop) /* * stop_floppy() * * Stops the floppy drive from spinning, so that other software is * jumped to with a known state. */ ENTRY(stop_floppy) movw $0x3F2, %dx xorb %al, %al outb %al, %dx ret /* * 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 data32 addr32 ljmp (offset) .code32 /* * chain_stage2(segment, offset) * * 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 /* store install info */ movl EXT_C(install_partition), %eax movl %eax, EXT_C(install_partition)-EXT_C(start)(%ebx) /* set up to pass boot drive */ movb EXT_C(boot_drive), %dl call EXT_C(prot_to_real) .code16 data32 addr32 ljmp (offset) .code32 /* * 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 */ data32 addr32 lgdt gdtdesc /* turn on protected mode */ movl %cr0, %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 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 */ sti /* return on new stack! */ data32 ret .code32 /* * biosdisk(subfunc, drive, geometry, sector, nsec, segment) * Read/write "nsec" sectors from disk to real segment "segment" * offset zero * * If it will fit into the BIOS geometry, it tries the INT 0x13 * AH= interface, else if it is a hard disk, it will * try the hard disk LBA calls (not yet implemented). If these * won't work, or otherwise it is out of the translated area, then * it returns BIOS_GEOMETRY_ERROR. */ ENTRY(biosdisk) push %ebp mov %esp, %ebp push %ebx push %ecx push %edx push %esi /* * "geometry" is a longword representing the BIOS geometry: * 6 bit zero * 10 bit cylinder (bytes 2 & 3) * 8 bit head (byte 1) * 8 bit sector (byte 0) */ /* set up original sector number */ xorl %edx, %edx movl 0x14(%ebp), %eax /* get sector offset, place in %ecx */ xorl %ebx, %ebx movb 0x10(%ebp), %bl divl %ebx movl %edx, %ecx /* get track offset (head number) in %edx, and cylinder offset in %eax */ xorl %edx, %edx xorl %ebx, %ebx movb 0x11(%ebp), %bl inc %ebx divl %ebx /* check cylinder offset, is there a geometry problem here? */ movl 0x10(%ebp), %ebx shrl $16, %ebx cmpl %ebx, %eax /* if not, go onto standard read function */ jle disk_use_standard_bios /* XXX else we better use LBA or generate a geometry error */ movl $BIOSDISK_ERROR_GEOMETRY, %eax jmp disk_exit_32 /* * This portion implements the BIOS standardized * INT 0x13/AH= interface. */ disk_use_standard_bios: shll $8, %edx /* get head number to %dh */ xchgl %eax, %ecx /* cylinder to %cx, sector to %al */ /* cylinder; the highest 2 bits of cyl is in %cl */ xchgb %ch, %cl rorb $2, %cl incb %al /* sector; sec starts from 1, not 0 */ orb %al, %cl movb 0xc(%ebp), %dl /* drive */ /* prot_to_real will set %es to 0, so must set it ourselves after it is called */ movb 0x18(%ebp), %bl /* number of sectors */ movb 0x8(%ebp), %bh /* bios disk subfunction */ shll $16, %ebx /* shift num sect. and subfunction */ movw 0x1c(%ebp), %bx /* segment */ call EXT_C(prot_to_real) /* enter real mode */ .code16 movw %bx, %es /* load segment */ /* * Shift num sectors and subfunction back */ data32 shrl $16, %ebx pushw %bx /* save num sectors */ xorw %bx, %bx movw $3, %si disk_loop: popw %ax /* restore num sectors */ pushw %ax /* save num sectors */ /* BIOS call for reading/writing */ int $0x13 /* set success return value */ movb $0, %bl /* did we actually succeed? */ data32 jnc disk_exit_16 /* do we try again? */ decw %si cmpw $0, %si /* if this isn't the third try, go again */ data32 jne disk_loop /* save return value */ movb %ah, %bl disk_exit_16: data32 call EXT_C(real_to_prot) /* back to protected mode */ .code32 movb %bl, %al /* return value in %eax */ disk_exit_32: pop %esi pop %edx pop %ecx pop %ebx pop %ebp ret /* * * get_diskinfo(drive): return a word that represents the * max number of sectors and heads and cylinders for this drive * */ ENTRY(get_diskinfo) pushl %ebp movl %esp, %ebp pushl %ebx pushl %ecx pushl %edx pushl %edi pushl %esi movw 0x8(%ebp), %dx /* diskinfo(drive #) */ call EXT_C(prot_to_real) /* enter real mode */ .code16 movb %dl, %al andb $0x80, %al data32 jnz hard_drive /* * Perform floppy probe! */ movl $probe_values-1, %esi probe_loop: /* reset floppy controller INT 13h AH=0 */ xorw %ax, %ax int $0x13 incw %si movb (%si), %cl /* if number of sectors is 0, display error and die */ cmpb $0, %cl data32 je probe_failed /* perform read */ movw $SCRATCHSEG, %ax movw %ax, %es xorw %bx, %bx movw $0x201, %ax movb $0, %ch movb $0, %dh int $0x13 data32 jc probe_loop /* %cl is already the correct value! */ movb $1, %dh movb $79, %ch data32 jmp probe_success probe_values: .byte 36, 18, 15, 9, 0 hard_drive: movb $0x8, %ah /* ask for disk info */ int $0x13 data32 jc probe_failed /* es:di = parameter table */ /* carry = 0 */ probe_success: /* * form a longword representing all this gunk: * 6 bit zero * 10 bit cylinder * 8 bit head * 8 bit sector */ movb %cl, %al /* Upper two bits of cylinder count */ andl $192,%eax addr32 leal 0(,%eax,4),%eax /* << 2 */ movb %ch, %al /* Lower 8 bits */ sall $16,%eax /* << 16 */ movb %dh, %ah /* max head */ andb $0x3f, %cl /* mask of cylinder gunk */ movb %cl, %al /* max sector (and # sectors) */ movl %eax, %ebx /* save return value */ data32 jmp got_drive probe_failed: /* * Urk. Call failed. It is not supported for floppies by old * BIOSes, but it should work for all hard drives!! * * Return a 0 here... presume there is no drive present. ???? */ movl $0, %ebx /* not present value */ got_drive: data32 call EXT_C(real_to_prot) /* back to protected mode */ .code32 /* set up return in correct register */ movl %ebx, %eax popl %esi popl %edi popl %edx popl %ecx popl %ebx popl %ebp ret /* * putchar(c) : Puts character on the screen, interpreting '\n' as in the * UNIX fashion. * * BIOS call "INT 10H Function 0Eh" to write character to console * Call with %ah = 0x0e * %al = character * %bh = page * %bl = foreground color ( graphics modes) */ ENTRY(putchar) push %ebp push %eax push %ebx movb 0x10(%esp), %bl /* if not '\n', just print the character */ cmpb $0xa, %bl jne pc_notnewline /* if newline, print CR as well */ pushl $0xd call EXT_C(putchar) popl %eax pc_notnewline: call EXT_C(prot_to_real) .code16 movb %bl, %al movb $0xe, %ah movw $1, %bx int $0x10 data32 call EXT_C(real_to_prot) .code32 pop %ebx pop %eax pop %ebp ret /* * * 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) push %ebp push %ebx mov 0xc(%esp), %ebx call EXT_C(prot_to_real) /* enter real mode */ .code16 cmpb $0x1, %bl data32 je xext int $0x12 data32 jmp xdone xext: movb $0x88, %ah int $0x15 xdone: movw %ax, %bx data32 call EXT_C(real_to_prot) .code32 movw %bx, %ax pop %ebx pop %ebp ret #ifndef NO_FANCY_STUFF /* * * 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) push %ebp push %ebx push %ecx push %edx call EXT_C(prot_to_real) /* enter real mode */ .code16 movw $0xe801, %ax int $0x15 shll $16, %ebx movw %ax, %bx data32 call EXT_C(real_to_prot) .code32 movl $0xFFFFFFFF, %eax cmpb $0x86, %bh je xnoteisa movl %ebx, %eax xnoteisa: pop %edx pop %ecx pop %ebx pop %ebp ret /* * * get_mem_map(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_mem_map) push %ebp push %ebx push %ecx push %edx push %edi push %esi /* place address (+4) in ES:DI */ movl 0x1c(%esp), %eax addl $4, %eax movl %eax, %edi andl $0xf, %edi shrl $4, %eax movl %eax, %esi /* set continuation value */ movl 0x20(%esp), %ebx /* 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 movw %si, %es movw $0xe820, %ax int $0x15 data32 jc xnosmap cmpl $0x534d4150, %eax data32 jne xnosmap cmpl $0x14, %ecx data32 jl xnosmap cmpl $0x400, %ecx data32 jg xnosmap data32 jmp xsmap xnosmap: movl $0, %ecx xsmap: data32 call EXT_C(real_to_prot) .code32 /* write length of buffer (zero if error) into "addr" */ movl 0x1c(%esp), %eax movl %ecx, (%eax) /* set return value to continuation */ movl %ebx, %eax pop %esi pop %edi pop %edx pop %ecx pop %ebx pop %ebp ret /* * 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. :-( */ ENTRY(gateA20) pushl %eax call gloop1 movb $KC_CMD_WOUT, %al outb $K_CMD gloopint1: inb $K_STATUS andb $K_IBUF_FUL, %al jnz gloopint1 movb $KB_OUTPUT_MASK, %al cmpb $0, 0x8(%esp) jz gdoit orb $KB_A20_ENABLE, %al gdoit: outb $K_RDWR call gloop1 popl %eax ret gloop1: inb $K_STATUS andb $K_IBUF_FUL, %al jnz gloop1 gloop2: inb $K_STATUS andb $K_OBUF_FUL, %al jz gloop2ret inb $K_RDWR jmp gloop2 gloop2ret: ret #ifdef DEBUG ENTRY(patch_code) /* labels start with "pc_" */ .code16 mov %cs, %ax mov %ax, %ds mov %ax, %es mov %ax, %fs mov %ax, %gs addr32 movl $0, 0 pc_stop: hlt data32 jmp pc_stop ENTRY(patch_code_end) .code32 #endif /* DEBUG */ /* * linux_boot() * * Does some funky things (including on the stack!), then jumps to the * entry point of the Linux setup code. */ ENTRY(linux_boot) /* don't worry about saving anything, we're committed at this point */ cld /* forward copying */ /* copy kernel */ movl $LINUX_SETUP, %eax movl LINUX_KERNEL_LEN_OFFSET(%eax), %ecx shll $2, %ecx movl $LINUX_STAGING_AREA, %esi movl $LINUX_KERNEL, %edi rep movsl ENTRY(big_linux_boot) /* XXX new stack pointer in safe area for calling functions */ movl $0x4000, %esp call EXT_C(stop_floppy) /* final setup for linux boot */ movw $LINUX_SETUP_SEG, %ax movw %ax, segment xorl %eax, %eax movl %eax, offset call EXT_C(prot_to_real) .code16 /* final setup for linux boot */ movw $LINUX_SETUP_STACK, %sp movw $LINUX_INIT_SEG, %ax movw %ax, %ss /* jump to start */ data32 addr32 ljmp (offset) .code32 /* * multi_boot(int start, int mbi) * * 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) /* * cls() * BIOS call "INT 10H Function 0Fh" to get current video mode * Call with %ah = 0x0f * Returns %al = (video mode) * %bh = (page number) * BIOS call "INT 10H Function 00h" to set the video mode (clears screen) * Call with %ah = 0x00 * %al = (video mode) */ ENTRY(cls) push %ebp push %eax push %ebx /* save EBX */ call EXT_C(prot_to_real) .code16 movb $0xf, %ah int $0x10 /* Get Current Video mode */ xorb %ah, %ah int $0x10 /* Set Video mode (clears screen) */ data32 call EXT_C(real_to_prot) .code32 pop %ebx pop %eax pop %ebp ret /* * getxy() * 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(getxy) push %ebp push %ebx /* save EBX */ push %ecx /* save ECX */ push %edx call EXT_C(prot_to_real) .code16 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 %edx pop %ecx pop %ebx pop %ebp ret /* * gotoxy(x,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(gotoxy) push %ebp push %eax push %ebx /* save EBX */ push %edx movb 0x14(%esp), %dl /* %dl = x */ movb 0x18(%esp), %dh /* %dh = y */ call EXT_C(prot_to_real) .code16 xorb %bh, %bh /* set page to 0 */ movb $0x2, %ah int $0x10 /* set cursor position */ data32 call EXT_C(real_to_prot) .code32 pop %edx pop %ebx pop %eax pop %ebp ret /* * set_attrib(attr) : Sets the character attributes for character at * current cursor position. * * Bitfields for character's display attribute: * Bit(s) Description * 7 foreground blink * 6-4 background color * 3 foreground bright * 2-0 foreground color * * Values for character color: * Normal Bright * 000b black dark gray * 001b blue light blue * 010b green light green * 011b cyan light cyan * 100b red light red * 101b magenta light magenta * 110b brown yellow * 111b light gray white * * BIOS call "INT 10H Function 08h" to read character and attribute data * Call with %ah = 0x08 * %bh = page * Returns %ah = character attribute * %al = character value * BIOS call "INT 10H Function 09h" to write character and attribute data * Call with %ah = 0x09 * %al = character value * %bh = page * %bl = character attribute * %cx = count to display (???, possible side-effects!!) */ ENTRY(set_attrib) push %ebp push %eax push %ebx push %ecx movl 0x14(%esp), %ecx xorl %ebx, %ebx call EXT_C(prot_to_real) .code16 movb $0x8, %ah int $0x10 movb $0x9, %ah movb %cl, %bl movw $1, %cx int $0x10 data32 call EXT_C(real_to_prot) .code32 pop %ecx pop %ebx pop %eax pop %ebp ret /* * 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 push %ecx push %edx call EXT_C(prot_to_real) /* enter real mode */ .code16 movb $0x2, %ah int $0x1a data32 jnc gottime movb $0xff, %dh gottime: data32 call EXT_C(real_to_prot) .code32 movb %dh, %al pop %edx pop %ecx pop %ebp ret /* * asm_getkey() * BIOS call "INT 16H Function 00H" to read character from keyboard * Call with %ah = 0x0 * Return: %ah = keyboard scan code * %al = ASCII character */ ENTRY(asm_getkey) push %ebp push %ebx /* save %ebx */ call EXT_C(prot_to_real) .code16 int $0x16 movw %ax, %bx /* real_to_prot uses %eax */ data32 call EXT_C(real_to_prot) .code32 movw %bx, %ax pop %ebx pop %ebp ret /* * checkkey() * 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(checkkey) push %ebp push %ebx xorl %ebx, %ebx call EXT_C(prot_to_real) /* enter real mode */ .code16 movb $0x1, %ah int $0x16 data32 jz notpending movw %ax, %bx data32 jmp pending notpending: movl $0xFFFFFFFF, %ebx pending: data32 call EXT_C(real_to_prot) .code32 mov %ebx, %eax pop %ebx pop %ebp ret #endif /* NO_FANCY_STUFF */ /* * This is the area for all of the special variables. */ .p2align 2 /* force 4-byte alignment */ protstack: .long PROTSTACKINIT VARIABLE(boot_drive) .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 /* * 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. */ .p2align 2 /* force 4-byte alignment */ 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 */