blob: bd57e6dd565967e27ec35dc283d4efcd55c84602 [file] [log] [blame] [raw]
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1996 Erich Boleyn <erich@uruk.org>
*
* 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 VERSION
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(write, drive, geometry, sector, nsec, segment)
* Read/write "nsec" sectors from disk to real segment "segment"
* offset zero
*
* Will try INT 0x13 LBA (AH=0x42/0x43) or standard (AH=0x2/0x3)
* depending on probed geometry. For addresses out of the translated
* area, it returns BIOS_GEOMETRY_ERROR.
*/
ENTRY(biosdisk)
push %ebp
mov %esp, %ebp
push %ebx
push %ecx
push %edx
push %esi
/* Check whether we have LBA. */
movb 0x13(%ebp), %al
shrb $4, %al
jz disk_compute_args /* nope. */
/* set up disk address packet for extended calls (LBA mode) */
movl 0x14(%ebp), %eax /* absolute block number */
movl %eax, dap_block
xorl %eax, %eax
movl %eax, dap_block + 4
movb 0x18(%ebp), %al /* number of sectors */
movw %ax, dap_blocks
/* set up the buffer address */
xorl %eax, %eax
movw 0x1c(%ebp), %ax /* segment */
shll $4, %eax
movl %eax, dap_buffer
movl $disk_address_packet, %esi
xorb %bl, %bl
movb 0x8(%ebp), %bh /* read=0, write=1 */
addb $0x42, %bh /* convert to extended subfunction */
movb 0xc(%ebp), %dl /* drive */
call EXT_C(prot_to_real) /* enter real mode */
.code16
movw %bx, %ax
int $0x13 /* do the operation */
/* set success return value */
movb $0, %bl
/* did we actually succeed? */
data32
jnc disk_exit_16
data32
call EXT_C(real_to_prot) /* back to protected mode */
.code32
/* either failed, or a floppy, so try standard BIOS calls */
disk_compute_args:
/*
* GEOMETRY is a longword representing the BIOS geometry:
* 4 bit BIOS extension (0 = none, 1 = LBA)
* 12 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
andb $0xf, %bh /* mask out bios extension code */
cmpl %ebx, %eax
/* if not, go on to standard read function */
jle disk_use_standard_bios
movl $BIOSDISK_ERROR_GEOMETRY, %eax
jmp disk_exit_32
disk_address_packet:
.align 4
.byte 0x20 /* length of packet */
.byte 0 /* reserved */
dap_blocks:
.word 0 /* number of blocks */
dap_buffer:
.long 0 /* buffer address */
dap_block:
.long 0 /* absolute block number */
.long 0
/*
* This portion implements the BIOS standardized
* INT 0x13/AH=<subfunc> interface.
*/
disk_use_standard_bios:
shll $8, %edx /* get head number to %dh */
xchgl %eax, %ecx /* cylinder to %cx, sector to %al */
/* FIXME: cylinder; bits 10,11 of cyl are in %dh */
/* cylinder; bits 8,9 of cyl are in %cl */
xchgb %ch, %cl
shlb $6, %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 /* read=0, write=1 */
addb $0x2, %bh /* convert to 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
xorb %bh, %bh /* no BIOS extensions */
data32
jmp probe_success
probe_values:
.byte 36, 18, 15, 9, 0
hard_drive:
/* Check for LBA. */
movb $0x41, %ah
movw $0x55aa, %bx
int $0x13 /* int 13 extensions install check */
data32
jc 1f /* invalid function */
cmpw $0xaa55, %bx
data32
jnz 1f /* failed magic */
andb $1, %cx
data32
jnz 1f /* LBA not supported */
/* Wahoo! Got LBA! */
movb $0x1, %bh
jmp 2f
1: xorb %bh, %bh /* Too bad, no LBA */
2: movb $0x8, %ah /* ask for disk info */
int $0x13
data32
jc probe_failed
/* es:di = parameter table */
/* carry = 0 */
probe_success:
/*
* form a dword representing all this gunk:
* 4 bit BIOS extension (0 = none, 1 = LBA)
* 12 bit cylinder
* 8 bit head
* 8 bit sector
*/
movb %bh, %ah /* restore BIOS extensions bits */
movb %dh, %al /* bits 10,11 of cylinder count */
andb $0xc0, %eax
shlw $2, %ax /* << 2 */
movb %cl, %al /* bits 8,9 of cylinder count */
andb $0xc0, %al
shlw $2, %ax /* << 2 */
movb %ch, %al /* Lower 8 bits */
shll $16, %eax /* << 16 */
andb $0x3f, %ah /* mask of cylinder gunk */
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 */