asm.S revision 7ce76caa61769eef87a2368b9ef90e4661e3f193
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001,2002,2004 Free Software Foundation, Inc.
*
* 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 "shared.h"
#ifdef STAGE1_5
#else
#endif
.file "asm.S"
.text
/* Tell GAS to generate 16-bit instructions so that this code works
in real mode. */
#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.
*/
#endif /* ! STAGE1_5 */
/*
* Guarantee that "main" is loaded at 0x0:0x8200 in stage2 and
* at 0x0:0x2200 in stage1.5.
*/
/*
* Compatibility version number
*
* These MUST be at byte offset 6 and 7 of the executable
* DO NOT MOVE !!!
*/
/*
* This is a special data area 8 bytes from the beginning.
*/
.long 0xFFFFFF
/* This variable is here only because of a historical reason. */
.long 0
.byte 0
#ifndef STAGE1_5
#else /* STAGE1_5 */
.long 0xffffffff
#endif /* STAGE1_5 */
/*
* Leave some breathing room for the config file name.
*/
.byte 0xEE
.byte 0
/*
* installgrub will place the pkg_version here
*/
/* the real mode code continues... */
cli /* we're not safe here! */
/* set up %ds, %ss, and %es */
#ifndef SUPPORT_DISKLESS
/*
* Save the sector number of the second sector (i.e. this sector)
*/
#endif
sti /* we're safe again */
#ifndef SUPPORT_DISKLESS
/* save boot drive reference */
/* reset disk system (%ah = 0) */
int $0x13
#endif
/* transition to protected mode */
/* The ".code32" directive takes GAS out of 16-bit mode. */
/* clean out the bss */
/* set %edi to the bss starting address */
#if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL)
#elif defined(HAVE_USCORE_EDATA_SYMBOL)
#elif defined(HAVE_EDATA_SYMBOL)
#endif
/* set %ecx to the bss end */
#if defined(HAVE_END_SYMBOL)
#elif defined(HAVE_USCORE_END_SYMBOL)
#endif
/* compute the bss length */
/* zero %al */
/* set the direction */
/* clean out */
/*
* Call the start of main body of C code, which does some
* of it's own initialization before transferring to "cmain".
*/
/*
* This call is special... it never returns... in fact it should simply
* hang at this point!
*/
/*
* 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.
*/
#ifndef STAGE1_5
/**************************************************************************
UNDI_CALL - wrapper around real-mode UNDI API calls
**************************************************************************/
cld /* Don't know whether or not we need this */
/* but pxelinux includes it for some reason, */
/* so we put it in just in case. */
/**************************************************************************
UNDI_IRQ_HANDLER - UNDI IRQ handler: calls PXENV_UNDI_ISR and send EOI
NOTE: For some reason, this handler needs to be aligned. Else, the
undi driver won't get the trigger count on some platforms.
**************************************************************************/
.align 4
/* set funcflag to PXENV_UNDI_ISR_IN_START */
/* push pxenv_undi_isr struct on stack */
cld /* Don't know whether or not we need this */
/* but pxelinux includes it for some reason, */
/* so we put it in just in case. */
jne 3f
jne 3f
/* send EOI -- non specific for now */
jg 2f
/* increment trigger count */
/* restore other registers */
.word 0
.long 0
.byte 0
.byte 0
.word 0 /* offset */
.word 0 /* segment */
.word 0 /* status */
.word 0 /* funcflag */
.long 0 /* struct padding not used by ISR */
.long 0
.long 0
/*
* stop_floppy()
*
* Stops the floppy drive from spinning, so that other software is
* jumped to with a known state.
*/
int $0x13
/*
* grub_reboot()
*
* Reboot the system. At the moment, rely on BIOS.
*/
/* cold boot */
/*
* 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.
*/
/* get the argument */
/* see if zero */
/* detect APM */
int $0x15
/* don't check %bx for buggy BIOSes... */
/* disconnect APM first */
int $0x15
/* connect APM */
int $0x15
/* set APM protocol level - 1.1 or bust. (this covers APM 1.2 also) */
int $0x15
/* set the power state to off */
int $0x15
/* shouldn't reach here */
/*
* track_int13(int drive)
*
* Track the int13 handler to probe I/O address space.
*/
/* copy the original int13 handler segment:offset */
/* replace the int1 handler */
/* read the MBR to call int13 successfully */
/* save FLAGS on the stack to emulate int13 */
/* set the TF flag */
/* FIXME: this can be simplified not to use AX */
.word 0 /* offset */
.word 0 /* segment */
/* TF is cleared here automatically */
/* restore the int1 handler */
/*
* Check if the next instruction is I/O, and if this is true, add the
* port into the io map.
*
* Note: Probably this will make the execution of int13 very slow.
*
* is impossible to detect memory-mapped I/O.
*/
/* IP */
/* CS */
/* examine the next instruction */
/* skip this code if it is a prefix */
je 1b
je 1b
je 1b
je 1b
jl 2f
jle 1b
jl 3f
jle 1b
3: /* check if this code is out* or in* */
/* ins? or outs? */
jl 4f
jle 5f
4: /* in? or out? (register operand version) */
jl 6f
jle 5f
6: /* in? or out? (immediate operand version) */
jl 8f
jg 8f
7: /* immediate has a port */
5: /* %dx has a port */
/* set %ds to zero */
/* set %si to the io map */
9: /* check if the io map already has the port */
/* check if this is the end */
jz 1f
/* check if this matches the port */
jne 9b
/* if so, leave from this handler */
jmp 8f
1: /* check for the buffer overrun */
je 8f
/* add the port into the io map */
8: /* restore registers */
/*
* set_int15_handler(void)
*
* Set up int15_handler.
*/
/* save the original int15 handler */
/* save the new int15 handler */
/*
* unset_int15_handler(void)
*
* Restore the original int15 handler
*/
/* check if int15_handler is set */
jne 1f
jne 1f
/* restore the original */
1:
/*
* Translate a key code to another.
*
* Note: This implementation cannot handle more than one length
* scancodes (such as Right Ctrl).
*/
/* if non-carrier, ignore it */
jnc 1f
/* check if AH=4F */
jne 1f
/* E0 and E1 are special */
je 4f
/* this flag is actually the machine code (je or jmp) */
je 4f
/* save bits 0-6 of %al in %dl */
/* save the highest bit in %bl */
/* set %ds to 0 */
/* set %si to the key map */
/* find the key code from the key map */
2:
/* check if this is the end */
jz 3f
/* check if this matches the key code */
jne 2b
/* if so, perform the mapping */
3:
/* restore %ax */
/* make sure that CF is set */
/* restore other registers */
4:
/* tricky: jmp (0x74) <-> je (0xeb) */
1:
/* just cascade to the original */
/* ljmp */
.byte 0xea
int15_offset: .word 0
int15_segment: .word 0
.align 4
/*
* set_int13_handler(map)
*
* Copy MAP to the drive map and set up int13_handler.
*/
/* copy MAP to the drive map */
/* save the original int13 handler */
/* decrease the lower memory size and set it to the BIOS memory */
/* compute the segment */
/* save the new int13 handler */
/* copy int13_handler to the reserved area */
/*
* Map a drive to another drive.
*/
/* set %si to the drive map */
/* find the drive number from the drive map */
1:
/* check if this is the end */
jz 2f
/* check if this matches the drive number */
jne 1b
/* if so, perform the mapping */
2:
/* restore %si */
/* save %ax in the stack */
/* simulate the interrupt call */
/* set %ax and %bp to the original values */
/* lcall */
.byte 0x9a
int13_offset: .word 0
int13_segment: .word 0
/* save flags */
/* restore %bp */
/* save %ax */
/* set the flags in the stack to the value returned by int13 */
/* check if should map the drive number */
jne 3f
jne 3f
/* check if the mapping was performed */
jz 3f
/* perform the mapping */
3:
.align 4
/*
* chain_stage1(segment, offset, part_table_addr)
*
* This starts another stage1 loader, at segment:offset.
*/
/* no need to save anything, just use %esp */
/* store %ESI, presuming %ES is 0 */
/* store new offset */
/* store new segment */
/* set up to pass boot drive */
#ifdef ABSOLUTE_WITHOUT_ASTERISK
#else
#endif
#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.
*/
/* no need to save anything, just use %esp */
/* store new offset */
/* store new segment */
/* generate linear address */
/* set up to pass the partition where stage2 is located in */
/* set up to pass the drive where stage2 is located in */
/* set up to pass the second sector of stage2 */
#ifdef ABSOLUTE_WITHOUT_ASTERISK
#else
#endif
#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.
*/
/* load the GDT register */
/* turn on protected mode */
/* jump to relocation, flush prefetch queue, and reload %cs */
/*
* The ".code32" directive only works in GAS, the GNU assembler!
* This gets out of "16-bit" mode.
*/
/* reload other segment registers */
/* put the return address in a known safe location */
/* get protected mode stack */
/* get return address onto the right stack */
/* zero %eax */
/* return on the old (or initialized) stack! */
/* just in case, set GDT */
/* save the protected mode stack */
/* get the return address */
/* set up new stack */
/* set up segment limits */
/* this might be an extra step */
/* clear the PE bit of CR0 */
/* flush prefetch queue, reload %cs */
/* we are in real mode now
* set up the real mode segment registers : DS, SS, ES
*/
/* zero %eax */
/* restore interrupts */
/* return on new stack! */
/*
* int biosdisk_int13_extensions (int ax, int drive, void *dap)
*
* is passed for disk address packet. If an error occurs, return
* non-zero, otherwise zero.
*/
/* compute the address of disk_address_packet */
/* drive */
/* ax */
/* enter real mode */
int $0x13 /* do the operation */
/* clear the data segment */
/* back to protected mode */
/*
* int biosdisk_standard (int ah, int drive, int coff, int hoff, int soff,
* int nsec, int segment)
*
* return non-zero, otherwise zero.
*/
/* set up CHS information */
/* drive */
/* segment */
/* save nsec and ah to %di */
/* enter real mode */
1:
int $0x13 /* do the operation */
/* if fail, reset the disk system */
int $0x13
je 2f
2:
/* back to protected mode */
/*
* int check_int13_extensions (int drive)
*
* Check if LBA is supported for DRIVE. If it is supported, then return
* the major version of extensions, otherwise zero.
*/
/* drive */
/* enter real mode */
int $0x13 /* do the operation */
/* check the result */
jc 1f
jne 1f
/* check if AH=0x42 is supported if FORCE_LBA is zero */
jnz 2f
jnz 2f
1:
2:
/* back to protected mode */
/*
* int get_diskinfo_standard (int drive, unsigned long *cylinders,
* unsigned long *heads, unsigned long *sectors)
*
* Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
* error occurs, then return non-zero, otherwise zero.
*/
/* drive */
/* enter real mode */
int $0x13 /* do the operation */
/* check if successful */
jnz 1f
/* bogus BIOSes may not return an error number */
/* XXX 0x60 is one of the unused error numbers */
1:
/* back to protected mode */
/* restore %ebp */
/* heads */
/* sectors */
/* cylinders */
counted from zero */
#if 0
/*
* int get_diskinfo_floppy (int drive, unsigned long *cylinders,
* unsigned long *heads, unsigned long *sectors)
*
* Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
* error occurs, then return non-zero, otherwise zero.
*/
/* drive */
/* enter real mode */
/* init probe value */
1:
int $0x13 /* reset floppy controller */
je 2f
/* perform read */
int $0x13
/* FIXME: Read from floppy may fail even if the geometry is correct.
So should retry at least three times. */
/* succeed */
jmp 2f
2:
/* back to protected mode */
/* restore %ebp */
/* cylinders */
/* heads */
/* sectors */
/* return value in %eax */
jne 3f
3:
#endif
/* Source files are splitted, as they have different copyrights. */
#ifndef STAGE1_5
# include "setjmp.S"
# include "apm.S"
#endif /* ! STAGE1_5 */
#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.
*/
/* will be the end of the bss */
# if defined(HAVE_END_SYMBOL)
# elif defined(HAVE_USCORE_END_SYMBOL)
# endif
#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.
*
*/
int $0x12
xext:
int $0x15
#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.
*
*/
int $0x15
/*
*
* 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.
*/
/* place address (+4) in ES:DI */
/* set continuation value */
/* set default maximum buffer size */
/* set EDX to 'SMAP' */
int $0x15
/* write length of buffer (zero if error) into "addr" */
/* set return value to continuation */
/*
* get_rom_config_table()
*
* Get the linear address of a ROM configuration table. Return zero,
* if fails.
*/
/* zero %ebx for simplicity */
int $0x15
/* compute the linear address */
/*
* int get_vbe_controller_info (struct vbe_controller *controller_ptr)
*
* Get VBE controller information.
*/
/* Convert the linear address to segment:offset */
int $0x10
/*
* int get_vbe_mode_info (int mode_number, struct vbe_mode *mode_ptr)
*
* Get VBE mode information.
*/
/* Convert the linear address to segment:offset */
/* Save the mode number in %cx */
int $0x10
/*
* int set_vbe_mode (int mode_number)
*
* Set VBE mode. Don't support user-specified CRTC information.
*/
/* Save the mode number in %bx */
/* Clear bit D11 */
int $0x10
/*
* 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. :-(
*/
/* first, try a BIOS call */
jz 1f
1: stc
int $0x15
jnc 2f
/* set non-zero if failed */
/* save the status */
jnz 3f
3: /*
* try to switch gateA20 using PORT92, the "Fast A20 and Init"
* register
*/
/* skip the port92 code if it's unimplemented (read returns 0xff) */
jz 6f
/* set or clear bit1, the ALT_A20_GATE bit */
jz 4f
jmp 5f
/* clear the INIT_NOW bit; don't accidently reset the machine */
6: /* use keyboard controller */
/* output a dummy command (USB keyboard hack) */
/*
* linux_boot()
*
* Does some funky things (including on the stack!), then jumps to the
* entry point of the Linux setup code.
*/
.long 0
.long 0
.long 0
/* don't worry about saving anything, we're committed at this point */
cld /* forward copying */
/* copy kernel */
/* copy the real mode part */
/* change %ebx to the segment address */
/* XXX new stack pointer in safe area for calling functions */
/* final setup for linux boot */
/* final setup for linux boot */
/* jump to start */
/* ljmp */
.byte 0xea
.word 0
.word 0
/*
* multi_boot(int start, int mb_info)
*
* This starts a kernel in the manner expected of the multiboot standard.
*/
/* no need to save anything */
/* boot kernel here (absolute address call) */
/* error */
#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.
*/
#ifdef STAGE1_5
#else
#endif
#ifndef STAGE1_5
/* use teletype output if control character */
je 1f
je 1f
je 1f
je 1f
/* save the character and the attribute on the stack */
/* get the current position */
int $0x10
/* check the column with the width */
jl 2f
/* print CR and LF, if next write will exceed the width */
int $0x10
int $0x10
/* get the current position */
int $0x10
2:
/* restore the character and the attribute */
/* write the character with the attribute */
int $0x10
/* move the cursor forward */
int $0x10
jmp 3f
#endif /* ! STAGE1_5 */
int $0x10
#ifndef STAGE1_5
/* this table is used in translate_keycode below */
.word 0
/*
* translate_keycode translates the key code %dx to an ascii code.
*/
1: lodsw
/* check if this is the end */
jz 2f
/* load the ascii code into %ax */
/* check if this matches the key code */
jne 1b
/* translate %dx, if successful */
/*
* remap_ascii_char remaps the ascii code %dl to another if the code is
* contained in ASCII_KEY_MAP.
*/
1:
/* check if this is the end */
jz 2f
/* check if this matches the ascii code */
jne 1b
/* if so, perform the mapping */
2:
/* restore %si */
.align 4
/*
* 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
*/
int $0x16
/*
* 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
*/
int $0x16
/*
* 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)
*/
int $0x10 /* get cursor position */
/*
* 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)
*/
int $0x10 /* set cursor position */
/*
* 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)
*/
/* move the cursor to the beginning */
int $0x10
/* write spaces to the entire screen */
int $0x10
/* move back the cursor */
int $0x10
/*
* 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
*/
.byte 1
.word 0
/* check if the standard cursor shape has already been saved */
jne 1f
int $0x10
1:
/* set %cx to the designated cursor shape */
jz 2f
2:
int $0x10
/* graphics mode functions */
#ifdef SUPPORT_GRAPHICS
.word 0
.word 0
.word 0
.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.
*/
int $0x10 /* Get Current Video mode */
int $0x10 /* Set Video mode */
/*
* unsigned char * graphics_get_font()
* BIOS call "INT 10H Function 11h" to set font
* Call with %ah = 0x11
*/
int $0x10
/*
* 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)
*/
/* wait vertical retrace */
int $0x10
#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)
*/
int $0x1a
/*
* currticks()
* return the real time in ticks, of which there are about
* 18-20 per second
*/
/* %ax is already zero */
int $0x1a
/*
* Based on code from AMD64 Volume 3
*/
popf /* save new %eax to EFLAGS */
pushf /* save new EFLAGS */
jne 1f
1:
int $0x15
/* XXX still need to pass back return */
#endif /* ! STAGE1_5 */
/*
* This is the area for all of the special variables.
*/
.long PROTSTACKINIT
#ifdef SUPPORT_DISKLESS
.long NETWORK_DRIVE
#else
.long 0
#endif
.long 0
/* an address can only be long-jumped to if it is in memory, this
is used by multiple routines */
.long 0x8000
.word 0
.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 */
/*
* 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.
*/
gdt:
.word 0, 0
.byte 0, 0, 0, 0
/* code segment */
.word 0xFFFF, 0
/* data segment */
.word 0xFFFF, 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 */
.long gdt /* addr */