blob: ea42fb8c4be6c80111964ca1852c0cab615bc66f [file] [log] [blame] [raw]
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001,2002,2004 Free Software Foundation, Inc.
*
* 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.
*/
/*
* 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
# define ABS(x) ((x) - EXT_C(main) + 0x2200)
#else
# define ABS(x) ((x) - EXT_C(main) + 0x8200)
#endif
.file "asm.S"
.text
/* Tell GAS to generate 16-bit instructions so that this code works
in real mode. */
.code16
#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.
*/
.globl start, _start
start:
_start:
#endif /* ! STAGE1_5 */
ENTRY(main)
/*
* Guarantee that "main" is loaded at 0x0:0x8200 in stage2 and
* at 0x0:0x2200 in stage1.5.
*/
ljmp $0, $ABS(codestart)
// /* control byte
// * Currently only bit 0 is used.
// * bit 0 disable the "unconditional command-line entrance" feature
// */
//
// . = EXT_C(main) + 0x5
// .byte 0
/*
* Compatibility version number
*
* These MUST be at byte offset 6 and 7 of the executable
* DO NOT MOVE !!!
*/
. = EXT_C(main) + 0x6
.byte COMPAT_VERSION_MAJOR, COMPAT_VERSION_MINOR
/*
* This is a special data area 8 bytes from the beginning.
*/
. = EXT_C(main) + 0x8
VARIABLE(install_partition)
.long 0xFFFFFF
/* This variable is here only because of a historical reason. */
VARIABLE(saved_entryno)
#if defined(STAGE1_5) /* || ! defined(PRESET_MENU_STRING) */
.long 0
#else
/* Note: GRUB for DOS uses this for the commandline preset_menu.
* A preset_menu can be embedded in the commandline of GRUB.EXE.
* This new preset_menu overrides the built-in preset_menu.
* If the variable is not touched, and the first byte at config_file is 0,
* then the new menu at 0x0800 will work.
* If the variable here is cleared to 0, or the first byte at config_file is
* not 0, then the built-in preset_menu will work.
*
* Do NOT change this variable to other value than 0.
*/
.long preset_menu
#endif
VARIABLE(stage2_id)
.byte STAGE2_ID
VARIABLE(force_lba)
.byte 0
VARIABLE(version_string)
.string VERSION
VARIABLE(config_file)
#ifndef STAGE1_5
.string "/boot/grub/menu.lst"
#else /* STAGE1_5 */
.long 0xffffffff
.string "/boot/grub/stage2"
#endif /* STAGE1_5 */
/*
* Leave some breathing room for the config file name.
*/
. = EXT_C(main) + 0x6C #; bss starting address
#ifndef STAGE1_5
#if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL)
//.word (__bss_start - main) & 0x0F, (__bss_start - main) >> 4
.long __bss_start
#elif defined(HAVE_USCORE_EDATA_SYMBOL)
//.word (_edata - main) & 0x0F, (_edata - main) >> 4
.long _edata
#elif defined(HAVE_EDATA_SYMBOL)
//.word (edata - main) & 0x0F, (edata - main) >> 4
.long edata
#else
#error no bss starting address
#endif
#endif
. = EXT_C(main) + 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, %es
movw %ax, %ss
movl $STACKOFF, %esp
#ifndef STAGE1_5
movb 0x0410, %al
movb %al, ABS(floppies_orig)
movb 0x0475, %al
movb %al, ABS(harddrives_orig)
//movb $((int13_handler_end - int13_handler + 0x3ff) / 0x400), ABS(int13_handler)
movl 0x4C, %eax
movl %eax, ABS(int13_offset)
movl %eax, ABS(int13_offset) + 0x113 - 0x1C
cmpl $0xC0000000, %eax
jnb 1f
cmpl $0x9A000000, %eax
jb 1f
andl $0x3FFFFF, %eax
cmpl $0x100, %eax
jnz 1f
movw 0x413, %ax
shlw $6, %ax
cmpw 0x4E, %ax
jne 1f
movw %ax, %ds /* DS=old int13 code segment */
movl 0x1C, %eax /* ROM int 13 vector */
cmpl $0x9A000000, %eax
jb 2f
movl 0x0C, %eax /* ROM int 15 vector */
cmpl $0x9A000000, %eax
jb 2f
/* restore old emu data, except the first byte of handler size. */
movw $(0x140 - 1), %cx
movw $1, %si
movw $ABS(int13_handler + 1), %di
cld
repz movsb
///* restore old int13_dup */
//movl (int13_dup - int13_handler), %eax
//movl %eax, %es:ABS(int13_dup)
///* restore old safe_mbr_hook */
//movl (safe_mbr_hook - int13_handler), %eax
//movl %eax, %es:ABS(safe_mbr_hook)
2:
xorw %ax, %ax
movw %ax, %ds /* DS=0 */
1:
movl ABS(ROM_int15), %eax
cmpl $0x9A000000, %eax
jnb 1f
movl 0x0054, %eax
movl %eax, ABS(ROM_int15)
1:
/* check the BIOS type (currently only for Bochs) */
movw $0xF000, %ax
movw %ax, %es /* ES=0xF000 */
movw $0xFF00, %di
movw $ABS(bochs_copygrght_string), %si
movw $0x22, %cx
repz cmpsw
setz ABS(bios_id) /* 1 for bochs, 0 for unknown. */
xorw %ax, %ax
movw %ax, %es /* ES=0 */
#endif /* STAGE1_5 */
#ifndef SUPPORT_DISKLESS
/*
* Save the sector number of the second sector (i.e. this sector)
* in INSTALL_SECOND_SECTOR. See also "stage2/start.S".
*/
ADDR32 movl %ebp, EXT_C(install_second_sector)
#endif
///* set up the real mode/BIOS stack */
//movl $STACKOFF, %ebp
//movl %ebp, %esp
/* for usb keyboard hack here, disable interrupt */
//sti /* we're safe again */
/* the active mouse will hang the machine */
#if 0
cli
pushal
pushw %ds
pushw %es
pushw %cs
call 1f
popw %es
popw %ds
popal
jmp 2f
1:
pushw $0xADDA
//ljmp $0xF000, $0x98E1
ljmp $0xF000, $0xA5F2
2:
ljmp $0xF000, $0xFFF0
#endif
pushw %dx
/* qemu-0.8.0 could hang on mouse init here. */
#if 0
/* reset mouse */
movw $0xC201, %ax
int $0x15
#endif
#if 0
/* disable mouse */
movw $0xC200, %ax
xorw %bx, %bx /* BH=0 means disable */
int $0x15
#endif
#if 0
/* set mouse handler address */
movw $0xC207, %ax
xorw %bx, %bx /* ES:BX=0000:0000 to cancel the handler */
int $0x15
#endif
#if 0
/* disable monitor clock (Watch-Dog) */
movw $0xC300, %ax
int $0x15
#endif
#if 0
/* restart all adaptors */
movb $0xFF, %al
outb %al, $0x96
xorw %cx, %cx
1: loop 1b
movb $0xF0, %al
outb %al, $0x96
xorw %cx, %cx
1: loop 1b
movb $0x00, %al
outb %al, $0x96
xorw %cx, %cx
1: loop 1b
/* initialize all adaptors */
movw $0xC000, %bx
2:
movw %bx, %ds
xorw %si, %si
lodsw
cmpw $0xAA55, %ax
jne 1f
xorw %ax, %ax
lodsb #; ROM size in sectors
addw $3, %ax
andw $0xFFFC, %ax
subw $4, %ax
shlw $5, %ax
pushaw
pushw %bx
pushw $3
movw %sp, %bp
lcall *(%bp)
popw %bx
popw %bx
popaw
1:
addw $0x80, %ax
addw %ax, %bx
cmpw $0xF000, %bx
jb 2b
#endif
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
#if 0
/* cancel alarm clock */
movb $0x07, %ah
int $0x1A
#endif
/* clear VDS */
andb $0xD7, 0x47B
popw %dx
cli
#ifndef SUPPORT_DISKLESS
/* save boot drive reference */
ADDR32 movb %dl, EXT_C(boot_drive)
movw $ABS(reset_disk_string),%si
call print_message /* will not change DX */
xorw %ax, %ax
/* reset disk system (%ah = 0) */
#ifdef STAGE1_5
int $0x13
#else
call safe_int13
#endif
movw $ABS(reset_disk_failure_string),%si
jc 1f
movw $ABS(reset_disk_success_string),%si
1:
call print_message /* will not change DX */
#endif
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
#ifndef STAGE1_5
movl $1, %ebx
/* check the DUCE indicator */
cmpl $0x45435544, 0x5FC
jz 1f
/* checkkey 'c' */
pushl %ebx
movb $0x01, %ah
int $0x16
popl %ebx
jz 1f /* no keypress */
orb $0x20, %al
cmpb $0x63, %al
jne 1f /* the key is not 'c' */
/* bypass all config files */
movl $0, %ebx
1:
#endif
/* transition to protected mode */
DATA32 call EXT_C(real_to_prot)
/* The ".code32" directive takes GAS out of 16-bit mode. */
.code32
#ifndef STAGE1_5
testl %ebx, %ebx
jnz 1f
movl %ebx, EXT_C(use_config_file)
1:
#endif
/* clean out the bss */
/* set %edi to the bss starting address */
#if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL)
movl $__bss_start, %edi
#elif defined(HAVE_USCORE_EDATA_SYMBOL)
movl $_edata, %edi
#elif defined(HAVE_EDATA_SYMBOL)
movl $edata, %edi
#else
#error no bss starting address
#endif
/* set %ecx to the bss end */
#if defined(HAVE_END_SYMBOL)
movl $end, %ecx
#elif defined(HAVE_USCORE_END_SYMBOL)
movl $_end, %ecx
#else
#error no bss ending address
#endif
/* compute the bss length */
subl %edi, %ecx
/* zero %al */
xorb %al, %al
/* set the direction */
cld
/* clean out */
rep
stosb
/* setup the new preset_menu */
//movb config_file, %al
//testb %al, %al
cmpb %al, config_file /* AL == 0 */
jnz 1f
movl saved_entryno, %ebx
testl %ebx, %ebx
jz 1f
movl $0x0800, (%ebx)
1:
/*
* 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.
*/
/* No external program ever calls HARD_STOP. HARD_STOP is only called
* by the asm.S itself, and all calls are from real mode. So we
* could(and should) use .code16 here clearly.
*/
.code16
//ENTRY(hard_stop)
hard_stop:
sti
hlt
//jmp EXT_C(hard_stop)
jmp hard_stop
.code16
/* real mode print string */
/* prints string DS:SI (modifies AX BX SI) */
print_message:
1:
cld
lodsb (%si), %al /* get token */
xorw %bx, %bx /* video page 0 */
movb $0x0e, %ah /* print it */
int $0x10 /* via TTY mode */
cmpb $0, %al /* end of string? */
jne 1b /* until done */
ret
reset_disk_string:
.ascii "Reseting the boot drive... \0"
reset_disk_success_string:
.ascii "Success.\r\n\0"
reset_disk_failure_string:
.ascii "Failure!\r\n\0"
bochs_copygrght_string:
.ascii "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team.\0"
ENTRY(bios_id)
.long 0 /* 1 for bochs, 0 for unknown. */
.code32
#ifndef STAGE1_5
/*
* stop_floppy()
*
* Stops the floppy drive from spinning, so that other software is
* jumped to with a known state.
*/
ENTRY(stop_floppy)
pusha
call EXT_C(prot_to_real)
.code16
sti #; added 2006-11-30
xorb %dl, %dl
#ifdef STAGE1_5
int $0x13
#else
call safe_int13
#endif
DATA32 call EXT_C(real_to_prot)
.code32
popa
ret
/*
* grub_reboot()
*
* Reboot the system. At the moment, rely on BIOS.
*/
ENTRY(grub_reboot)
call EXT_C(prot_to_real)
.code16
/* cold boot */
//sti /* needn't enable interrupt here. comment it out */
movw $0x0472, %di
movw %ax, (%di)
ljmp $0xFFFF, $0x0000
.code32
/*
* 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.
*/
ENTRY(grub_halt)
/* get the argument */
movl 4(%esp), %eax
/* see if zero */
testl %eax, %eax
jnz EXT_C(stop)
call EXT_C(prot_to_real)
.code16
//sti /* this is not needed here, so comment it out. */
sti #; added 2006-11-30
/* detect APM */
movw $0x5300, %ax
xorw %bx, %bx
int $0x15
//jc EXT_C(hard_stop)
jc hard_stop
/* don't check %bx for buggy BIOSes... */
/* disconnect APM first */
movw $0x5304, %ax
xorw %bx, %bx
int $0x15
/* connect APM */
movw $0x5301, %ax
xorw %bx, %bx
int $0x15
//jc EXT_C(hard_stop)
jc hard_stop
/* set APM protocol level - 1.1 or bust. (this covers APM 1.2 also) */
movw $0x530E, %ax
xorw %bx, %bx
movw $0x0101, %cx
int $0x15
//jc EXT_C(hard_stop)
jc hard_stop
/* set the power state to off */
movw $0x5307, %ax
movw $1, %bx
movw $3, %cx
int $0x15
/* shouldn't reach here */
//jmp EXT_C(hard_stop)
jmp hard_stop
/*
* int check_64bit (void)
*
* Checks whether 64-bit mode is supported
*
* Stolen from a patch originaly intended for syslinux
* (http://syslinux.zytor.com/archives/2007-January/007832.html)
*
* Copyright (C) 2007 Byron Stanoszek <gandalf@winds.org>
*
* Adapted to AT&T syntax by Robert Millan <rmh@aybabtu.com>
*/
ENTRY(check_64bit)
.code32
pushl %ebp
pushl %ebx
pushl %edx
/* Check if this CPU supports the CPUID command */
pushfl
pushfl
popl %eax
movl %eax, %ebx
xorl $(1 << 21), %eax // CPUID bit
pushl %eax
popfl
pushfl
popl %eax
popfl // Restore the original flags
xorl %ebx, %eax
jz is_32bit
/* Now check for the 64-bit flag in the CPU features byte ($0000_0001, edx)
This is bit 30 for Intel CPUs, and bit 29 for AMD CPUs */
movl $0x00000000, %eax // Find last Intel cpuid #
cpuid
cmpl $0x00000000, %eax
je test_amd
movl $0x00000001, %eax // Read Intel CPU flags
cpuid
btl $30, %edx // 64-bit if bit 30 is set
jc is_64bit
test_amd:
movl $0x80000000, %eax // Find last AMD cpuid #
cpuid
cmpl $0x80000000, %eax
jbe is_32bit
movl $0x80000001, %eax // Read AMD CPU flags
cpuid
btl $29, %edx // 64-bit if bit 29 is set
jnc is_32bit
is_64bit:
movl $1, %eax
popl %edx
popl %ebx
popl %ebp
ret
is_32bit:
xorl %eax, %eax
popl %edx
popl %ebx
popl %ebp
ret
/* Catch CPU exceptions 0 - 7
* 0 Divide
* 1 Debug
* 2 NMI
* 3 Break point
* 4 Overflow
* 5 Bound
* 6 Invalid Instruction
* 7 no coprocessor
*/
set_fault_recovery_handler:
.code16
pushfw
pushw %ds
pushw %es
pushaw
/* backup int 00 - 07 */
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
xorw %si, %si
movw $ABS(int_00_07_vectors), %di
movw $16, %cx
cld
repz movsw
/* set to new vector */
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
xorw %si, %si
movw $ABS(int_00_07_vectors), %di
pushl %eax
xorw %di, %di
movl $ABS(fault_recovery_handler), %eax /* 0000:fault_recovery_handler */
movw $8, %cx
cld
repz stosl
popl %eax
popaw
popw %es
popw %ds
popfw
ret
unset_fault_recovery_handler:
.code16
pushfw
pushw %ds
pushw %es
pushaw
/* restore int 00 - 07 */
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
xorw %di, %di
movw $ABS(int_00_07_vectors), %si
movw $16, %cx
cld
repz movsw
popaw
popw %es
popw %ds
popfw
ret
.align 4
int_00_07_vectors:
.space 32
original_registers:
.space 48
safe_int13:
.code16
/* setup our fault recovery handler */
call set_fault_recovery_handler
/* backup old registers. Note: CS=0 */
movw %ds, %cs:ABS(original_registers)
movw %es, %cs:ABS(original_registers) + 4
movw %ss, %cs:ABS(original_registers) + 8
movl %esp, %cs:ABS(original_registers) + 12
movl %eax, %cs:ABS(original_registers) + 16
movl %ebx, %cs:ABS(original_registers) + 20
movl %ecx, %cs:ABS(original_registers) + 24
movl %edx, %cs:ABS(original_registers) + 28
movl %esi, %cs:ABS(original_registers) + 32
movl %edi, %cs:ABS(original_registers) + 36
movl %ebp, %cs:ABS(original_registers) + 40
pushw %bp
pushw %ax
movw %sp, %bp
movw (%bp), %ax
movw %ax, %cs:ABS(original_registers) + 44 #; return IP
popw %ax
popw %bp
int $0x13
call unset_fault_recovery_handler
ret
fault_recovery_handler:
.code16
/* restore old registers. Note: CS=0 */
movw %cs:ABS(original_registers), %ds
movw %cs:ABS(original_registers) + 4, %es
movw %cs:ABS(original_registers) + 8, %ss
movl %cs:ABS(original_registers) + 12, %esp
movl %cs:ABS(original_registers) + 16, %eax
movl %cs:ABS(original_registers) + 20, %ebx
movl %cs:ABS(original_registers) + 24, %ecx
movl %cs:ABS(original_registers) + 28, %edx
movl %cs:ABS(original_registers) + 32, %esi
movl %cs:ABS(original_registers) + 36, %edi
movl %cs:ABS(original_registers) + 40, %ebp
/* stack is available, so we can push and pop. */
pushw %bp
pushw %ax
movw %sp, %bp
movw %cs:ABS(original_registers) + 44, %ax #; return IP
movw %ax, (%bp)
popw %ax
popw %bp
pushl $1 #; CF=1 indicating error
popfl #; CLD, CLI, and many more...
call unset_fault_recovery_handler
ret
/* never come here. */
iret
.code32
/*
* track_int13(int drive)
*
* Track the int13 handler to probe I/O address space.
*/
ENTRY(track_int13)
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %edi
/* copy the original int13 handler segment:offset */
movl $0x4c, %edi
movl (%edi), %eax
movl %eax, track_int13_addr
/* replace the int1 handler */
movl $0x4, %edi
pushl (%edi)
movl $ABS(int1_handler), %eax
movl %eax, (%edi)
/* read the MBR to call int13 successfully */
movb 8(%ebp), %dl
call EXT_C(prot_to_real)
.code16
//sti /* it is not bad keeping interrupt off */
movw $SCRATCHSEG, %ax
movw %ax, %es
xorw %bx, %bx
movw $1, %cx
xorb %dh, %dh
/* save FLAGS on the stack to emulate int13 */
pushfw
/* set the TF flag */
/* FIXME: this can be simplified not to use AX */
pushfw
popw %ax
orw $0x100, %ax
pushw %ax
popfw
movw $0x0201, %ax
.byte 0x9a /* lcall */
track_int13_addr:
.word 0 /* offset */
.word 0 /* segment */
/* TF is cleared here automatically */
DATA32 call EXT_C(real_to_prot)
.code32
/* restore the int1 handler */
movl $0x4, %edi
popl (%edi)
popl %edi
popl %ebx
popl %ebp
ret
/*
* 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.
*
* Note2: In this implementation, all we can know is I/O-mapped I/O. It
* is impossible to detect memory-mapped I/O.
*/
int1_handler:
.code16
pushw %bp
movw %sp, %bp
pushw %ds
pushw %ax
pushw %si
pushw %dx
/* IP */
movw 2(%bp), %si
/* CS */
movw 4(%bp), %ax
movw %ax, %ds
/* examine the next instruction */
1: lodsb (%si), %al
/* skip this code if it is a prefix */
cmpb $0x2E, %al
je 1b
cmpb $0x36, %al
je 1b
cmpb $0x3E, %al
je 1b
cmpb $0x26, %al
je 1b
cmpb $0x64, %al
jl 2f
cmpb $0x67, %al
jle 1b
2: cmpb $0xF0, %al
jl 3f
cmpb $0xF3, %al
jle 1b
3: /* check if this code is out* or in* */
/* ins? or outs? */
cmpb $0x6C, %al
jl 4f
cmpb $0x6F, %al
jle 5f
4: /* in? or out? (register operand version) */
cmpb $0xEC, %al
jl 6f
cmpb $0xEF, %al
jle 5f
6: /* in? or out? (immediate operand version) */
cmpb $0xE4, %al
jl 8f
cmpb $0xE7, %al
jg 8f
7: /* immediate has a port */
lodsb (%si), %al
movzbw %al, %dx
5: /* %dx has a port */
/* set %ds to zero */
xorw %ax, %ax
movw %ax, %ds
/* set %si to the io map */
movw $ABS(EXT_C(io_map)), %si
9: /* check if the io map already has the port */
lodsw (%si), %ax
/* check if this is the end */
testw %ax, %ax
jz 1f
/* check if this matches the port */
cmpw %ax, %dx
jne 9b
/* if so, leave from this handler */
jmp 8f
1: /* check for the buffer overrun */
cmpw $(ABS(EXT_C(io_map)) + (IO_MAP_SIZE + 1) * 2), %si
je 8f
/* add the port into the io map */
movw %dx, -2(%si)
8: /* restore registers */
popw %dx
popw %si
popw %ax
popw %ds
popw %bp
iret
.code32
ENTRY(io_map)
.space (IO_MAP_SIZE + 1) * 2
/*
* set_int15_handler(void)
*
* Set up int15_handler.
*/
ENTRY(set_int15_handler)
pushl %edi
/* save the original int15 handler */
movl $0x54, %edi
movw (%edi), %ax
movw %ax, ABS(int15_offset)
movw 2(%edi), %ax
movw %ax, ABS(int15_segment)
/* save the new int15 handler */
movw $ABS(int15_handler), %ax
movw %ax, (%edi)
xorw %ax, %ax
movw %ax, 2(%edi)
popl %edi
ret
/*
* unset_int15_handler(void)
*
* Restore the original int15 handler
*/
ENTRY(unset_int15_handler)
pushl %edi
/* check if int15_handler is set */
movl $0x54, %edi
movw $ABS(int15_handler), %ax
cmpw %ax, (%edi)
jne 1f
xorw %ax, %ax
cmpw %ax, 2(%edi)
jne 1f
/* restore the original */
movw ABS(int15_offset), %ax
movw %ax, (%edi)
movw ABS(int15_segment), %ax
movw %ax, 2(%edi)
1:
popl %edi
ret
/*
* Translate a key code to another.
*
* Note: This implementation cannot handle more than one length
* scancodes (such as Right Ctrl).
*/
.code16
int15_handler:
/* if non-carrier, ignore it */
jnc 1f
/* check if AH=4F */
cmpb $0x4F, %ah
jne 1f
/* E0 and E1 are special */
cmpb $0xE1, %al
je 4f
cmpb $0xE0, %al
/* this flag is actually the machine code (je or jmp) */
int15_skip_flag:
je 4f
pushw %bp
movw %sp, %bp
pushw %bx
pushw %dx
pushw %ds
pushw %si
/* save bits 0-6 of %al in %dl */
movw %ax, %dx
andb $0x7f, %dl
/* save the highest bit in %bl */
movb %al, %bl
xorb %dl, %bl
/* set %ds to 0 */
xorw %ax, %ax
movw %ax, %ds
/* set %si to the key map */
movw $ABS(EXT_C(bios_key_map)), %si
/* find the key code from the key map */
2:
lodsw
/* check if this is the end */
testw %ax, %ax
jz 3f
/* check if this matches the key code */
cmpb %al, %dl
jne 2b
/* if so, perform the mapping */
movb %ah, %dl
3:
/* restore %ax */
movw %dx, %ax
orb %bl, %al
/* make sure that CF is set */
orw $1, 6(%bp)
/* restore other registers */
popw %si
popw %ds
popw %dx
popw %bx
popw %bp
iret
4:
/* tricky: jmp (0x74) <-> je (0xeb) */
xorb $(0x74 ^ 0xeb), ABS(int15_skip_flag)
1:
/* just cascade to the original */
/* ljmp */
.byte 0xea
int15_offset: .word 0
int15_segment: .word 0
.code32
.align 4
ENTRY(bios_key_map)
.space (KEY_MAP_SIZE + 1) * 2
/*
* set_int13_handler(map)
*
* Copy MAP to the drive map and set up int13_handler.
*/
ENTRY(set_int13_handler)
pushl %ebp
movl %esp, %ebp
pushl %edi
pushl %esi
/* copy MAP to the drive map */
movl $(DRIVE_MAP_SIZE * DRIVE_MAP_SLOT_SIZE / 4), %ecx
movl $ABS(hooked_drive_map), %edi
movl 8(%ebp), %esi
cld
repz movsl
// Now initialized early at the beginning of this file
//
// /* save the original int13 handler */
// movl $0x4c, %edi
// movl (%edi), %eax
// movl %eax, ABS(int13_offset)
/* decrease the lower memory size and set it to the BIOS memory */
movl $0x413, %edi
movl %edi, %esi
lodsw
/* KBytes that int13 handler occupies */
subb ABS(int13_handler), %al
stosw
/* compute the segment(high word) */
shll $(16 + 6), %eax
/* the offset(low word) should be 0x100 */
movw $0x100, %ax
/* save the new int13 handler */
movl $0x4c, %edi
stosl
/* EDI points to the destination int13 handler in the reserved area */
movl %eax, %edi /* the int13 vector just saved */
shrl $12, %edi /* get base address of segment */
/* set ESI to the drive map */
movl $ABS(hooked_drive_map), %esi
movl $(DRIVE_MAP_SIZE), %ecx
1:
cmpb $0xff, 1(%esi) /* Is there a mapped memdrive? */
je 1f /* Yes, hook int15 for e820 memory */
/* try next slot */
addl $DRIVE_MAP_SLOT_SIZE, %esi
loop 1b
jmp 2f /* no memdrives, don't hook int15 */
1:
/* save the new int15 handler */
movw $(int15_e820_handler - int13_handler), %ax /* segment still in high word */
movl %eax, 0x54
2:
/* copy int13_handler to the reserved area */
movl $ABS(int13_handler), %esi
movl $((int13_handler_end - int13_handler) / 4), %ecx
cld
repz movsl
popl %esi
popl %edi
popl %ebp
ret
/* int
* unset_int13_handler(check_status_only)
*
* Restore the original int13 handler
*
* Return 0 for success and non-zero for failure.
*/
ENTRY(unset_int13_handler)
pushl %ebp
movl %esp, %ebp
pushl %edi
/* check if int13_handler is set */
movl $0x413, %edi
movw (%edi), %ax
cmpw $640, %ax
jae 1f #; needn't unset
// cmpw $632, %ax
// jb 1f
shll $(16 + 6), %eax
/* the offset(low word) should be 0x100 */
movw $0x100, %ax
cmpl %eax, 0x4c
jne 1f #; not hooked, unset failure
movl %eax, %edi
shrl $12, %edi /* segment base address */
cmpl $0x9A000000, 0x1C(%edi) /* old int 13 */
jb 1f
cmpl $0x9A000000, 0x0C(%edi) /* old int 15 */
jb 1f
movl ABS(int13_offset), %eax
cmpl 0x1C(%edi), %eax
jnz 1f
movl ABS(ROM_int15), %eax
cmpl 0x0C(%edi), %eax
jnz 1f
xorl %eax, %eax
cmpl %eax, 8(%ebp)
jnz 1f
/* increase the lower memory size */
movzbw (%edi), %ax
addw %ax, 0x413
/* restore the original int15 handler */
movl ABS(ROM_int15), %eax
movl %eax, 0x54
/* restore the original int13 handler */
movl ABS(int13_offset), %eax
movl %eax, 0x4c
1:
/* return non-zero for failure */
popl %edi
popl %ebp
ret
/*
* Map a drive to another drive or a disk image file.
*/
.code16
/* align it this way so that int13_handler can be used as a segment
* base address. The `cdrom' command requires this.
*/
.align 16
int13_handler:
/* memory size in K that int13 handler uses. */
.byte ((int13_handler_end - int13_handler + 0x3ff) / 0x400)
/* 9-byte space reserved. */
. = int13_handler + 0x0A
VARIABLE(floppies_orig)
.byte 0 /* original value at 0040:0010 */
. = int13_handler + 0x0B
VARIABLE(harddrives_orig)
.byte 0 /* original value at 0040:0075 */
. = int13_handler + 0x0C
ROM_int15:
.long 0 /* original int15 vector */
. = int13_handler + 0x10
/* 12-byte space reserved. */
. = int13_handler + 0x1C
int13_offset: .word 0 /* original int13 vector */
int13_segment: .word 0
. = int13_handler + 0x20 /* drive map table begins at 0x20 */
ENTRY(hooked_drive_map)
.space (DRIVE_MAP_SIZE + 1) * DRIVE_MAP_SLOT_SIZE
/* 8-byte space reserved. */
. = int13_handler + 0x100 /* real int13 handler entry at 0x100 */
jmp 1f
. = int13_handler + 0x103
/* SafeMBRHook structure begin */
.ascii "$INT13SF" /* Win9x use this! Don't touch! */
.ascii "GRUB4DOS" /* 8-byte Vender ID */
. = int13_handler + 0x113
int13_dup:
.long 0
. = int13_handler + 0x117
VARIABLE(safe_mbr_hook)
.long 0x00000001 /* safe MBR hook flag */
/* SafeMBRHook structure end */
/* But Win9x may expect additional data after SafeMBRHook structure.
* This is undocumented, and mysterious. If this area is not what
* Win9x expected, Win9x could hang.
*/
. = int13_handler + 0x11B
VARIABLE(int13_scheme)
/* bit 0 controls how we access sectors in protected mode.
* bit0=0: use pushf and far call for ROM int13 service.
* bit0=1: use the `int $0x13' instruction.
*/
.long 0x00000001
/* space reserved. */
. = int13_handler + 0x140
1:
cmpb $0x1a, %ah /* PS/2 low level format ESDI drive!!!! */
je error_01_disable2 /* disabled in any case */
/* (BP+10): old Flags */
/* (BP+8): return CS */
/* (BP+6): return IP */
cld
pushl %eax /* (BP+2): old EAX */
pushw %bp /* (BP+0): old BP */
movw %sp, %bp /* BP->old BP */
/* EDD30 code imported from edd30.asm of Smart Boot Manager. */
/*
#; asmsyntax=nasm
#;
#; CD-ROM Boot Extension v 1.1 for Smart Boot Manager
#;
#; Copyright (C) 2000, Christopher Li <chrisl@gnuchina.org>.
#; Copyright (C) 2000, James Su <suzhe@gnuchina.org>
#;
#; This is free software, you can redistribute it and/or modify it
#; under the terms of the GNU General Public License version 2 or above.
#;
#; The ATAPI driver is based on the source code of atadrv written by
#; Hale Landis <hlandis@ibm.net>, Thanks him a lot!
#;
#; Without his great program, we could not implement the CD-ROM Boot feature
#; so quickly.
#;
*/
//#define EDD_3_0
#define sane_check
#define check_extra_fail
//#define CB_DATA 0 //; data reg in/out pio_base_addr1+0
//#define CB_FR 1 //; feature reg out pio_base_addr1+1
//#define CB_SC 2 //; sector count in/out pio_base_addr1+2
//#define CB_SN 3 //; sector number in/out pio_base_addr1+3
//#define CB_CL 4 //; cylinder low in/out pio_base_addr1+4
//#define CB_CH 5 //; cylinder high in/out pio_base_addr1+5
//#define CB_DH 6 //; device head in/out pio_base_addr1+6
//#define CB_STAT 7 //; primary status in pio_base_addr1+7
//#define CB_CMD 7 //; command out pio_base_addr1+7
//#define CB_DC 8 //; device control out pio_base_addr2+6
//#define CB_ASTAT 8 //; alternate status in pio_base_addr2+6
//#define CB_DC_SRST 0x04 //; soft reset
#define CB_STAT_BSY 0x80 //; busy
#define CB_STAT_RDY 0x40 //; ready
#define CB_STAT_SKC 0x10 //; seek complete
#define CB_STAT_DRQ 0x08 //; data request
#define CB_STAT_ERR 0x01 //; error
#define CB_SC_P_TAG 0xf8 //; ATAPI tag (mask)
#define CB_SC_P_REL 0x04 //; ATAPI release
#define CB_SC_P_IO 0x02 //; ATAPI I/O
#define CB_SC_P_CD 0x01 //; ATAPI C/D
#define FAILBIT8 0x0100 //; SC( CD/IO bits) wrong at end of cmd
#define FAILBIT6 0x0040 //; byte count wrong at data packet xfer time
#define FAILBIT5 0x0020 //; SC (IO bit) wrong at data packet xfer time
#define FAILBIT4 0x0010 //; SC (CD bit) wrong at data packet xfer time
#define FAILBIT3 0x0008 //; byte count wrong at cmd packet xfer time
#define FAILBIT2 0x0004 //; SC wrong at cmd packet xfer time
#define FAILBIT0 0x0001 //; slow setting BSY=1 or DRQ=1 after AO cmd
#define CB_DC_HD15 0x08 //; bit should always be set to one
#define CB_DC_NIEN 0x02 //; disable interrupts
#define cmd_DC CB_DC_HD15
#define cmd_DC_ni CB_DC_HD15 | CB_DC_NIEN
#undef DEBUG
#ifdef DEBUG
#include "debug.h"
#endif
cmpb $0, %cs:(atapi_dev_count - int13_handler)
jz no_cdrom /* no cdrom */
cmpb %cs:(min_cdrom_id - int13_handler), %dl
jb not_cdrom_drv /* not cdrom drive */
cmpb %cs:(max_cdrom_id - int13_handler), %dl
jbe edd30_for_cdrom
not_cdrom_drv:
// cmpb %cs:(min_cdrom512_id - int13_handler), %dl
// jb not_cdrom512_drv /* not cdrom512 drive */
// cmpb %cs:(max_cdrom512_id - int13_handler), %dl
// jbe edd30_for_cdrom512
//
//not_cdrom512_drv:
cmpb $0, %cs:(emu_disk_type - int13_handler)
je not_emu_drv
cmpb %cs:(edd30_cdemu_spec - int13_handler + 2), %dl #; struc_cdemu_spec.emu_drvid
je cdemu_int13h
not_emu_drv:
no_cdrom:
pushw %si /* BP-2: old SI */
/* set %si to the drive map */
movw $(hooked_drive_map - int13_handler), %si
/* find the drive number from the drive map */
subw $DRIVE_MAP_SLOT_SIZE, %sp
1:
addw $DRIVE_MAP_SLOT_SIZE, %sp
lodsl %cs:(%si), %eax
pushl %eax /* BP-6: FROM, TO, Hmax, Smax */
lodsl %cs:(%si), %eax
pushl %eax /* BP-10: to_C, to_H, to_S */
lodsl %cs:(%si), %eax
pushl %eax /* BP-14: StartLBA_Lo */
lodsl %cs:(%si), %eax
pushl %eax /* BP-18: StartLBA_Hi */
lodsl %cs:(%si), %eax
pushl %eax /* BP-22: S_count_Lo */
lodsl %cs:(%si), %eax
pushl %eax /* BP-26: S_count_Hi */
movl -6(%bp), %eax /* FROM, TO, Hmax, Smax */
/* check if this is the end */
testl %eax, %eax
jnz 3f /* not end, continue */
movl -14(%bp), %eax /* StartLBA_Lo */
testl %eax, %eax
jnz 3f /* not end, continue */
movl -22(%bp), %eax /* S_count_Lo */
testl %eax, %eax
jz 2f /* map whole drive to itself signals the end */
3:
/* Now this is a valid drive map slot */
movw -6(%bp), %ax /* AL=FROM, AH=TO */
cmpb %al, %dl /* check if this matches the drive number */
jne 1b /* no, continue to check the next map */
/* yes, found the map corresponding to drive DL */
testb $0x80, -7(%bp)
jnz 1f
/* non-zero StartLBA signals emulation */
movl -14(%bp), %eax /* StartLBA_Lo */
testl %eax, %eax
jnz drive_emulation
/* S_count being not 1 signals emulation */
movl -22(%bp), %eax /* S_count_Lo */
shrl $1, %eax
jnz drive_emulation
/* now StartLBA=0 and sector count=1(for whole disk) */
/* if sectors per track > 1, this is force geometry screw. */
movw -4(%bp), %ax /* AL=Hmax, AH=Smax */
testb $62, %ah /* Sectors > 1 means force geom, this -- */
jnz drive_emulation /* -- also leads to drive emulation */
1:
/* bit 7 of the TO_S is for in-situ primary partition(alter MBR) */
/* ignore geom and directly map a whole drive */
/* bits of AH:
* 7 bit set means readonly/fakewrite
* 6 bit set means disable LBA
* 5 - 1 bits already cleared(=0)
* 0 bit cleared means disable CHS
* So, if AH!=1, it is a restricted disk access;
* and if AH=1, it is a normal disk access.
*/
cmpb $1, %ah
je 1f
// testb $0x3F, %ah
// jz 3f
// testb $0xC0, %ah
// jz 1f
//3:
call restricted_map
1:
/* map a whole drive, normal access */
movw -6(%bp), %ax /* AL=FROM, AH=TO */
movb %ah, %dl /* Let DL access TO instead of FROM */
2:
/* might map to itself, i.e., actually not mapped */
movw -2(%bp), %si /* restore SI */
pushw 10(%bp) /* pushfw, simulate the interrupt call */
movl 2(%bp), %eax /* restore EAX */
movw (%bp), %bp /* restore BP */
// .byte 0x9a /* lcall, simulate the interrupt call */
//int13_offset: .word 0
//int13_segment: .word 0
lcall %cs:*(int13_offset - int13_handler)
// /* the original int13 vector must be stored in the first kilo bytes */
//
// . = . - (. - int13_handler) / 0x401
pushw %ax /* save AX */
pushfw /* save flags returned by int13 */
popw %ax
movw %sp, %bp /* set new BP */
/* BP+38: old Flags */
/* BP+36: return CS */
/* BP+34: return IP */
/* BP+30: old EAX */
/* BP+28: old BP */
/* BP+26: old SI */
/* BP+22: FROM, TO, Hmax, Smax */
/* BP+18: to_C, to_H, to_S */
/* BP+14: StartLBA_Lo */
/* BP+10: StartLBA_Hi */
/* BP+ 6: S_count_Lo */
/* BP+ 2: S_count_Hi */
/* BP+ 0: new AX returned from int 13 */
#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
leaw 28(%bp), %bp // it is addw $28, %bp but won't touch flags
/* BP+10: old Flags */
/* BP+ 8: return CS */
/* BP+ 6: return IP */
/* BP+ 2: old EAX */
/* BP+ 0: old BP */
/* BP- 2: old SI */
/* BP- 6: FROM, TO, Hmax, Smax */
/* BP-10: to_C, to_H, to_S */
/* BP-14: StartLBA_Lo */
/* BP-18: StartLBA_Hi */
/* BP-22: S_count_Lo */
/* BP-26: S_count_Hi */
/* BP-28: new AX returned from int 13 */
#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
movw %ax, 10(%bp) /* update the flags in the stack */
/* check if should restore(reversely map) the drive number */
jc 1f /* restore DL on error */
movw 2(%bp), %ax /* lower word of the saved EAX */
/* alter MBR after read */
testb $0x80, -7(%bp)
jz 3f
cmpb $0x02, %ah
je alter_mbr
cmpb $0x42, %ah
je alter_mbr
3:
cmpb $0x08, %ah /* int13 AH=08h, read drive parameters */
jne 2f
/* DL==number of drives, should not restore */
pushw %ds
xorw %ax, %ax
movw %ax, %ds
movw -6(%bp), %ax /* AL=FROM, AH=TO */
testb %al, %al /* hard drive? */
movb 0x475, %dl /* DL=number of hard drives */
js 3f /* yes, jump */
//movw $0x10, %bx /* for floppy, refer to function 0x48 */
movb 0x410, %al
rorb $1, %al
cbw
shlb $1, %al
shrb $6, %al
incw %ax
andb %ah, %al
movb %al, %dl /* DL=number of floppy drives */
//lesw 0x0078, %di /* point to int 1E floppy parameters */
3:
popw %ds
jmp 3f
2:
//cmpb $0x15, %ah /* int13 AH=15h, read drive type */
//je 3f /* CX:DX==total sectors, should not restore */
//jne 1f
xorb $0x15, %ah /* int13 AH=15h, read drive type */
/* CF=0 for now. if ZF=1, then AH was 0x15, and it is "equal"
* for the jxx class of instructions. if ZF=0, then AH was not 0x15,
* and it is "above" for the jxx class of instructions.
*/
1:
movw -6(%bp), %ax /* get the drive mapping */
/* should not simply use "jne 2f" here because ZF is unknown when
* error occurred for the above int13 call
*/
jc 2f /* "below" means error occurred for int13 */
ja 2f /* "above" means it is not int13 AH=15h */
/* "equal" means int13 AH=15h */
testb %al, %al /* the FROM drive is harddrive? */
js 3f /* yes, do not restore DL */
/* restore DL for floppy int13 AH=15h call */
2:
/* try to restore DL if possible */
cmpb %al, %ah /* check if the mapping was performed */
je 3f /* not performed, so need not restore DL */
cmpb %dl, %ah
jne 3f /* DL changed by int13, so do not restore */
movb %al, %dl /* restore DL back to the FROM drive */
3:
/* return */
movl 2(%bp), %eax /* restore the original EAX */
popw %ax /* update the int13 status in AX */
movw (%bp), %bp /* restore the original BP */
addw $32, %sp
iret
alter_mbr:
call modify_in_situ
movw -6(%bp), %ax /* get the drive mapping */
jmp 2b
restricted_map:
movw 2(%bp), %ax /* lower word of the saved EAX */
/* CHS read functions */
cmpb $0x02, %ah /* read sectors */
je 2f
cmpb $0x04, %ah /* verify sectors, also a read operation */
je 2f
cmpb $0x0a, %ah /* read long sectors */
je 2f
cmpb $0x0c, %ah /* seek to cylinder */
je 2f
cmpb $0x21, %ah /* PS/1 and newer PS/2 - read multiple disk sectors */
jne 1f
2:
movb -3(%bp), %ah /* AH=Smax */
testb $63, %ah /* check if Sectors=0, i.e., disable CHS */
jz error_01_disable
ret
1:
/* CHS write functions */
cmpb $0x03, %ah /* CHS write sectors */
je 2f
cmpb $0x05, %ah /* PC/XT/AT/EISA format tracks */
je 2f
cmpb $0x06, %ah /* PC/XT format tracks with bad sectors */
je 2f
cmpb $0x07, %ah /* PC/XT format multiple cylinders */
je 2f
cmpb $0x0b, %ah /* PC/XT/AT/EISA write sectors with ECC */
je 2f
cmpb $0x0f, %ah /* PC/XT/PS/1 write sector buffer */
je 2f
cmpb $0x22, %ah /* PS/1 and newer PS/2 - write multiple disk sectors */
jne 1f
2:
movb -3(%bp), %ah /* AH=Smax */
testb $63, %ah /* check if Sectors=0, i.e., disable CHS */
jz error_01_disable
testb $0x80, %ah /* readonly access? */
jnz readonly_fakewrite
ret
1:
/* LBA read functions */
cmpb $0x41, %ah /* Extensions - INSTALLATION CHECK */
je 2f
cmpb $0x42, %ah /* Extensions - EXTENDED READ */
je 2f
cmpb $0x44, %ah /* Extensions - verify sectors */
je 2f
cmpb $0x45, %ah /* Extensions - LOCK/UNLOCK DRIVE */
je 2f
cmpb $0x46, %ah /* Extensions - EJECT MEDIA */
je 2f
cmpb $0x47, %ah /* Extensions - EXTENDED SEEK */
je 2f
cmpb $0x48, %ah /* Extensions - GET DRIVE PARAMETERS */
je 2f
cmpb $0x49, %ah /* Extensions - detect media change */
je 2f
cmpb $0x4a, %ah /* Bootable CDROM - INITIATE DISK EMULATION */
je 2f
cmpb $0x4b, %ah /* Bootable CDROM - TERMINATE DISK EMULATION */
je 2f
cmpb $0x4c, %ah /* Bootable CDROM - INITIATE DISK EMULATION AND BOOT */
je 2f
cmpb $0x4d, %ah /* Bootable CDROM - RETURN BOOT CATALOG */
je 2f
cmpb $0x4e, %ah /* Extensions v2.1 - SET HARDWARE CONFIGURATION */
jne 1f
2:
movb -3(%bp), %ah /* AH=Smax */
testb $64, %ah /* disable LBA? */
jnz error_01_disable
ret
1:
/* LBA write functions */
cmpb $0x43, %ah /* Extensions - EXTENDED WRITE */
jne 1f
2:
movb -3(%bp), %ah /* AH=Smax */
testb $64, %ah /* disable LBA? */
jnz error_01_disable
testb $0x80, %ah /* readonly access? */
jnz readonly_fakewrite
1:
/* no restrictions, return and continue */
ret
error_01_disable:
/* function not supported, or the input CHS is invalid */
addw $26, %sp /* adjust SP */
popw %si
popw %bp
popl %eax
error_01_disable2:
movb $0x01, %ah /* signal invalid function call */
stc /* signal error */
lret $2 /* far return discard the original flags */
readonly_fakewrite:
addw $26, %sp /* adjust SP */
popw %si
testb $1, -22(%bp) /* lowest bit of S_count_Lo */
popw %bp
popl %eax
jnz 1f
/* read only */
movb $0x03, %ah /* signal write protection */
stc /* signal error */
lret $2 /* far return discard the original flags */
1:
/* fake write */
xorb %ah, %ah /* signal write succeeded */
/*clc*/ /* signal success, CF already cleared by XOR */
lret $2 /* far return discard the original flags */
drive_emulation:
movw -4(%bp), %ax /* AL=Hmax, AH=Smax */
testb $63, %ah /* disable CHS? */
jz 2f /* yes, call restricted map */
testb $0xc0, %ah /* readonly or disable LBA? */
jz 1f
2:
call restricted_map
1:
movw 2(%bp), %ax /* get original AX */
testb %ah, %ah /* reset disk system, always succeed */
jnz 1f
clc
jmp int13_return
1:
cmpb $0x01, %ah /* get status of last operation, always succeed */
jnz 1f
xorb %ah, %ah
clc
jmp int13_return
1:
cmpb $0x04, %ah /* verify sectors, always succeed */
jnz 1f
xorb %ah, %ah
clc
jmp int13_return
1:
cmpb $0x09, %ah /* INITIALIZE CONTROLLER WITH DRIVE PARAMETERS */
jnz 1f
xorb %ah, %ah
clc
jmp int13_return
1:
cmpb $0x0c, %ah /* SEEK TO CYLINDER */
jnz 1f
xorb %ah, %ah
clc
jmp int13_return
1:
cmpb $0x0d, %ah /* reset hard disks */
jnz 1f
xorb %ah, %ah
clc
jmp int13_return
1:
cmpb $0x10, %ah /* check if drive ready */
jnz 1f
xorb %ah, %ah
clc
jmp int13_return
1:
cmpb $0x11, %ah /* recalibrate drive */
jnz 1f
xorb %ah, %ah
clc
jmp int13_return
1:
cmpb $0x14, %ah /* CONTROLLER INTERNAL DIAGNOSTIC */
jnz 1f
xorb %ah, %ah
clc
jmp int13_return
1:
cmpb $0x05, %ah /* format track */
jnz 1f
xorb %ah, %ah /* do nothing but return success */
clc
jmp int13_return
1:
cmpb $0x08, %ah /* get drive parameters */
jnz 1f
pushw %ds
xorw %ax, %ax
movw %ax, %ds
testb %dl, %dl /* hard drive? */
movb 0x475, %dl /* DL=number of hard drives */
js 2f /* yes, jump */
movw $0x10, %bx /* for floppy, refer to function 0x48 */
movb 0x410, %al
rorb $1, %al
cbw
shlb $1, %al
shrb $6, %al
incw %ax
andb %ah, %al
xchgw %ax, %dx /* DL=number of floppy drives */
lesw 0x0078, %di /* point to int1E floppy parameters */
2:
popw %ds
movw -4(%bp), %ax /* AL=Hmax, AH=Smax */
movb %al, %dh /* max head number */
andb $63, %ah
movb %ah, %cl /* max sector number */
///* simply use 1024 cylinders, hope this would work well */
//orw $0xffc0, %cx /* max cylinder number=1023 */
/* max cylinder number */
pushl %edx
pushl %ecx
movzbl %dh, %eax
movzbl %cl, %ecx
incl %eax
mull %ecx /* EDX=0, EAX=sectors per cylinder */
xchgl %eax, %ecx
movl -22(%bp), %eax /* S_count_Lo */
andb $0xfe, %al
decl %eax
//xorl %edx, %edx
divl %ecx /* EAX=max cylinder number */
popl %ecx
popl %edx
movb %al, %ch /* low 8 bits of cylinder */
shlb $6, %ah /* high 2 bits of cylinder */
orb %ah, %cl
//movb $0x08, %dl /* drives=8(HDs=8 as well as FDs=8) */
xorw %ax, %ax
clc
jmp int13_return
1:
cmpb $0x15, %ah /* get disk type */
jnz 1f
testb %dl, %dl /* hard drive? */
js 2f /* yes, jump */
movb $0x02, %ah /* floppy (or other removable drive) with change-line support */
clc
jmp int13_return
2:
movb $0x03, %ah /* hard disk */
/* CX:DX=total number of sectors */
movw -22(%bp), %dx /* lo word of S_count_Lo */
movw -20(%bp), %cx /* hi word of S_count_Lo */
andb $0xfe, %dl /* clear bit 0 */
clc
jmp int13_return
1:
cmpb $0x16, %ah
jnz 1f
xorb %ah, %ah /* AH=0 means disk not changed */
/* clc */ /* signal success, CF already cleared by XOR */
jmp int13_return
1:
cmpb $0x17, %ah /* set floppy type for format */
jnz 1f
movb $0x03, %al /* 1.44M drive, 1.44M floppy */
xorb %ah, %ah
/* clc */ /* signal success, CF already cleared by XOR */
jmp int13_return
1:
cmpb $0x18, %ah /* set media type for format */
jnz 1f
testb %dl, %dl /* hard drive? */
js 2f /* yes, jump */
pushw %ax
xorw %ax, %ax
movw %ax, %es
movw $0x0078, %di
movl %es:(%di), %eax
movw %ax, %di
shrl $0x10, %eax
movw %ax, %es
popw %ax
xorb %ah, %ah
/* clc */ /* signal success, CF already cleared by XOR */
jmp int13_return
2:
movb $1, %ah /* temp disabled for hard disks */
stc
jmp int13_return
1:
/* Now AH is neither 0 nor 1 */
testb $0xfc, %ah /* CHS read/write sectors */
jnz 1f
/* so AH is either 2(for read) or 3(for write) */
#if 1
cmpw $0x0301, %ax /* is it write 1 sector? */
jne 2f
cmpw $0x0001, %cx /* write to cylinder 0, sector 1? */
jne 2f
cmpb $0x01, %dh /* write to head 0 or 1? */
ja 2f
/* cmpw $0xaa55, %es:0x1fe(%bx)
je 2f */
testb %dl, %dl
js 3f /* protect hard disk head 0 and 1 */
testb %dh, %dh /* write to floppy head 0? */
jne 2f /* no, write permitted */
3:
movb -22(%bp), %ah /* lowest byte of S_count_Lo */
testb $1, %ah /* fake write here means safeboot */
jz 2f /* unsafe boot allows the write */
/* disable the write */
addw $24, %sp /* adjust SP */
popw %si
popw %bp
popl %eax
xorb %ah, %ah /* signal write succeeded, this is a fake */
/* clc */ /* signal success, CF already cleared by XOR */
lret $2 /* far return discard the original flags */
#else
/* debug only. locate windows 98 unidentified write to floppy */
pushaw
movw 8(%bp), %ax
movw %ax, %ds
movw 6(%bp), %si
subw $0x0400, %si
movw %bx, %di
movw $0x0400, %cx
cld
rep
movsw
movw %si, %ax
movw %di, %bx
subw $4, %di
stosw
movw %bx, %ax
stosw
popaw
movb $4, %al
/*3:*/
/*hlt*/ /* invalid write to floppy, halt the machine */
/*jmp 3b*/
#endif
2:
cmpb $0x7F, %al /* check if sectors exceed 127 */
ja error_01_invalid
testb %al, %al /* read 0 sectors not allowed */
jz error_01_invalid
testb $63, %cl /* beginning sector number 0 is invalid */
jz error_01_invalid
movb -3(%bp), %ah /* AH=Smax */
andb $63, %ah
pushw %cx
andb $63, %cl
cmpb %ah, %cl /* CL should not > max sector number */
popw %cx
ja error_01_invalid
movb -4(%bp), %ah
cmpb %ah, %dh /* DH should not > max head number */
ja error_01_invalid
pushw %ds
pushw %cs
popw %ds
movw $(EBIOS_disk_address_packet - int13_handler), %si
movb %al, 2(%si) /* sectors */
movw %bx, 4(%si)
movw %es, 6(%si)
xorl %eax, %eax
movb %ah, 3(%si) /* sectors_hi_reserved */
movl %eax, 12(%si)
movb %ch, %al /* cylinder number */
movb %cl, %ah /* CL holds higher 2 bits */
shrb $6, %ah /* AH lower holds the 2 bits */
/* Now AX holds the 10 bit cylinder number */
pushl %ebx
xorl %ebx, %ebx
movb -4(%bp), %bl /* max head number */
/* xorb %bh, %bh */
incw %bx /* BX=total heads, <=256 */
pushl %edx
mull %ebx /* EDX:EAX=result, so EDX=0 */
popl %edx
xorl %ebx, %ebx
movb %dh, %bl /* head number */
addl %ebx, %eax
movb -3(%bp), %bl /* Max sector number */
andb $63, %bl
pushl %edx
mull %ebx /* EDX:EAX=result, so EDX=0 */
popl %edx
movb %cl, %bl /* sector number */
andb $63, %bl
decb %bl
addl %ebx, %eax /* EAX=lo 32 bits of logical sector number */
movl %eax, 8(%si)
popl %ebx
call bound_check
testb $0xfe, %ah /* allow all or part of sectors to tranfer */
jnz 3f /* no sectors to transfer, skip */
movl -14(%bp), %eax /* StartLBA_Lo */
addl %eax, 8(%si)
setb 12(%si)
movb -5(%bp), %dl /* DL=TO_DRIVE */
movb 3(%bp), %ah /* 0x02=read, 0x03=write */
orb $0x40, %ah /* 0x42=EXT_read, 0x43=EXT_write */
#if 1
call real_int13_service
#else
pushfw
lcall *(int13_offset - int13_handler) /* call original int13 */
#endif
jc 2f /* on error we needn't use the bound check result */
4:
call modify_boot_sectors
popfw /* use bound check info */
pushfw /* dummy push, for the following dummy pop */
jnc 4f
movb $0x04, %ah /* sector not found */
2:
popfw /* dummy pop */
stc
pushfw
4:
movb 2(%si), %al /* number of sectors transferred */
/* It is not safe to use POPW %DS for running in protected mode */
popfw /* dummy pop, skip the pushed flags */
movb -6(%bp), %dl /* restore DL(=FROM_DRIVE) */
3:
popw %ds
jmp int13_return
1:
cmpb $0x41, %ah /* EBIOS installation check */
jnz 1f
cmpw $0x55aa, %bx
jnz error_01_invalid
// testb %dl, %dl
// jns error_01_invalid
movw $0xaa55, %bx
movb $0x21, %ah /* major version 2.1(EDD-1.1) */
movw $0x01, %cx /* support functions 42h,43h,44h,47h,48h */
clc
jmp int13_return
1:
cmpb $0x42, %ah /* EBIOS read sectors */
jz 2f
cmpb $0x43, %ah /* EBIOS write sectors */
jnz 1f
2:
movw -2(%bp), %si /* get old SI, disk address packet */
movl (%si), %eax /* packet length, sectors, etc. */
testb %ah, %ah
jnz error_01_invalid
testb $0xf0, %al
jz error_01_invalid
shrl $16, %eax
testw $0xff80, %ax
jnz error_01_invalid
call bound_check
testb $0xfe, %ah /* allow all or part of sectors to tranfer */
jnz int13_return /* no sectors to transfer, skip */
movl -14(%bp), %eax /* StartLBA_Lo */
addl %eax, 8(%si)
jnc 2f
incl 12(%si)
2:
movb -5(%bp), %dl /* DL=TO_DRIVE */
movb 3(%bp), %ah /* 0x42=read, 0x43=write */
#if 1
call real_int13_service
#else
pushfw
lcall %cs:*(int13_offset - int13_handler) /* call original int13 */
#endif
// pushfw
// pushl %eax
// movl -14(%bp), %eax /* StartLBA_Lo */
// subl %eax, 8(%si)
// sbbl $0, 12(%si)
// popl %eax
// popfw
jc 2f /* on error need not use the bound check */
// call modify_boot_sectors
popfw /* use bound check info */
pushfw /* dummy push, for the following dummy pop */
jnc 3f
movb $0x04, %ah /* sector not found */
2:
popfw /* dummy pop */
stc
pushfw
3:
#if 0
/* this pair can be omitted */
popfw /* dummy pop */
pushfw
#endif
pushl %eax
movl -14(%bp), %eax /* StartLBA_Lo */
subl %eax, 8(%si)
jnc 2f
decl 12(%si)
2:
popl %eax
call modify_boot_sectors
movb -6(%bp), %dl /* restore DL(=FROM_DRIVE) */
popfw /* pop the pushed Flags by bound_check */
jmp int13_return
1:
cmpb $0x44, %ah /* EBIOS verify sectors */
jnz 1f
xorb %ah, %ah
clc
jmp int13_return
1:
cmpb $0x47, %ah /* EBIOS seek */
jnz 1f
xorb %ah, %ah
clc
jmp int13_return
1:
cmpb $0x48, %ah /* EBIOS GET DRIVE PARAMETERS */
jnz error_01_invalid
movw -2(%bp), %si /* get old SI, extended drive parameter table */
movw $26, (%si) /* buffer length */
movw $2, 2(%si) /* info */
xorl %eax, %eax
movl %eax, 8(%si) /* total heads */
movl %eax, 12(%si) /* sectors per track */
movl %eax, 16(%si) /* total sectors */
movl %eax, 20(%si) /* hi 32 bits of total sectors */
pushl %ebx
xorl %ebx, %ebx
movw -4(%bp), %ax /* AL=Hmax, AH=Smax */
andb $63, %ah
movb %ah, 12(%si) /* sectors per track */
movb %ah, %bl
xorb %ah, %ah
incw %ax /* total heads=Hmax+1 */
movw %ax, 8(%si) /* total heads */
pushl %edx
mulw %bx /* DX:AX=product, DX=0 */
movw %ax, %bx /* BX=sectors per cylinder */
movl -22(%bp), %eax /* S_count_Lo */
andb $0xfe, %al
movl %eax, 16(%si) /* total sectors */
xorl %edx, %edx /* EDX:EAX=64bit total sectors */
testw %bx, %bx
jz 2f
divl %ebx /* EAX=quotient, EDX=residue */
2:
testl %edx, %edx
popl %edx
popl %ebx
jz 2f
incl %eax
2:
movl %eax, 4(%si) /* total cylinders */
movw $512, 24(%si) /* bytes per sector */
xorb %ah, %ah
/*clc*/ /* signal success, CF already cleared by XOR */
jmp int13_return
error_01_invalid:
movb $0x01, %ah /* unsupported function call */
stc /* signal error */
int13_return:
pushw %ax /* save status */
//pushfw
//popw %ax
lahf /* Load Flags into AH */
movb 10(%bp), %al
shrw $1, %ax
rolb $1, %al
movb %al, 10(%bp) /* update flags in the stack */
movw -2(%bp), %si /* restore the original SI */
movl 2(%bp), %eax /* restore the original EAX */
movw (%bp), %bp /* restore the original BP */
popw %ax /* restore status */
addw $32, %sp /* adjust SP */
iret
bound_check:
/*
* check if the request exceeds the boundary of the emulated disk.
*
* input: DS:SI
* output: AH=0 no restrictions, all sectors transferred
* AH=1 sectors transferred, but not all
* AH=4 error exit immediately, no sectors transferred
*
* if S_count=StartLBA=0, then no restrictions
* else if 8(%si)>=S_count then error
* else if 8(%si)+2(%si)<=S_count then no restrictions
* else let 2(%si)=S_count-8(%si),call original int13,signal error
*
*/
movl -14(%bp), %eax /* StartLBA_Lo */
testl %eax, %eax
jnz 2f
movl -22(%bp), %eax /* S_count_Lo */
shrl $1, %eax
jnz 2f
clc /* map whole drive, signal no restrictions */
popw %ax /* pop return IP */
pushfw #; push additional Flags!!
pushw %ax /* push return IP */
xorb %ah, %ah /* no restrictions, all sectors transferred */
ret
2:
movl 12(%si), %eax /* hi 32-bit of the requested StartLBA */
testl %eax, %eax
jnz 3f /* non-zero is considered `too big' */
movl -22(%bp), %eax /* S_count_Lo */
andb $0xfe, %al
cmpl %eax, 8(%si) /* lo 32-bit of the requested StartLBA */
jb 2f
3:
/* all sectors exceed the bound, exit immediately */
#; Return WITHOUT additional Flags pushed!!
movb $4, %ah
xorb %al, %al /* no sectors transferred */
movb %al, 2(%si) /* SI+2 retrieve sectors transferred */
stc
ret
2:
subl 8(%si), %eax
pushl %ebx
xorl %ebx, %ebx
movw 2(%si), %bx
cmpl %eax, %ebx
popl %ebx
ja 2f
clc /* signal no restrictions */
popw %ax /* pop return IP */
pushfw #; push additional Flags!!
pushw %ax /* push return IP */
xorb %ah, %ah /* no restrictions, all sectors transferred */
ret
2:
movb %al, 2(%si) /* SI+2 retrieve sectors transferred */
stc
popw %ax /* pop return IP */
pushfw #; push additional Flags!!
pushw %ax /* push return IP */
movb $1, %ah /* not all sectors tranferred */
ret
real_int13_service:
/* AH = 0x42 or 0x43 */
cmpb $0xff, %dl /* mem drive */
jne 2f
cmpb $0x42, %ah
je 3f
cmpb $0x43, %ah
jne 5f
/* DS:SI points to disk address packet */
/* 2(%si), byte, sectors(1-127) */
/* 4(%si), word, offset */
/* 6(%si), word, segment */
/* 8(%si), qword, lba */
3:
/* AH = 0x42 or 0x43 */
pushw %es
pushal
sahf /* Store AH into flags SF ZF xx AF xx PF xx CF */
pushf
movzwl 4(%si), %ebx #; BX=offset, EBX_high_word=0
movzwl 6(%si), %eax #; AX=segment
movl 8(%si), %ecx #; ECX=LBA of MEMDRIVE SECTOR
shll $9, %ecx #; ECX=linear address of SECTOR(above 1M)
shll $4, %eax #; EAX=linear base address of segment
addl %ebx, %eax #; EAX=linear address of BUFFER(below 1M)
popf
jc 4f /* write */
xchgl %eax, %ecx
4:
cmpl $0, %cs:(memdisk_raw - int13_handler)
je 4f /* do not use raw mode */
/* raw mode as in memdisk, contributed by Bean */
/* Data transfer: %eax -> %ecx, number of sector 2(%si) */
// pushw %ax
smsw %bx
testb $1, %bl
// popw %ax
jnz 4f /* protected mode */
/* switch to protected mode myself */
/* ebx destroy */
pushfl
pushw %ds
pushw %es
movl %ecx, %edi
movzbl 2(%si), %ecx #; ECX=number of sectors to transfer
movl %eax, %esi #; ESI changed!!
cli
#;xorl %ebx, %ebx
movw %cs, %bx #; EBX_high_word=0
shll $4, %ebx
addl $(MyGDT - int13_handler), %ebx
movl %ebx, %cs:MyGDT - int13_handler + 2
/* Test to see if A20 is enabled or not */
xorw %ax, %ax
movw %ax, %ds /* DS=0 */
decw %ax
movw %ax, %es /* ES=0xFFFF */
movw %ds:0, %ax
movw %ax, %bx
xorw %es:0x10, %bx
notw %ax
movw %ax, %ds:0
movw %ax, %dx
xorw %es:0x10, %dx
notw %ax
movw %ax, %ds:0
orw %bx, %dx
pushw %dx /* 0=A20 off, 1=A20 on */
jnz 1f
movb $1, %dl /* A20 is off, so it need turn on. */
call int13_enable_a20
1:
shll $7, %ecx /* 128 dwords per sector */
lgdt %cs:MyGDT - int13_handler
movl %cr0, %eax
orb $1, %al
movl %eax, %cr0 /* Switch to protected mode */
movw $16, %bx /* Switch to 4G data segment */
movw %bx, %ds
movw %bx, %es
cld
addr32 rep movsl
// movw $8, %bx /* Switch back to 64K data segment */
// movw %bx, %ds
// movw %bx, %es
andb $0xFE, %al
movl %eax, %cr0 /* Back to real mode */
popw %dx
popw %es
popw %ds
/* Keep A20 on. This should hurt nothing. */
#if 0
/* Disable A20 if necessary ! */
andw %dx, %dx /* 0=orig A20 off, 1=orig A20 on */
jnz 1f
call int13_enable_a20
1:
#endif
popfl
popal
popw %es
xorb %ah, %ah
ret
4:
movb 2(%si), %bl /* number of sectors to be moved */
pushw %cs
popw %es
movw $(GDT_data - int13_handler), %si /* SI changed!! */
movw %ax, %es:0x12(%si) #; source physical address low 16 bits
shrl $16, %eax
movb %al, %es:0x14(%si) #; source physical address bit 16-23
movb %ah, %es:0x17(%si) #; source physical address bit 24-32
movw %cx, %es:0x1a(%si) #; dest physical address low 16 bits
shrl $16, %ecx
movb %cl, %es:0x1c(%si) #; dest physical address bit 16-23
movb %ch, %es:0x1f(%si) #; dest physical address bit 24-32
xorw %cx, %cx /* ECX is 0 */
movl %ecx, %es:(%si)
movl %ecx, %es:0x04(%si)
movl %ecx, %es:0x08(%si)
movl %ecx, %es:0x0c(%si)
movl %ecx, %es:0x20(%si)
movl %ecx, %es:0x24(%si)
movl %ecx, %es:0x28(%si)
movl %ecx, %es:0x2c(%si)
movb %bl, %ch /* CX=number of words to be moved */
movb $0x87, %ah /* access extended memory */
pushw %ax
smsw %ax
testb $1, %al
popw %ax
jnz 4f /* protected mode */
#if 0
pushw %ax
pushw %ds
xorw %ax, %ax
movw %ax, %ds
movl 0x0054, %eax /* point to int15 vector */
popw %ds
cmpl %eax, %cs:(ROM_int15 - int13_handler)
popw %ax
je 4f
/* real mode with int15 vector changed */
#endif
/* real mode */
pushfw
lcall %cs:*(ROM_int15 - int13_handler)
#if 0
/* ensure A20 is on when we return to caller. */
/* A20 control is a slow operation. Enabling A20 here in int13 handler
* could cause problems on some machines.
*/
pushfw
cli /* yes, keep interrupt off when controlling A20 */
movw $0x00ff, %cx # try so many times on failure
movw $0x0001, %dx # non-zero means `enable'
call enable_disable_a20
//sete %dl # DL=1 means success
popfw
#endif
jmp 6f
4:
///* protected mode, or real mode with int15 vector unchanged */
/* protected mode */
int $0x15
6:
popal
popw %es
jc 6f
xorb %ah, %ah
ret
6:
movb $0, 2(%si) /* signal no sectors successfully moved */
5:
/* sector not found */
movb $4, %ah
stc
ret
2:
/* normal disk drive */
/* AH = 0x42 or 0x43 */
#if 1
pushw %ax
smsw %ax
testb $1, %al
popw %ax
jz real_mode_ROM_int13
#else
pushw %ds
pushw %si
xorw %si, %si
movw %si, %ds /* DS=SI=0 */
/* DS:[SI+0x4C] points to int 13 vector */
ldsw 0x4C(%si), %si /* new DS:SI=int 13 vector, pointing to */
/* int 13 service routine */
cmpb $0x63, (%si) /* win 98 places ARPL here */
popw %si
popw %ds
jne real_mode_ROM_int13
#endif
/* protected mode(Windows 98, EMM386) */
#;/* now inside win 98, will call protected mode int 13 */
#if 0
/* debug code to test the win98 behavior */
/* disable the disk access in protected mode. */
testw $0x400, -10(%bp) /* to_C */
jnz 1f
movb $0, 2(%si)
movb $2, %ah /* I/O error */
stc
ret
1:
#endif
pushw %si /* save SI */
pushl %eax /* save EAX */
pushl %ebx /* save EBX */
/* set SI to the drive map */
movw $(hooked_drive_map - int13_handler), %si
/* find the drive number from the drive map */
cld
subw $DRIVE_MAP_SLOT_SIZE - 4, %si
2:
addw $DRIVE_MAP_SLOT_SIZE - 4, %si
lodsl %cs:(%si), %eax
testl %eax, %eax /* end of map table? */
movl %eax, %ebx /* save the map to EBX */
jz 2f /* yes, no map found */
cmpb %dl, %ah /* found the map? */
jne 2b /* no, check the next slot */
/* drive is mapped. check if map a whole drive */
shrl $16, %eax
testb $62, %ah
jnz 2b /* no, check the next slot */
movl %cs:4(%si), %eax /* StartLBA_Lo */
testl %eax, %eax
jnz 2b /* no, check the next slot */
movl %cs:12(%si), %eax /* S_count_Lo */
shrl $1, %eax
jnz 2b /* no, check the next slot */
2:
testl %ebx, %ebx /* mapped or not mapped ? */
jz 2f /* not mapped, do nothing */
movb %bl, %dl /* use the mapped FROM_DRIVE for win 98 */
2:
movb -6(%bp), %al /* AL=FROM_DRIVE */
testb %al, %al /* hard drive emulation? */
jns 2f /* floppy, jump */
cmpb %al, %dl
jb 2f
incb %dl
2:
popl %ebx
popl %eax
popw %si
#if 0
/* This does not work, although it seems it should work. */
int $0x13 /* call win 98 protected-mode int 13 */
ret
#else
/* This works on win98, but not tested under emm386. */
// pushfw
// lcall %cs:*(int13_offset - int13_handler)
// ret
/* so we can just fall through to real mode... */
#endif
real_mode_ROM_int13:
/* in real mode DOS, call original real mode int13 */
/* DS:SI points to disk address packet */
/* 2(%si), byte, sectors(1-127) */
/* 4(%si), word, offset */
/* 6(%si), word, segment */
/* 8(%si), qword, lba */
#if 0
/* debug code to test the win98 behavior */
/* disable the disk access in real mode. */
testw $0x400, -10(%bp) /* to_C */
jnz 1f
movb $0, 2(%si)
movb $2, %ah /* I/O error */
stc
ret
1:
#endif
pushw %ax /* 0x42 or 0x43 */
/* DS:SI is so important! Save it just in case it is destroyed by
* a buggy BIOS.
*/
#;jmp 1f /* test the CHS translation code */
pushw %ds
pushw %si
pushw %ax
smsw %ax
testb $1, %al
popw %ax
jz 4f
/* pmode */
testb $1, %cs:(int13_scheme - int13_handler)
jz 4f
int $0x13
//pushfw
//lcall %cs:*(int13_dup - int13_handler)
////lcall $0, $0x4C
jmp 2f
4:
/* real mode */
pushfw
lcall %cs:*(int13_dup - int13_handler)
2:
popw %si
popw %ds
jc 1f
testb %ah, %ah
jnz 1f
addw $2, %sp /* discard AX on the stack */
/* CF=0 */
ret
1:
/* try again using CHS mode */
/* byte at SI+2: number of sectors to access */
/* 8 bytes at SI+8: StartLBA */
/* CHS mode cannot access large addresses */
movl 12(%si), %eax
testl %eax, %eax
jnz 1f
pushl %edx
pushl %ebx
movw -10(%bp), %ax /* to_C */
andw $0x3FF, %ax /* get lo 10 bits */
incw %ax /* cylinders */
pushw %ax
movzbw -8(%bp), %ax /* to_H */
incw %ax /* heads */
movzbw -7(%bp), %bx /* to_S */
andb $0x3F, %bl
mulw %bx /* DX=0, AX=heads*sectors */
popw %bx /* cylinders */
mulw %bx /* DX:AX=total sectors in drive */
pushw %dx
pushw %ax
movzbl 2(%si), %eax
addl 8(%si), %eax /* last sector number + 1 */
popl %edx /* total sectors in drive */
cmpl %edx, %eax
popl %ebx
popl %edx
jnb 1f
/* all requested sectors can be accessed by CHS */
/* we will access one sector at a time */
popw %ax /* AH=0x42 or 0x43 */
pushw %ax /* AH=0x42 or 0x43 */
andb $0x0F, %ah /* AH=0x02 0r 0x03 */
movb %ah, %cs:(rw_self_mod + 2 - int13_handler)
pushw %es
pushw %bx
pushw %cx
pushl %edi
movzbw 2(%si), %cx /* sectors to transfer */
movl 8(%si), %edi /* start sector number(LBA) */
3:
/* translate LBA to CHS */
pushw %cx
/* get sectors per cylinder */
pushw %dx
pushl %edi /* lba */
movzbw -8(%bp), %ax /* to_H */
incw %ax /* heads */
movzbw -7(%bp), %bx /* to_S */
andb $0x3F, %bl
mulw %bx /* DX=0, AX=sectors per cylinder */
popw %cx /* lba_lo */
xchgw %ax, %cx /* CX=sectors per cylinder, AX=lba_lo */
popw %dx /* DX:AX=lba */
divw %cx /* AX=cylinder number, DX=rem */
xchgw %ax, %dx /* DX=cylinder number, AX=rem */
movb %dl, %ch /* CH=lo 8 bits of cylinder */
divb %bl /* AL=head number, AH=sector number - 1 */
movb %ah, %cl
incw %cx /* CL=sector number */
shlb $6, %dh /* hi 2 bits of cylinder */
orb %dh, %cl
popw %dx /* DL=drive number */
pushw %dx
movb %al, %dh /* DH=head number */
pushl %edi
movw %di, %ax
subw 8(%si), %di /* sectors already read */
shlw $5, %di /* paragraghs */
addw 6(%si), %di
movw 4(%si), %bx
movw %bx, %ax
shrw $4, %ax
andw $0x000F, %bx
addw %di, %ax /* segment */
movw %ax, %es
rw_self_mod:
movw $0x0201, %ax
/* DS:SI is so important! Save it just in case it is destroyed by
* a buggy BIOS.
*/
pushw %ds
pushw %si
pushw %ax
smsw %ax
testb $1, %al
popw %ax
jz 4f
/* pmode */
testb $1, %cs:(int13_scheme - int13_handler)
jz 4f
int $0x13
//pushfw
//lcall %cs:*(int13_dup - int13_handler)
////lcall $0, $0x4C
jmp 2f
4:
/* real mode */
pushfw
lcall %cs:*(int13_dup - int13_handler)
2:
popw %si
popw %ds
popl %edi
popw %dx
incl %edi /* next sector */
popw %cx
jc 3f
testb %ah, %ah
jnz 3f
loop 3b
popl %edi
popw %cx
popw %bx
popw %es
popw %ax
xorb %ah, %ah /* success */
ret
3:
popl %edi
popw %cx
popw %bx
popw %es
popw %ax
movb $0, 2(%si)
movb $2, %ah /* I/O error */
stc
ret
1:
popw %ax
movb $0, 2(%si)
movb $4, %ah /* sector not found */
stc
ret
modify_boot_sectors:
pushw %es
pushal
movw 2(%bp), %ax /* get original AX */
/* check CHS read */
cmpb $0x02, %ah /* is it read? */
jne 3f
cmpw $0x0001, %cx /* read from cylinder 0, sector 1? */
je 5f
3:
/* check LBA read */
cmpb $0x42, %ah /* is it extended read? */
jne 4f
cmpl $0, 12(%si) /* read from LBA_high=0? */
jne 4f
movb $0, %dh /* simulate CHS read of head 0 */
cmpl $0, 8(%si) /* read from LBA_low=0? */
je 3f
movzbl -3(%bp), %eax /* AL=Smax */
andb $63, %al
movb $1, %dh /* simulate CHS read of head 1 */
cmpl %eax, 8(%si) /* read from LBA_low=sectors per track? */
jne 4f
3:
movw 4(%si), %bx /* simulate CHS read buffer offset */
movw 6(%si), %es /* simulate CHS read buffer segment */
5:
#; testb %dl, %dl /* The TO_DRIVE is hard drive? */
#; jns 4f /* no, do nothing */
cmpw $0xaa55, %es:0x1fe(%bx)
jne 4f
movl -22(%bp), %eax /* S_count_Lo */
shrl $1, %eax
jz 4f /* map a whole drive, nothing need to change */
movl -14(%bp), %eax /* StartLBA_Lo */
testl %eax, %eax
jz 4f /* geometry translation only, needn't change */
movl -6(%bp), %eax /* FROM_DRIVE, TO_DRIVE, H, S */
testb %al, %al /* The FROM_DRIVE is hard drive? */
jns modify_floppy /* no, goto floppy boot record modification */
testb %dh, %dh /* read from head 0? */
jnz modify_HD_DOS /* no, goto HD DOS boot record modification */
/* we have read an MBR, and we need to modify the partition table */
/* the partition table could be an extended partition table. if so,
* we need to turn it to be a primary partition table.
*/
/* if all start_sectors are not sectors_per_track, then it is not
* an extended partition table.
*/
shrl $16, %eax /* AL=MaxH, AH=MaxS */
andb $63, %ah /* AH=MaxS */
// cmpb $1, %ah
// jbe 4f /* do not modify partition table when disable CHS mode */
movzbl %ah, %eax
movw $0x1c6, %si /* SI=0x1c6 */
cmpl %eax, %es:(%bx, %si)
jz 5f
addw $16, %si /* SI=0x1d6 */
cmpl %eax, %es:(%bx, %si)
jz 5f
addw $16, %si /* SI=0x1e6 */
cmpl %eax, %es:(%bx, %si)
jz 5f
addw $16, %si /* SI=0x1f6 */
cmpl %eax, %es:(%bx, %si)
jz 5f
3:
jmp 4f /* not an extended partition table */
5:
/* check if it is a primary partition table */
/* a primary partition table entry should have C/H/S=0/1/1 */
movl %es:-8(%bx, %si), %eax
shrl $8, %eax
cmpl $0x000101, %eax /* 0x00=C, 0x01=S, 0x01=H */
je 3b /* yes, it is primary partition table */
/* now we are sure this is an extended partition table */
/* compose a master boot record routine */
cld
#if 1
movw %bx, %di
movb $0xFA, %al /* cli */
stosb
movl $0xD08EC033, %eax /* xor AX,AX; mov SS,AX */
stosl
movl $0xFB7C00BC, %eax /* mov SP,7C00 ; sti */
stosl
movl $0x07501F50, %eax /* push AX; pop DS; push AX; pop ES */
stosl
movl $0x7C1CBEFC, %eax /* cld; mov SI,7C1C */
stosl
movl $0x50061CBF, %eax /* mov DI,061C ; push AX */
stosl
movl $0x01E4B957, %eax /* push DI ; mov CX, 01E4 */
stosl
movl $0x1ECBA4F3, %eax /* repz movsb; retf; push DS */
stosl
movl $0x537C00BB, %eax /* mov BX,7C00 ; push BX */
stosl
movl $0x520180BA, %eax /* mov DX,0180 ; push DX */
stosl
movl $0x530201B8, %eax /* mov AX,0201 ; push BX */
stosl
movl $0x5F13CD41, %eax /* inc CX; int 13; pop DI */
stosl
movl $0x5607BEBE, %eax /* mov SI,07BE ; push SI */
stosl
movl $0xCBFA5A5D, %eax /* pop BP; pop DX; cli; retf */
stosl
#endif
/* empty all other entries except this one by 2 steps: */
/* step 1. move this entry onto the first one (overwrite it) */
movl %es:-8(%bx, %si), %eax
movl %eax, %es:0x1be(%bx)
movl %es:-4(%bx, %si), %eax
movl %eax, %es:0x1c2(%bx)
movl %es:(%bx, %si), %eax
movl %eax, %es:0x1c6(%bx)
movl %es:4(%bx, %si), %eax
movl %eax, %es:0x1ca(%bx)
#if 1
/* step 2. empty the last 3 entries */
xorw %ax, %ax
movw %bx, %di
addw $0x1ce, %di
movw $24, %cx
repz stosw /* DI=BX+0x1fe */
#else
xorw %ax, %ax
movw %bx, %di
addw $0x1d1, %di
stosw /* DI=BX+0x1d3 */
addw $2, %di /* DI=BX+0x1d5 */
stosb /* DI=BX+0x1d6 */
addw $0x28, %di /* DI=BX+0x1fe */
#endif
/* modify the start_CHS of the first entry */
subw $0x40, %di /* DI=BX+0x1be */
movb $0x80, %al /* set boot indicator */
stosb /* DI=BX+0x1bf */
movb $0x01, %al /* AX=1 */
stosb /* DI=BX+0x1c0, H=0x01 */
stosw /* DI=BX+0x1c2, S=0x01, C=0x00 */
#if 0
movb $0x0B, %al
stosb /* DI=BX+0x1c3 */
#else
/* modify the end_CHS of the first entry */
incw %di /* DI=BX+0x1c3 */
#endif
/* get total sectors */
movl %es:7(%di), %eax /* DI+7=BX+0x1ca */
/* calculate the end sector number */
addl %es:3(%di), %eax /* DI+3=BX+0x1c6 */
//decl %eax
pushl %eax /* EAX-1=end sector number */
movzwl -4(%bp), %eax /* H, S */
andb $63, %ah
movzbl %ah, %ecx
mulb %cl
addw %cx, %ax /* EAX=sectors per cylinder */
pushl %eax /* EAX=sectors per cylinder */
movw $1024, %cx
mull %ecx /* EAX=CHS addressible total sectors */
/* EDX=0 */
popl %edx /* EDX=sectors per cylinder */
popl %ecx /* ECX-1=end sector number */
cmpl %eax, %ecx
jb 5f
/* assign max end sector number */
movl %eax, %ecx
//subl $0x3EC1, %ecx /* XXX: 0x3EC1=255*63 */
5:
pushl %ecx /* ECX-1=end sector number */
movl %edx, %ecx /* ECX=sectors per cylinder */
xorl %edx, %edx
popl %eax /* EAX-1=end sector number */
decl %eax
divl %ecx /* EAX=cylinder number */
/* EDX=sector number in the last cylinder */
/* EAX hi=0, EDX hi=0 */
xchgw %ax, %cx /* CX=cylinder number */
xchgw %ax, %dx /* AX=sector number in the last cylinder */
movb -3(%bp), %dl /* S */
andb $63, %dl
divb %dl /* AL=head number, AH=sector number - 1 */
stosb /* DI=BX+0x1c4 */
movb %ah, %al
incw %ax /* AL=sector number */
movb %cl, %ah /* cylinder lo 8 bits */
shlb $6, %ch /* cylinder hi 2 bits */
orb %ch, %al
//movb $0, %ah /* XXX: let cylinder lo 8 bits=0 */
stosw /* DI=BX+0x1c6 */
4:
/* end partition table modification */
popal
popw %es
ret
modify_floppy:
/* AL=FROM_DRIVE is the floppy drive number. */
cmpb $0x00, %dh /* read from head 0? */
jne 4b
#; xorl %eax, %eax
#; cmpl %eax, %es:0x1c(%bx) /* Number of hidden sectors */
#; je 4b
#; cmpl $0x33544146, %es:0x52(%bx) /* FAT32? */
#; je 5f
#; /* NTFS drive number is also at offset 0x24 */
#; //cmpl $0x31544146, %es:0x36(%bx) /* FAT16? */
#; //jne 4b
#; cmpw $0x80, %es:0x24(%bx) /* Physical drive number */
#; jne 4b
#; movb %al, %es:0x24(%bx) /* AL=floppy drive number */
#; jmp 6f
#;5:
#; cmpb $0x80, %es:0x40(%bx) /* Physical drive number */
#; jne 4b
#; movb %al, %es:0x40(%bx) /* AL=0 means floppy */
#;6:
#; movl %eax, %es:0x1c(%bx) /* let number of hidden sectors=0 */
#; /*movb $0xf0, %es:0x15(%bx)*/ /* set floppy media descriptor */
#; jmp 4b
pushl %ecx
pushw %si
/* FAT12/FAT16/NTFS drive number is at offset 0x24. */
movw $0x24, %si
xorl %ecx, %ecx
/* check if it is FAT32. */
/* FAT32 should have 0 root_dir_entries and total_sectors_short. */
cmpl %ecx, %es:0x11(%bx)
jne 5f /* not FAT32 */
/* FAT32 should have 0 sectors_per_fat. */
cmpw %cx, %es:0x16(%bx)
jne 5f /* not FAT32 */
/* FAT32 should have non-zero total_sectors_long. */
cmpl %ecx, %es:0x20(%bx)
je 5f /* not FAT32 */
/* FAT32 should have non-zero sectors_per_fat32. */
cmpl %ecx, %es:0x24(%bx)
je 5f /* not FAT32 */
/* Now it is FAT32, and the drive number is at offset 0x40. */
movw $0x40, %si
5:
movb %al, %es:(%bx, %si) /* modify the boot drive number. */
movl %ecx, %es:0x1c(%bx) /* let number of hidden sectors=0 */
/*movb $0xf0, %es:0x15(%bx)*/ /* set floppy media descriptor */
popw %si
popl %ecx
jmp 4b
modify_HD_DOS:
// cmpb $0x42, 3(%bp) /* is extended read? */
// je 4b
cmpb $0x01, %dh /* read from head 1? */
jne 4b
movl -14(%bp), %eax /* StartLBA_Lo */
testl %eax, %eax
jz 4b
cmpl %eax, %es:0x1c(%bx) /* Number of hidden sectors */
jbe 4b
subl %eax, %es:0x1c(%bx)
movzbl -3(%bp), %eax /* AL=Smax */
andb $63, %al
cmpl %eax, %es:0x1c(%bx) /* Number of hidden sectors */
jnb 4b
movl %eax, %es:0x1c(%bx)
jmp 4b
modify_in_situ:
pushw %es
pushal
//movw 2(%bp), %ax /* get original AX */
/* if FROM is not harddrive, do nothing. */
testb $0x80, -6(%bp) /* FROM drive */
jz 4f
cmpb $0x02, %ah /* is it CHS read? */
jne 3f
/* AL=sectors read */
/* translate CHS to LBA */
movw %dx, %di /* save DX to DI */
movw %cx, %si /* save CX to SI */
movb -7(%bp), %al /* TO_S */
andb $63, %al
movzbw %al, %dx
mulb -8(%bp)
addw %dx, %ax /* AX=sectors_per_cylinder */
/* get current cylinder number */
shrb $6, %cl
xchgb %cl, %ch
mulw %cx /* DX:AX=sectors */
pushw %dx
pushw %ax
/* restore DX */
movw %di, %dx
/* get current head number */
movb -7(%bp), %al /* TO_S */
andb $63, %al
mulb %dh /* AX=sectors */
popl %edx
movzwl %ax, %eax
addl %edx, %eax
movw %si, %cx /* restore CX */
andb $0x3F, %cl
decw %cx
movzbl %cl, %ecx
addl %ecx, %eax /* EAX=start sector number */
movzbl 2(%bp), %ecx /* sectors read */
jmp 6f
3:
cmpb $0x42, %ah /* is it LBA read? */
jne 4f
cmpl $0, 12(%si) /* read from LBA_high=0? */
jne 4f
movl 8(%si), %eax /* start sector number */
movzbl 2(%si), %ecx /* sectors read */
movw 4(%si), %bx /* simulate CHS read buffer offset */
movw 6(%si), %es /* simulate CHS read buffer segment */
6:
#; testb %dl, %dl /* The TO_DRIVE is hard drive? */
#; jns 4f /* no, do nothing */
/* ECX=sectors read */
cmpl $0, %eax /* start sector number 0 is for MBR */
je 5f
/* if EAX <= startLBA_Lo < EAX + ECX, then the boot sector is read. */
cmpl -14(%bp), %eax /* startLBA_Lo */
ja 4f /* boot sector was not read */
addl %eax, %ecx
cmpl -14(%bp), %ecx
jbe 4f /* boot sector was not read */
movl -14(%bp), %ecx
subl %eax, %ecx /* sectors between ES:BX and boot record */
/* modify hidden sectors of the partition boot record */
cmpb $0x07, -10(%bp) /* NTFS */
je 3f
cmpb $0x0C, -10(%bp) /* FAT32(LBA) */
je 3f
cmpb $0x0E, -10(%bp) /* FAT12/16 */
je 3f
cmpb $0x83, -10(%bp) /* EXT2 */
jne 4f
3:
movw %es, %ax
shlw $5, %cx /* 1 sector is 32 paragraphs */
addw %cx, %ax
movw %ax, %es
cmpw $0xaa55, %es:0x1fe(%bx)
jne 4f
movl -14(%bp), %eax /* startLBA_Lo */
movl %eax, %es:0x1c(%bx)
jmp 4f
5:
/* Modify partition table. Note that there are 4 partition entries.
* The first one is called entry 0, and the last is entry 3. */
cmpw $0xaa55, %es:0x1fe(%bx)
jne 4f
leaw 0x1be(%bx), %di /* DI=BX+0x1be */
movw %di, %si
/* if the first entry is empty, we simply put our new entry on it. */
cmpb $0, %es:4(%si) /* consider partition type 00 as empty */
jz 5f
testb $63, %es:2(%si) /* invalid start sector number of 0 */
jz 5f
testb $63, %es:6(%si) /* invalid end sector number of 0 */
jz 5f
cmpl $0, %es:8(%si) /* invalid start LBA of 0 */
jz 5f
cmpl $0, %es:12(%si) /* invalid sector count of 0 */
jz 5f
/* Now that the first entry is not empty, we should find an empty one
* in entries 1, 2, 3(the last 3 partition entries). */
7:
addw $0x10, %si /* SI=BX+0x1ce or BX+0x1de or BX+0x1ee */
cmpb $0, %es:4(%si) /* consider partition type 00 as empty */
jz 7f
testb $63, %es:2(%si) /* invalid start sector number of 0 */
jz 7f
testb $63, %es:6(%si) /* invalid end sector number of 0 */
jz 7f
cmpl $0, %es:8(%si) /* invalid start LBA of 0 */
jz 7f
cmpl $0, %es:12(%si) /* invalid sector count of 0 */
jz 7f
leaw 0x1ee(%bx), %bx
cmpw %bx, %si
leaw -0x1ee(%bx), %bx
jb 7b /* try next entry */
/* Now SI=BX+0x1ee points to the last entry */
7:
/* SI points to empty entry(or the last entry), we move old entry 0
* onto this one.
*/
movl %es:(%di), %eax
movl %eax, %es:(%si)
movl %es:4(%di), %eax
movl %eax, %es:4(%si)
movl %es:8(%di), %eax
movl %eax, %es:8(%si)
movl %es:12(%di), %eax
movl %eax, %es:12(%si)
5:
/* build our new entry 0 */
/* DI=BX+0x1be */
movl -14(%bp), %eax /* startLBA_Lo */
call lba_to_chs
movb $0x80, %al /* set boot indicator */
//movb $0x00, %al /* clear boot indicator */
stosl /* DI=BX+0x1c2 */
movl -22(%bp), %eax
addl -14(%bp), %eax
decl %eax /* endLBA */
call lba_to_chs
/* if in situ, TO_C holds the partition type:
* 0x07(NTFS), 0x0C(FAT32), 0x0E(FAT12/16), 0x83(EXT2/3)
*/
movb -10(%bp), %al /* TO_C */
stosl /* DI=BX+0x1c6 */
/* modify start sector and sector count */
movl -14(%bp), %eax
stosl /* DI=BX+0x1ca */
movl -22(%bp), %eax
stosl /* DI=BX+0x1ce */
#if 1
/* clear other boot indicators in the last 3 entries */
movb $0, %al /* DI=BX+0x1ce */
stosb /* DI=BX+0x1cf */
leaw 0x1de(%bx), %di /* DI=BX+0x1de */
stosb /* DI=BX+0x1df */
leaw 0x1ee(%bx), %di /* DI=BX+0x1ee */
stosb /* DI=BX+0x1ef */
#endif
//#if 0
// /* hide entry 3 (test only) */
// orb $0x10, %es:0x1f2(%bx)
//#endif
#if 0
/* move extended partition entries to last */
cmpb $0x0F, %es:0x1f2(%bx)
je 4f /* entry 3 is extended, do nothing */
cmpb $0x05, %es:0x1f2(%bx)
je 4f /* entry 3 is extended, do nothing */
cmpb $0x0F, %es:0x1e2(%bx)
je 7f /* entry 2 is extended */
cmpb $0x05, %es:0x1e2(%bx)
je 7f /* entry 2 is extended */
/* both entry 2 and entry 3 are not extended */
cmpb $0x0F, %es:0x1d2(%bx)
je 8f /* entry 1 is extended */
cmpb $0x05, %es:0x1d2(%bx)
jne 4f /* all entries 1, 2, 3 are not extended, do nothing */
8:
/* entry 1 is extended, but entry 2 and entry 3 are not extended.
* so exchange entry 1 and entry 3. */
xchgl %eax, %es:0x1ce(%bx)
xchgl %eax, %es:0x1ee(%bx)
xchgl %eax, %es:0x1ce(%bx)
xchgl %eax, %es:0x1d2(%bx)
xchgl %eax, %es:0x1f2(%bx)
xchgl %eax, %es:0x1d2(%bx)
xchgl %eax, %es:0x1d6(%bx)
xchgl %eax, %es:0x1f6(%bx)
xchgl %eax, %es:0x1d6(%bx)
xchgl %eax, %es:0x1da(%bx)
xchgl %eax, %es:0x1fa(%bx)
xchgl %eax, %es:0x1da(%bx)
jmp 4f
7:
/* entry 2 is extended, but entry 3 is not. if entry 1 is extended,
* we exchange entry 1 and 3, else, we exchange entry 2 and 3. */
*/
cmpb $0x0F, %es:0x1d2(%bx)
je 8b /* entry 1 is extended */
cmpb $0x05, %es:0x1d2(%bx)
je 8b /* entry 1 is extended */
/* exchange entry 2 and 3 */
xchgl %eax, %es:0x1de(%bx)
xchgl %eax, %es:0x1ee(%bx)
xchgl %eax, %es:0x1de(%bx)
xchgl %eax, %es:0x1e2(%bx)
xchgl %eax, %es:0x1f2(%bx)
xchgl %eax, %es:0x1e2(%bx)
xchgl %eax, %es:0x1e6(%bx)
xchgl %eax, %es:0x1f6(%bx)
xchgl %eax, %es:0x1e6(%bx)
xchgl %eax, %es:0x1ea(%bx)
xchgl %eax, %es:0x1fa(%bx)
xchgl %eax, %es:0x1ea(%bx)
#endif
// /* empty the last 3 entries */
// xorw %ax, %ax
// movw %bx, %di
// addw $0x1ce, %di
// movw $24, %cx
// repz stosw /* DI=BX+0x1fe */
4:
/* end partition table and BPB modification */
popal
popw %es
ret
lba_to_chs:
/* input:
*
* EAX: LBA
*
* output:
*
* EAX: CHS
*
*
*
*/
pushl %eax /* EAX=end sector number */
movzwl -8(%bp), %eax /* TO_H, TO_S */
andb $63, %ah
movzbl %ah, %ecx
mulb %cl
addw %cx, %ax /* EAX=sectors per cylinder */
pushl %eax /* EAX=sectors per cylinder */
movw $1024, %cx
mull %ecx /* EAX=CHS addressible total sectors */
/* EDX=0 */
popl %edx /* EDX=sectors per cylinder */
popl %ecx /* ECX=end sector number */
decl %eax
cmpl %eax, %ecx
jb 5f
/* assign max end sector number */
movl %eax, %ecx
//subl $0x3EC1, %ecx /* XXX: 0x3EC1=255*63 */
5:
pushl %ecx /* ECX=end sector number */
movl %edx, %ecx /* ECX=sectors per cylinder */
xorl %edx, %edx
popl %eax /* EAX=end sector number */
divl %ecx /* EAX=cylinder number */
/* EDX=sector number in the last cylinder */
/* EAX hi=0, EDX hi=0 */
xchgw %ax, %cx /* CX=cylinder number */
xchgw %ax, %dx /* AX=sector number in the last cylinder */
movb -7(%bp), %dl /* TO_S */
andb $63, %dl
divb %dl /* AL=head number, AH=sector number - 1 */
//stosb /* DI=BX+0x1c4 */
xchgb %ah, %al /* AH=head number, AL=sector number - 1 */
pushw %ax
//movb %ah, %al
incw %ax /* AL=sector number */
movb %cl, %ah /* cylinder lo 8 bits */
shlb $6, %ch /* cylinder hi 2 bits */
orb %ch, %al
//movb $0, %ah /* XXX: let cylinder lo 8 bits=0 */
//stosw /* DI=BX+0x1c6 */
shll $16, %eax
popw %ax
ret
ENTRY(atapi_dev_count) .long 0
ENTRY(min_cdrom_id) .long 0xE0
max_cdrom_id: .long 0xE0
ENTRY(memdisk_raw) .long 1 /* set to 0 if accessing memdrives using int15/AH=87h */
minimum_mem_lo_in_map:
.long 0 /* min ram drive base below 16M */
minimum_mem_hi_in_map:
.long 0 /* min ram drive base above 16M */
int15_e820_handler:
/* Comments are mostly gotten from Ralf Brown's Interrupt List. */
cmpw $0xe820, %ax //cmpl $0x0000e820, %eax
jne 1f
/* Newer BIOSes - GET SYSTEM MEMORY MAP */
//AX = E820h
//EAX = 0000E820h
//EDX = 534D4150h ('SMAP')
//EBX = continuation value or 00000000h to start at beginning of map
//ECX = size of buffer for result, in bytes (should be >= 20 bytes)
//ES:DI -> buffer for result
//Return:
//CF clear if successful
//EAX = 534D4150h ('SMAP')
//ES:DI buffer filled
//EBX = next offset from which to copy or 00000000h if all done
//ECX = actual length returned in bytes
//CF set on error
//AH = error code (86h)
/* Notes: Originally introduced with the Phoenix BIOS v4.0, this
* function is now supported by most newer BIOSes, since various
* versions of Windows call it to find out about the system memory.
* A maximum of 20 bytes will be transferred at one time, even if ECX
* is higher; some BIOSes (e.g. Award Modular BIOS v4.50PG) ignore the
* value of ECX on entry, and always copy 20 bytes. Some BIOSes expect
* the high word of EAX to be clear on entry, i.e. EAX=0000E820h. If
* this function is not supported, an application should fall back to
* AX=E802h, AX=E801h, and then AH=88h. The BIOS is permitted to return
* a nonzero continuation value in EBX and indicate that the end of the
* list has already been reached by returning with CF set on the next
* iteration. This function will return base memory and ISA/PCI memory
* contiguous with base memory as normal memory ranges; it will
* indicate chipset-defined address holes which are not in use and
* motherboard memory-mapped devices, and all occurrences of the system
* BIOS as reserved; standard PC address ranges will not be reported
*/
//Format of Phoenix BIOS system memory map address range descriptor:
//
//Offset Size Description
//------ ----- ----------------------
// 00h QWORD base address
// 08h QWORD length in bytes
// 10h DWORD type of address range
//Values for System Memory Map address type:
//01h memory, available to OS
//02h reserved, not available (e.g. system ROM, memory-mapped device)
//03h ACPI Reclaim Memory (usable by OS after reading ACPI tables)
//04h ACPI NVS Memory (OS is required to save this memory between NVS sessions)
//other not defined yet -- treat as Reserved
cmpl $0x534D4150, %edx /* "SMAP" */
jne 1f
cmpl $20, %ecx
jb 2f
pushfw
lcall %cs:*(ROM_int15 - int13_handler)
jc 3f
cmpl $0x534D4150, %eax /* "SMAP" */
jne 2f
pushal
movl %es:4(%di), %eax /* BaseAddrHigh */
testl %eax, %eax
jnz 4f
movl %es:16(%di), %eax /* Type */
decl %eax /* 1=usable memory, available to the operating system */
jnz 4f
/* set %si to the drive map */
movw $(hooked_drive_map - int13_handler), %si
movw $(DRIVE_MAP_SIZE), %cx
6:
cmpb $0xff, %cs:1(%si)
jne 5f
movl %cs:8(%si), %ebx /* start_sector */
shll $9, %ebx
movl %es:4(%di), %eax /* BaseAddrHigh */
testl %eax, %eax
jnz 5f
cmpl %es:(%di), %ebx /* BaseAddrLow */
jb 5f
movl %es:12(%di), %eax /* LengthHigh */
testl %eax, %eax
jnz 7f
movl %es:8(%di), %eax /* LengthLow */
addl %es:(%di), %eax
jc 7f
cmpl %eax, %ebx
jnb 5f
7:
subl %es:(%di), %ebx /* new length */
movl %ebx, %es:8(%di) /* LengthLow */
xorl %ebx, %ebx
movl %ebx, %es:12(%di) /* LengthHigh */
//jmp 4f
5:
/* try next slot */
addw $DRIVE_MAP_SLOT_SIZE, %si
loop 6b
//memory block length update done
4:
popal
clc
lret $2
2:
movb $0x86, %ah /* function not supported */
3:
stc
lret $2
#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1:
cmpw $0xe801, %ax //cmpl $0x0000e801, %eax
je 1f
cmpw $0xe881, %ax //cmpl $0x0000e881, %eax
je 1f
cmpw $0xda88, %ax
je 1f
cmpb $0xc7, %ah
je 1f
cmpb $0x8a, %ah
je 1f
cmpb $0x88, %ah
je 1f
ljmp %cs:*(ROM_int15 - int13_handler)
#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1:
pushal
/* find minimum mem ever used in the drive map slots */
movl $-1, %eax /* 0xffffffff */
movl %eax, %cs:(minimum_mem_hi_in_map - int13_handler)
movl $0x1000000, %cs:(minimum_mem_lo_in_map - int13_handler)
/* set %si to the drive map */
movw $(hooked_drive_map - int13_handler), %si
movw $(DRIVE_MAP_SIZE), %cx
6:
cmpb $0xff, %cs:1(%si)
jne 5f
movl %cs:8(%si), %ebx /* StartLBA_Lo */
shll $9, %ebx
cmpl $0x1000000, %ebx /* 16M */
jb 7f
/* hi mem */
cmpl %ebx, %cs:(minimum_mem_hi_in_map - int13_handler)
jbe 5f
movl %ebx, %cs:(minimum_mem_hi_in_map - int13_handler)
jmp 5f
7:
/* lo mem */
cmpl %ebx, %cs:(minimum_mem_lo_in_map - int13_handler)
jbe 5f
movl %ebx, %cs:(minimum_mem_lo_in_map - int13_handler)
5:
/* try next slot */
addw $DRIVE_MAP_SLOT_SIZE, %si
loop 6b
popal
cmpl $-1, %cs:(minimum_mem_hi_in_map - int13_handler)
jne 1f
cmpl $0x1000000, %cs:(minimum_mem_lo_in_map - int13_handler)
jne 1f
ljmp %cs:*(ROM_int15 - int13_handler)
#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1:
cmpw $0xe801, %ax //cmpl $0x0000e801, %eax
jne 1f
/* Phoenix BIOS v4.0 - GET MEMORY SIZE FOR >64M CONFIGURATIONS */
//AX = E801h
//Return:
//CF clear if successful
//AX = extended memory between 1M and 16M, in K (max 3C00h = 15MB)
//BX = extended memory above 16M, in 64K blocks
//CX = configured memory 1M to 16M, in K
//DX = configured memory above 16M, in 64K blocks
//CF set on error
/* Notes: Supported by the A03 level (6/14/94) and later XPS P90
* BIOSes, as well as the Compaq Contura, 3/8/93 DESKPRO/i, and
* 7/26/93 LTE Lite 386 ROM BIOS. Supported by AMI BIOSes dated
* 8/23/94 or later. On some systems, the BIOS returns AX=BX=0000h;
* in this case, use CX and DX instead of AX and BX. This interface
* is used by Windows NT 3.1, OS/2 v2.11/2.20, and is used as a
* fall-back by newer versions if AX=E820h is not supported. This
* function is not used by MS-DOS 6.0 HIMEM.SYS when an EISA machine
* (for example with parameter /EISA) (see also MEM F000h:FFD9h), or
* no Compaq machine was detected, or parameter /NOABOVE16 was given.
*/
//movw $0x3c00, %ax /* 1-16M mem in K (0x3c00KB = 15MB) */
//movw $0x0300, %bx /* mem above 16M in 64K blocks */
4:
pushfw
pushfw
lcall %cs:*(ROM_int15 - int13_handler)
jc 3f
pushl %eax
/* between 16M and 4G, we modify BX, DX */
movl %cs:(minimum_mem_hi_in_map - int13_handler), %eax
cmpl $-1, %eax /* 4G - 1 */
je 5f
subl $0x1000000, %eax /* 16M */
shrl $16, %eax /* AX=mem above 16M in 64K blocks */
movw %ax, %bx
movw %ax, %dx
popl %eax
popfw
pushfw
pushl %eax
jnc 5f
movzwl %bx, %ebx
movzwl %dx, %edx
5:
popl %eax
pushl %eax
/* between 1M and 16M, we modify AX, CX */
movl %cs:(minimum_mem_lo_in_map - int13_handler), %eax
cmpl $0x1000000, %eax /* 16M */
je 5f
subl $0x0100000, %eax /* 1M */
shrl $10, %eax /* AX=1-16M mem in K */
movw %ax, %cx
popl %eax
movw %cx, %ax
popfw
pushfw
pushl %eax
jnc 5f
popl %eax
movzwl %ax, %eax
pushl %eax
movzwl %cx, %ecx
5:
popl %eax
popfw
clc
lret $2
3:
popfw
stc
lret $2
#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1:
cmpw $0xe881, %ax //cmpl $0x0000e881, %eax
jne 1f
/* Phoenix BIOS v4.0 - GET MEMORY SIZE FOR >64M CONFIGURATIONS (32-bit) */
//AX = E881h
//Return:
//CF clear if successful
//EAX = extended memory between 1M and 16M, in K (max 3C00h = 15MB)
//EBX = extended memory above 16M, in 64K blocks
//ECX = configured memory 1M to 16M, in K
//EDX = configured memory above 16M, in 64K blocks
//CF set on error
/* Notes: Supported by AMI BIOSes dated 8/23/94 or later. This
* interface is used by Windows NT 3.1, OS/2 v2.11/2.20, and is used
* as a fall-back by newer versions if AX=E820h is not supported
*/
//movl $0x3c00, %eax /* 1-16M mem in K (0x3c00 = 15MB) */
//movl $0x0300, %ebx /* mem above 16M in 64K blocks */
stc
jmp 4b
#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1:
cmpw $0xda88, %ax
jne 1f
/* AMI PCI BIOS - GET EXTENDED MEMORY SIZE */
//AX = DA88h
//Return:
//CF clear (successful)
//AX = 0000h
//CL:BX = extended memory size in KBytes
/* 63M = 3f00000 Bytes = 0xfc00 K */
//movw $0xfc00, %bx
#; pushfw
#; lcall %cs:*(ROM_int15 - int13_handler)
#;
#; jc 3f
pushl %eax
movl %cs:(minimum_mem_lo_in_map - int13_handler), %eax
cmpl $0x1000000, %eax /* 16M */
jb 5f
movl %cs:(minimum_mem_hi_in_map - int13_handler), %eax
5:
subl $0x0100000, %eax
shrl $10, %eax /* EAX=extened mem in K */
xchgw %ax, %bx
shrl $16, %eax
movb %al, %cl
popl %eax
xorw %ax, %ax
clc
lret $2
#;3:
#; stc
#; lret $2
#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1:
cmpb $0xC7, %ah
jne 1f
/* SYSTEM - later PS/2s - RETURN MEMORY-MAP INFORMATION */
//AH = C7h
//DS:SI -> user supplied memory map table
//Return:
//CF set on error
//CF clear if successful
/* Notes: Call AH=C0h and examine bit 4 of feature byte 2 to check if
* this function is supported. IBM classifies this function as
* optional. Windows95 OSR2 reportedly does not support this function
* even though INT 15/AH=C0h indicates that it is available (observed
* on several different systems)
*/
//Format of memory-map table structure:
//
//Offset Size Description
//------ ----- --------------------------------------------------------------
// 00h WORD length of table (excluding this word)
// 02h DWORD local memory between 1M and 16M, in 1K blocks
// 06h DWORD local memory between 16M and 4G, in 1K blocks
// 0Ah DWORD system memory between 1M and 16M, in 1K blocks
// 0Eh DWORD system memory between 16M and 4G, in 1K blocks
// 12h DWORD cacheable memory between 1M and 16M, in 1K blocks
// 16h DWORD cacheable memory between 16M and 4G, in 1K blocks
// 1Ah DWORD 1K blocks before start of non-system memory between 1M and 16M
// 1Eh DWORD 1K blocks before start of non-system memory between 16M and 4G
// 22h WORD start segment of largest free block from C0000h-DFFFFh
// 24h WORD size of largest free block
// 26h DWORD reserved
//movw $0x0028, (%si)
pushw %ds
pushw %si
pushfw
lcall %cs:*(ROM_int15 - int13_handler)
popw %si
popw %ds
jc 3f
pushl %eax
/* between 16M and 4G */
movl %cs:(minimum_mem_hi_in_map - int13_handler), %eax
cmpl $-1, %eax /* 4G - 1 */
je 5f
subl $0x1000000, %eax /* 16M */
shrl $10, %eax /* AX=mem above 16M in 1K blocks */
movl %eax, 0x0e(%si)
movl %eax, 0x1e(%si)
5:
/* between 1M and 16M */
movl %cs:(minimum_mem_lo_in_map - int13_handler), %eax
cmpl $0x1000000, %eax /* 16M */
je 5f
subl $0x0100000, %eax /* 1M */
shrl $10, %eax /* AX=1-16M mem in K */
movl %eax, 0x0a(%si)
movl %eax, 0x1a(%si)
5:
popl %eax
clc
lret $2
3:
stc
lret $2
#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1:
cmpb $0x8a, %ah
jne 1f
/* Phoenix BIOS v4.0 - GET BIG MEMORY SIZE */
//AH = 8Ah
//Return:
//DX:AX = extended memory size in K
//movw $0xfc00, %ax
#; pushfw
#; lcall %cs:*(ROM_int15 - int13_handler)
#;
#; jc 3f
pushl %eax
movl %cs:(minimum_mem_lo_in_map - int13_handler), %eax
cmpl $0x1000000, %eax /* 16M */
jb 5f
movl %cs:(minimum_mem_hi_in_map - int13_handler), %eax
5:
subl $0x0100000, %eax
shrl $10, %eax /* EAX=extened mem in K */
popw %dx
pushw %ax
shrl $16, %eax
xchgw %ax, %dx
popl %eax
clc
lret $2
#;3:
#; stc
#; lret $2
#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1:
cmpb $0x88, %ah
jne 1f
/* SYSTEM - GET EXTENDED MEMORY SIZE (286+) */
//AH = 88h
//Return:
//CF clear on success(Not all BIOSes correctly return the carry flag)
//AX = number of contiguous KB starting at absolute address 100000h
//CF set on error
//AH = status
// 80h invalid command (PC,PCjr)
// 86h unsupported function (XT,PS30)
//movw $0xfc00, %ax
#; pushfw
#; lcall %cs:*(ROM_int15 - int13_handler)
#;
#; jc 3f
pushl %eax
movl %cs:(minimum_mem_lo_in_map - int13_handler), %eax
cmpl $0x1000000, %eax /* 16M */
jb 5f
movl %cs:(minimum_mem_hi_in_map - int13_handler), %eax
5:
subl $0x0100000, %eax
shrl $10, %eax /* EAX=extened mem in K */
cmpl $0x10000, %eax
jb 5f
movw $0xFFFF, %ax
5:
addw $2, %sp
pushw %ax
popl %eax
clc
lret $2
#;3:
#; stc
#; lret $2
1:
ljmp %cs:*(ROM_int15 - int13_handler)
#include "a20.inc"
// Input:
// dl=0 - Disable A20
// dl=1 - Eanble A20
int13_enable_a20:
movw $0x2400,%ax
addb %dl, %al
stc
pushfw
lcall %cs:*(ROM_int15 - int13_handler)
jnc int13_a20_gloop2ret
call int13_a20_gloop1
movb $KC_CMD_WOUT, %al
outb $K_CMD
int13_a20_gloopint1:
inb $K_STATUS
andb $K_IBUF_FUL, %al
jnz int13_a20_gloopint1
movb $KB_OUTPUT_MASK, %al
orb %dl, %dl
jz int13_a20_gdoit
orb $KB_A20_ENABLE, %al
int13_a20_gdoit:
outb $K_RDWR
call int13_a20_gloop1
/* output a dummy command (USB keyboard hack) */
movb $0xff, %al
outb $K_CMD
call int13_a20_gloop1
jmp int13_a20_gloop2ret
int13_a20_gloop1:
inb $K_STATUS
andb $K_IBUF_FUL, %al
jnz int13_a20_gloop1
int13_a20_gloop2:
inb $K_STATUS
andb $K_OBUF_FUL, %al
jz int13_a20_gloop2ret
inb $K_RDWR
jmp int13_a20_gloop2
int13_a20_gloop2ret:
ret
__reg_select_dev:
#; ax = dev
#; simple version of the select dev
addb $0x0A, %al #; AL= 0x0A or 0x0B
shlb $4, %al #; AL= 0xA0 or 0xB0
movw reg_addr - int13_handler + (6 * 2), %dx #; CB_DH = drive select
outb %al, %dx
call delay400ns
ret
reg_select_dev:
#; input: ax = bx = dev
#; set_timeout first
// movw %ax, %bx
cmpb $2, (reg_dev_info - int13_handler)(%bx) # REG_CONFIG_TYPE_ATA
jb __reg_select_dev # not ATA
/* The device is ATA */
// pushw %bx
call reg_poll_busy # change AL, DX
#if 0
orw %ax, %ax
jnz 1f # return if,{or ax,ax},nz
#else
ja 1f # timeout, failure
#endif
movw %bx, %ax
call __reg_select_dev # change AL, DX
#ifndef USE_ATA
call reg_poll_busy # change AL, DX
#if 0
jmp 1f
#else
#if 0
orw %ax, %ax
jz 1f
#else
jna 1f
#endif
#endif
#else //; USE_ATA
3:
movw reg_addr - int13_handler + (7 * 2), %dx #; CB_STAT
inb %dx, %al
cmpb $2, (reg_dev_info - int13_handler)(%bx) # REG_CONFIG_TYPE_ATA
jne 4f
andb $(CB_STAT_BSY | CB_STAT_RDY | CB_STAT_SKC), %al
# return if,{cmp al,CB_STAT_RDY|CB_STAT_SKC},e
cmpb $(CB_STAT_RDY | CB_STAT_SKC), %al
je 1f
jmp 3b
4:
# return if,{test al,CB_STAT_BSY},z
testb $CB_STAT_BSY, %al
jz 1f
jmp 3b
#endif //; USE_ATA
call __reg_select_dev
1:
// popw %bx
ret
select_atapi:
cmpw atapi_cur_dev - int13_handler, %ax # device serial number
jne select_atapi_force
//clc #; CF already cleared
ret
select_atapi_force:
#; input: ax = device serial number
#; return: cf =0 success, cf =1 failed
pushaw
pushw %es
pushw %cs
popw %es
cmpw atapi_dev_count - int13_handler, %ax
cmc
jb 2f
movw %ax, atapi_cur_dev - int13_handler # device serial number
movw $atapi_dev_base - int13_handler, %si # array of ATAPI reg pointer and dev
shlw $2, %ax # each element of array has 4 bytes
addw %ax, %si
movw (%si), %bx # BX is the reg pointer of the current device
call reg_setup_base_addr # fill the 10-word reg_addr area
# change no registers
movw 2(%si), %bx # BX is the dev number of the current device
movw %bx, %ax
call set_timeout # set timeout for reg_select_dev
# change no registers
call reg_select_dev #; reg_select_dev
addb $0x0A, %bl #; BL= 0x0A or 0x0B
shlb $4, %bl #; BL= 0xA0 or 0xB0
movb %bl, reg_cur_dev - int13_handler
clc
2:
pop %es
popaw
ret
read_atapi:
#;input: es:di -> buffer, cx = sector count, edx = lba address
#;return: cf =0 success, cx = number of bytes actually read
pushaw
orw %cx, %cx
jz 3f
1:
pushw %cx
pushl %edx
call clear_atapi_buffer
movb $0x28, atapi_cmd_buffer - int13_handler
bswapl %edx
movl %edx, atapi_cmd_buffer - int13_handler + 2
movb $1, atapi_cmd_buffer - int13_handler + 8
# invoke reg_packet,byte, 0, es, di, REG_ATAPI_MAX_BYTES
pushw $0x8000 #; REG_ATAPI_MAX_BYTES
pushw %di
pushw %es
pushw $0
call reg_packet
addw $8, %sp
orw %ax, %ax
jnz 2f
cmpw $0x800, %cx #; CDSECTOR_SIZE
jne 2f
popl %edx
incl %edx
addw %cx, %di
popw %cx
loop 1b
clc
jmp 4f
2:
popl %edx
popw %cx
3:
stc
4:
popaw
ret
read_bios_time:
#; return EAX as the long time
pushw %ds
xorw %ax, %ax
movw %ax, %ds
movl 0x46c, %eax
popw %ds
ret
edd30_read_cdrom:
#;return cf=0 success, cf=1 fail, ah = fail code
#;will change all registers, including ES!!
call test_atapi_ready
movb $0xAA, %ah # error code if CF=1
jc 4f
movw %ds, %ax # save DS
movw -18(%bp), %ds # get DS for the disk address packet
movw 2(%si), %cx # struc_int13ext.blk_count
movw 4(%si), %di # struc_int13ext.buf_addr_off
movw 6(%si), %bx # struc_int13ext.buf_addr_seg
movl 8(%si), %edx # struc_int13ext.blk_num_low1
cmpb $16, (%si) # valid packet size is 16
movw %ax, %ds # restore DS
setne %ah # AH=1 for invalid function call
stc # error out if ...
jne 4f # ... invalid packet size
movw %bx, %es # ES changed!!
call read_atapi
movb $0x0C, %ah # error code if CF=1
4:
// /* debug print AX and FLAGS */
// pushfw
// pushw %ax
// pushw $read_cdrom_ah_flags - int13_handler # the format string
// call realmode_printf
// popw %ax
// popw %ax
// popfw
ret
//read_cdrom_ah_flags:
// .ascii "edd30_read_cdrom: AX=%X, FLAGS=%X\r\n\0"
reg_reset:
#; call after reg_probe_exist
pushaw
#; mov_ax 0
#; call __reg_select_dev
#if 0
/* The ATA software reset mechanism, SRST, (bit 2 in the Device Control
* Register) cannot be used for ATAPI Device, because resets issued by
* the ATAPI driver would also reset any attached hard disk and vice
* versa. To solve this, ATAPI defines an ATAPI Soft Reset command
* using a reserved ATA opcode which could be decoded by the interface
* controller hardware.
*/
movb $cmd_DC, %al
orb $0x04, %al #; CB_DC_SRST = soft reset
movw reg_addr - int13_handler + (8 * 2), %dx #; CB_DC
outb %al, %dx
call delay400ns
#endif
#if 0
movb $0x08, %al #; ATAPI Soft Reset
movw reg_addr - int13_handler + (7 * 2), %dx #; CB_CMD
outb %al, %dx
call delay400ns
#endif
#if 1
/* set features (0xEF to command port) */
/* set transfer mode (0x03 in feature register) */
/* mode value in Sector Count register (0 for PIO default mode) */
movb $0, %al #; PIO default transfer mode
movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register
outb %al, %dx
call delay400ns
movb $0x03, %al #; 0x03 = set transfer mode based on value in sector count register
movw reg_addr - int13_handler + (1 * 2), %dx #; CB_FR = Feature Register
outb %al, %dx
call delay400ns
movb $0xEF, %al #; Set Features Command
movw reg_addr - int13_handler + (7 * 2), %dx #; CB_CMD
outb %al, %dx
call delay400ns
#endif
#if 1
movb $0x08, %al #; ATAPI Soft Reset
movw reg_addr - int13_handler + (7 * 2), %dx #; CB_CMD
outb %al, %dx
call delay400ns
#endif
#; Test only: do a hard reset and see if it works
#if 1
/* The ATA software reset mechanism, SRST, (bit 2 in the Device Control
* Register) cannot be used for ATAPI Device, because resets issued by
* the ATAPI driver would also reset any attached hard disk and vice
* versa. To solve this, ATAPI defines an ATAPI Soft Reset command
* using a reserved ATA opcode which could be decoded by the interface
* controller hardware.
*/
movb $cmd_DC, %al
orb $0x04, %al #; CB_DC_SRST = soft reset
movw reg_addr - int13_handler + (8 * 2), %dx #; CB_DC
outb %al, %dx
call delay400ns
#endif
movb $cmd_DC, %al
movw reg_addr - int13_handler + (8 * 2), %dx #; CB_DC
outb %al, %dx
call delay400ns
call set_timeout
cmpb $0, reg_dev_info - int13_handler
je 1f #; master not exist
call sub_atapi_delay
call reg_poll_busy
1:
cmpb $0, reg_dev_info - int13_handler + 1
je 1f #; slave not exist
call sub_atapi_delay
//call reg_poll_busy #; added recently for test
//jmp 1f #; added recently for test
2:
movw $1, %ax
call __reg_select_dev
movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register
inb %dx, %al
movb %al, %ah
movw reg_addr - int13_handler + (3 * 2), %dx #; CB_SN = reserved for SAM TAG byte
inb %dx, %al
cmpw $0x0101, %ax
je 1f
call check_timeout
jna 2b
1:
popaw
ret
test_atapi_ready:
#; return: cf =0 ready, cf =1 not ready
pushaw
movw $2, %cx
1:
pushw %cx
call clear_atapi_buffer
#; TEST UNIT READY Command
#;
#; The TEST UNIT READY command provides a means to check if the Device
#; is ready. This is not a request for a self-test. If the Device would
#; accept an appropriate medium-access command without returning CHECK
#; CONDITION status, this command shall return a GOOD status. If the
#; Device cannot become operational or is in a state such that a Host
#; Computer action(e.g. START/STOP UNIT command with LoEj = 0 and Start
#; = 1) is required to make the unit ready, the ATAPI CD-ROM Drive
#; shall return CHECK CONDITION status with a sense key of NOT READY.
#;
#; Byte
#; 0 Operation code (00h)
#; 1 Reserved
#; 2 Reserved
#; 3 Reserved
#; 4 Reserved
#; 5 Reserved
#; 6 Reserved
#; 7 Reserved
#; 8 Reserved
#; 9 Reserved
#; 10 Reserved
#; 11 Reserved
#;
#; Using the TEST UNIT READY Command
#;
#; The TEST UNIT READY command is useful in that it allows a Host
#; Computer to poll a Device until it is ready without the need to
#; allocate space for returned data. It is especially useful to check
#; cartridge status. ATAPI CD-ROM Drives are expected to respond
#; promptly to indicate the current status of the device.
# invoke reg_packet,byte,0,cs, atapi_tmp_buffer, 128
pushw $128
pushw $atapi_tmp_buffer - int13_handler
pushw %cs
pushw $0
call reg_packet
addw $8, %sp
// /* debug print AX */
// pushw %ax
// pushw $test_atapi_ax - int13_handler # the format string
// call realmode_printf
// popw %ax
// popw %ax
orw %ax, %ax
jnz 2f
call get_atapi_sense
// /* debug print AL, FLAGS */
// pushfw
// pushw %ax
// pushw $test_atapi_al_flags - int13_handler # the format string
// call realmode_printf
// popw %ax
// popw %ax
// popfw
jc 2f
orb %al, %al
jnz 2f
popw %cx
clc
popaw
ret
2:
call start_stop_unit #; start unit
popw %cx
loop 1b #; try again
stc
popaw
ret
//test_atapi_ax:
// .ascii "test_atapi_ax: AX=%X\r\n\0"
//test_atapi_al_flags:
// .ascii "test_atapi_al_flags: AX=%X, FLAGS=%X\r\n\0"
start_stop_unit:
#; return: cf =0 ready, cf =1 not ready
pushaw
movw $1, %cx
1:
pushw %cx
call clear_atapi_buffer
#; START/STOP UNIT Command
#;
#; The START/STOP UNIT command requests that the ATAPI CD-ROM Drive
#; enable or disable media access operations.
#;
#; | bit bit bit bit bit bit bit bit
#; Byte | 7 6 5 4 3 2 1 0
#; -----+--------------------------------------------------------------
#; 0 | Operation code (1Bh)
#; -----+-------------------------------------------------------+------
#; 1 | Reserved | Immed
#; -----+-------------------------------------------------------+------
#; 2 | Reserved
#; -----+--------------------------------------------------------------
#; 3 | Reserved
#; -----+-----------------------------------------------+-------+------
#; 4 | Reserved | LoEj | Start
#; -----+-----------------------------------------------+-------+------
#; 5 |
#; -----+--------------------------------------------------------------
#; 6 | Reserved
#; -----+--------------------------------------------------------------
#; 7 | Reserved
#; -----+--------------------------------------------------------------
#; 8 | Reserved
#; -----+--------------------------------------------------------------
#; 9 | Reserved
#; -----+--------------------------------------------------------------
#; 10 | Reserved
#; -----+--------------------------------------------------------------
#; 11 | Reserved
#; -----+--------------------------------------------------------------
movb $0x1B, atapi_cmd_buffer - int13_handler
movb $0x01, atapi_cmd_buffer - int13_handler + 4 #; start unit
# invoke reg_packet,byte,0,cs, atapi_tmp_buffer, 128
pushw $128
pushw $atapi_tmp_buffer - int13_handler
pushw %cs
pushw $0
call reg_packet
addw $8, %sp
orw %ax, %ax
jnz 2f
// call get_atapi_sense
//
// jc 2f
// orb %al, %al
// jnz 2f
popw %cx
clc
popaw
ret
2:
popw %cx
loop 1b
stc
popaw
ret
reg_setup_base_addr:
#; input: bx = base addr pointer
pushw %ax
pushw %di
cld # store upward
movw $reg_addr - int13_handler, %di # points to 10-word space
movw (%bx), %ax # get base address
# store 8 ports(command block registers)
#if 0
pushw 18(%di)
pushw 16(%di)
pushw 14(%di)
pushw 12(%di)
pushw 10(%di)
pushw 8(%di)
pushw 6(%di)
pushw 4(%di)
pushw 2(%di)
pushw (%di)
pushw 2(%bx)
pushw %ax
pushw $reg_addresses - int13_handler # the format string
call realmode_printf
addw $26, %sp # adjust the stack pointer
#endif
stosw
incw %ax
stosw
incw %ax
stosw
incw %ax
stosw
incw %ax
stosw
incw %ax
stosw
incw %ax
stosw
incw %ax
stosw
movw 2(%bx), %ax # get base address
stosw # store 2 ports(control block registers)
incw %ax
stosw
popw %di
popw %ax
ret
#if 0
reg_addresses:
.ascii "reg_addresses: Data=%X, Ctrl=%X, di: %X, %X, %X, %X, %X, %X, %X, %X, %X, %X\r\n\0"
#endif
set_timeout:
pushl %eax
call read_bios_time
addl $(5 * 18), %eax #; 20 seconds
# not 20 seconds, but 5 seconds
#; FIXME: Midnight overflow
# Midnight overflow is handled in check_timeout
movl %eax, time_out - int13_handler
popl %eax
ret
check_timeout:
#; you need to setup the timeout first
pushl %eax
pushl %ebx
pushl %edx
call read_bios_time
movl time_out - int13_handler, %ebx
movl %ebx, %edx
subl $(5 * 18), %ebx # the time when we set_timeout
cmpl %ebx, %eax
jnb 1f
/* the next day */
addl $0x001800B0, %eax /* 24 hours = 0x1800B0 ticks */
1:
cmpl %edx, %eax
popl %edx
popl %ebx
popl %eax
ret
clear_atapi_buffer:
pushaw
pushw %es
pushw %cs
popw %es
cld
movw $atapi_tmp_buffer - int13_handler, %di
movw $128, %cx # but note the buffer is 256-byte long!
xorb %al, %al
rep stosb # added the lost rep prefix
movw $atapi_cmd_buffer - int13_handler, %di
movw $16, %cx
rep stosb # added the lost rep prefix
popw %es
popaw
ret
reg_packet:
#;proc reg_packet,withlocal,dir,packet_seg,packet_off,packet_len
#; input:
#; return ax = 0 noerror, ah = error code al= error bit
#; cx = len
pushaw
movw %sp, %bp
call set_timeout
movb $0, _pre_fail_bit7_ - int13_handler
#; outbytes CB_DC,cmd_DC_ni,CB_FR,0,CB_SC,0,CB_SN,0,
# outbytes CB_DC,cmd_DC,CB_FR,0,CB_SC,0,CB_SN,0,
movb $cmd_DC, %al
movw reg_addr - int13_handler + (8 * 2), %dx #; CB_DC
outb %al, %dx
call delay400ns #; added recently
call sub_atapi_delay #; added recently
movb $0, %al
movw reg_addr - int13_handler + (1 * 2), %dx #; CB_FR = Feature Register
outb %al, %dx
movb $0, %al
movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register
outb %al, %dx
movb $0, %al
movw reg_addr - int13_handler + (3 * 2), %dx #; CB_SN = reserved for SAM TAG byte
outb %al, %dx
# outbytes CB_CL,[.packet_len],CB_CH,[.packet_len+1]
movb 24(%bp), %al #; packet_len low byte
movw reg_addr - int13_handler + (4 * 2), %dx #; CB_CL = byte count low
outb %al, %dx
movb 25(%bp), %al #; packet_len high byte
movw reg_addr - int13_handler + (5 * 2), %dx #; CB_CH = byte count high
outb %al, %dx
# outbyte CB_DH,[reg_cur_dev]
movb reg_cur_dev - int13_handler, %al
movw reg_addr - int13_handler + (6 * 2), %dx #; CB_DH = drive select
outb %al, %dx
# outbyte CB_CMD,CMD_PACKET
movb $0xA0, %al #; CMD_PACKET
movw reg_addr - int13_handler + (7 * 2), %dx #; CB_CMD
outb %al, %dx
call delay400ns
call sub_atapi_delay
subw %bx, %bx #; clear error number
# while {inbyte CB_ASTAT},{test al,CB_STAT_BSY|CB_STAT_ERR|CB_STAT_DRQ},z
3:
# inbyte CB_ASTAT
movw reg_addr - int13_handler + (8 * 2), %dx #; CB_ASTAT=8, CB_STAT=7
inb %dx, %al
testb $(CB_STAT_BSY | CB_STAT_DRQ | CB_STAT_ERR), %al
jnz 3f
orb $FAILBIT0, %bl #; error
call check_timeout
jna 3b
3:
# endwhile
# while {inbyte CB_ASTAT},{test al, CB_STAT_BSY}, nz
3:
# inbyte CB_ASTAT
movw reg_addr - int13_handler + (8 * 2), %dx #; CB_ASTAT=8, CB_STAT=7
inb %dx, %al
testb $CB_STAT_BSY, %al
jz 3f
call check_timeout
jna 3b
movw $-1, 18(%bp)
movb $51, %bh #; error
3:
# endwhile
cmpb $0, %bh
jnz 2f #; error
# inbyte CB_STAT,[.status]
movw reg_addr - int13_handler + (7 * 2), %dx #; CB_STAT
inb %dx, %al
movb %al, _status_ - int13_handler
# inbyte CB_SC, [.reason]
movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register
inb %dx, %al
movb %al, _reason_ - int13_handler
# inbyte CB_CL, [.bcnt]
movw reg_addr - int13_handler + (4 * 2), %dx #; CB_CL = byte count low
inb %dx, %al
movb %al, _bcnt_ - int13_handler
# inbyte CB_CH, [.bcnt+1]
movw reg_addr - int13_handler + (5 * 2), %dx #; CB_CH = byte count high
inb %dx, %al
movb %al, _bcnt_ - int13_handler + 1
movb _status_ - int13_handler, %al
andb $(CB_STAT_BSY | CB_STAT_DRQ), %al
cmpb $CB_STAT_DRQ, %al
jz 3f
movb $52, %bh #; error
#ifdef DEBUG
print_stat [.status],"%b %s check BSY=0 DRQ=1 now\n",bx,STAT_BUF
#endif
jmp 2f
3:
#ifdef sane_check
movb _reason_ - int13_handler, %al
testb $(CB_SC_P_TAG | CB_SC_P_REL | CB_SC_P_IO), %al
jnz 3f
testb $CB_SC_P_CD, %al
jnz 4f
3:
orb $FAILBIT2, %bl #; error
#ifdef DEBUG
print_stat [.status],"%b %s %b check reason\n",bx,STAT_BUF,[.reason]
#endif
4:
movw _bcnt_ - int13_handler, %ax
cmpw 24(%bp), %ax
jz 3f
orb $FAILBIT3, %bl #; error
#ifdef DEBUG
print_stat [.status],"%b %s 0x%x check packet_len\n",bx,STAT_BUF,[.bcnt]
#endif
3:
#endif //; sane_check
#ifdef DEBUG
debug_print "sending cmd buffer"
#endif
movw $atapi_cmd_buffer - int13_handler, %si
#if 0
movw $12, %cx #; cmd_buff_len is 12
shrw $1, %cx
#else
movw $6, %cx #; cmd_buff_len is 12
#endif
movw reg_addr - int13_handler + (0 * 2), %dx #; CB_DATA = 16-bit data port
cld
repz outsw
call delay400ns
subw %cx, %cx
3:
# while
#ifdef DEBUG
debug_print " data transfer ----------------------------\n"
#endif
call sub_atapi_delay
4:
# while {inbyte CB_ASTAT},{test al,CB_STAT_BSY},nz
# inbyte CB_ASTAT
movw reg_addr - int13_handler + (8 * 2), %dx #; CB_ASTAT=8, CB_STAT=7
inb %dx, %al
testb $CB_STAT_BSY, %al
jz 4f
call check_timeout
jna 4b
movb $54, %bh #; error
jmp 2f
# endwhile
4:
#ifdef DEBUG
print_stat al,"%b %s wait ASTAT BSY=0\n",bx,STAT_BUF
#endif
#; Data transfer loop
#; read the primary state register
# inbyte CB_STAT,[.status]
movw reg_addr - int13_handler + (7 * 2), %dx #; CB_STAT
inb %dx, %al
movb %al, _status_ - int13_handler
# inbyte CB_SC, [.reason]
movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register
inb %dx, %al
movb %al, _reason_ - int13_handler
# inbyte CB_CL, [.bcnt]
movw reg_addr - int13_handler + (4 * 2), %dx #; CB_CL = byte count low
inb %dx, %al
movb %al, _bcnt_ - int13_handler
# inbyte CB_CH, [.bcnt+1]
movw reg_addr - int13_handler + (5 * 2), %dx #; CB_CH = byte count high
inb %dx, %al
movb %al, _bcnt_ - int13_handler + 1
#ifdef DEBUG
print_stat [.status],"%b %s pre-data reason=%b len=%d\n",bx,STAT_BUF,[.reason],[.bcnt]
print_stat [.status]," stat "
debug_print " check the device said end of command"
#endif
testb $(CB_STAT_BSY | CB_STAT_DRQ), _status_ - int13_handler
jnz 4f
orb $0x80, 18(%bp) #; NON_DATA
jmp 2f
4:
#ifdef DEBUG
debug_print " device want transfer data BSY = 0 DRQ =1"
#endif
movb _status_ - int13_handler, %al
andb $(CB_STAT_BSY | CB_STAT_DRQ), %al
cmpb $CB_STAT_DRQ, %al
jz 4f
movb $55, %bh #; error
jmp 2f
4:
#ifdef sane_check
#ifdef DEBUG
print_stat al," stat "
debug_print " check: C/nD=0, IO=1 (read) or IO=0 (write)"
#endif
testb $(CB_SC_P_TAG | CB_SC_P_REL | CB_SC_P_CD), _reason_ - int13_handler
jz 4f
orb $FAILBIT4, %bl #; error
#ifdef DEBUG
print_stat al, " FAIL:%b %s reason=%b C/nD=0, IO=1 (read) or IO=0 (write)\n",bx,STAT_BUF,[.reason],
#endif
4:
testb $CB_SC_P_IO, _reason_ - int13_handler
jz 4f
cmpb $0, 18(%bp)
jz 4f
orb $FAILBIT5, %bl #; error
#ifdef DEBUG
print_stat al, " FAIL:%b %s reason=%b dir=%d\n",bx,STAT_BUF,[.reason],[.dir]
#endif
4:
#endif //; sane_check
#; do the slow data transfer
#ifdef DEBUG
debug_print " do slow delay"
#endif
call sub_atapi_delay
#ifdef DEBUG
debug_print " check data len zero"
#endif
movw _bcnt_ - int13_handler, %ax
orw %ax, %ax
jnz 4f
movb $60, %bh #; error
orb $0x80, 18(%bp) #; NON_DATA
jmp 2f
4:
#ifdef DEBUG
debug_print " check the buffer len"
#endif
#ifdef sane_check
cmpw $0x8000, %ax #; REG_ATAPI_MAX_BYTES
jna 4f
orb $FAILBIT6, %bl #; error
4:
#endif //; sane_check
movb _pre_fail_bit7_ - int13_handler, %dl
orb %dl, %bl #; error
testb $1, %al
setnz _pre_fail_bit7_ - int13_handler
movw %cx, %dx
addw %ax, %dx
cmpw 24(%bp), %dx
jna 4f
movb $59, %bh #; error
orb $0x80, 18(%bp) #; NON_DATA
jmp 2f
4:
pushw %dx
movw %ax, %cx
incw %cx
shrw $1, %cx
movw reg_addr - int13_handler + (0 * 2), %dx #; CB_DATA = 16-bit data port
cld
cmpb $0, 18(%bp)
jz 4f
pushw %ds
movw 20(%bp), %ds #; packet_seg
movw 22(%bp), %si #; packet_off
repz outsw
popw %ds
jmp 5f
4:
pushw %es
movw 20(%bp), %es #; packet_seg
movw 22(%bp), %di #; packet_off
repz insw
popw %es
5:
popw %cx
addw %ax, 22(%bp) #; packet_off
call delay400ns
jmp 3b
# endwhile
3:
testb $0x80, 18(%bp) #; NON_DATA
jnz 3f
call sub_atapi_delay
call check_timeout # !!added recently!!
jna 3f
movb $57, %bh #; error: time is out
#ifdef DEBUG
debug_print " end of command, wait for BSY=0\n"
#endif
jmp 2f
3:
# inbyte CB_STAT, #; [.status]
movw reg_addr - int13_handler + (7 * 2), %dx #; CB_STAT
inb %dx, %al
testb $(CB_STAT_BSY | CB_STAT_DRQ | CB_STAT_ERR), %al
jz 1f
movb $58, %bh #; error
#ifdef DEBUG
debug_print "Error: final check for stat al\n"
#endif
jmp 2f
1:
# inbyte CB_SC, [.reason]
movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register
inb %dx, %al
movb %al, _reason_ - int13_handler
2:
#ifdef check_extra_fail
movb _reason_ - int13_handler, %al
testb $(CB_SC_P_TAG | CB_SC_P_REL), %al
jnz 1f
testb $CB_SC_P_IO, %al
jz 1f
testb $CB_SC_P_CD, %al
jnz 2f
1:
orw $FAILBIT8, %bx #; error
#ifdef DEBUG
debug_print "FAIL:final check for protocol failures C/nD=1 IO=1\n"
#endif
2:
#endif //; check_extra_fail
#; outbyte CB_DC,cmd_DC
#ifdef DEBUG
debug_print " end of command, ec:failbit=%x, %d bytes data\n",bx,cx
#endif
movw %bx, 14(%bp) # __AX #; error
movw %cx, 12(%bp) # __CX
popaw
ret
# local status,1,reason,1,bcnt,2,pre_fail_bit7,1
_status_: .byte 0
_reason_: .byte 0
_bcnt_: .byte 0, 0
_pre_fail_bit7_: .byte 0
# //; end of function reg_packet
delay400ns:
pushw %cx
movw $0x0040, %cx
1:
pause
loop 1b
popw %cx
ret
# inbyte CB_ASTAT
movw reg_addr - int13_handler + (8 * 2), %dx #; CB_ASTAT=8, CB_STAT=7
inb %dx, %al
inb %dx, %al
inb %dx, %al
inb %dx, %al
ret
sub_atapi_delay:
#; delay a few clicks
cmpl $0, slow_atapi_device - int13_handler
jz 1f
# delay 1 millisecond
pushl %ecx
//pushfw
//sti #; sti should already be done by the caller
movl delay_repeat_num - int13_handler, %ecx # loops per millisecond
2:
call read_bios_time
cmpw %ax, %ax # for more accurate timing
addr32 loope 2b
//popfw
popl %ecx
1:
ret
reg_poll_busy:
#; need to setup the timeout first
#;return ax=0 ok
#; ax = 1 timeout
1:
movw reg_addr - int13_handler + (7 * 2), %dx #; CB_STAT
inb %dx, %al
andb $0x80, %al # CB_STAT_BSY
jz 1f
call check_timeout
jna 1b
# timeout
1:
ret
get_atapi_sense:
#; return: cf =0 success, al = sense key, bl = asc, bh = ascq
#; cf =1 failed
call clear_atapi_buffer
#; REQUEST SENSE Command
#;
#; The REQUEST SENSE command requests that the ATAPI CD-ROM Drive
#; transfer sense data to the Host Computer.
#;
#; Byte
#; 0 Operation code (03h)
#; 1 Reserved
#; 2 Reserved
#; 3 Reserved
#; 4 Allocation Length
#; 5 Reserved
#; 6 Reserved
#; 7 Reserved
#; 8 Reserved
#; 9 Reserved
#; 10 Reserved
#; 11 Reserved
movb $0x03, atapi_cmd_buffer - int13_handler
movb $32, atapi_cmd_buffer - int13_handler + 4
# invoke reg_packet,byte,0,cs, atapi_tmp_buffer, 128
pushw $128
pushw $atapi_tmp_buffer - int13_handler
pushw %cs
pushw $0
call reg_packet
addw $8, %sp
orw %ax, %ax
jnz 1f
movb atapi_tmp_buffer - int13_handler, %al
/* bit 7 is `Valid' bit, and should be 1
* bit 0-6 is the error code and can be 70h or 71h
*/
andb $0x7f, %al
cmpb $0x70, %al
je 2f
cmpb $0x71, %al
jne 1f
2:
movb atapi_tmp_buffer - int13_handler + 2, %al #; get sense key
andb $0x0f, %al #; low 4 bits are sense key
xorw %bx, %bx
cmpb $0x06, atapi_tmp_buffer - int13_handler + 7 #; additional sense length
jb 3f
movw atapi_tmp_buffer - int13_handler + 12, %bx
3:
clc
ret
1:
stc
ret
edd30_for_cdrom:
// ; Stack layout:
// ; +10 INT flags
// ; +8 INT CS
// ; +6 INT IP
// ; +2 EAX
// ; BP+0 BP
// ; -2 ax
// ; -4 cx
// ; -6 dx
// ; -8 bx
// ; -10 sp
// ; -12 bp
// ; -14 si
// ; -16 di
// ; -18 ds
// ; -20 es
// ; -24 edx
sti #; for edd30_for_cdrom
cld
pushaw
pushw %ds
pushw %es
pushl %edx
pushw %cs
popw %ds
pushw %ax
movzbw %dl, %ax
subb %cs:(min_cdrom_id - int13_handler), %al
call select_atapi #; select_atapi_force
popw %ax
jnc 1f
movb $0xAA, %ah
jmp edd30_for_cdrom_fail_out
1:
movw $(edd30_act_table_end - edd30_act_table), %cx
xorw %bx, %bx
1:
cmpb %ah, (edd30_act_table - int13_handler)(%bx)
je 1f
incw %bx
loop 1b
jmp edd30_for_cdrom_invalid_cmd
1:
shlw $1, %bx
jmp *(edd30_jmp_table - int13_handler)(%bx)
edd30_for_cdrom_stop_disk_emu:
movw %si, %di
pushw -18(%bp)
popw %es
movw $edd30_cdemu_spec - int13_handler, %si
movw $0x13, %cx #; SIZE_OF_CDEMU_SPEC
repz movsb
orb %al, %al
jnz edd30_for_cdrom_success_out
//.terminate_emu:
movb %al, emu_disk_type - int13_handler
jmp edd30_for_cdrom_success_out
edd30_for_cdrom_init_disk_emu_and_boot:
call edd30_init_disk_emu
jc edd30_for_cdrom_fail_out
movw edd30_cdemu_spec - int13_handler + 12, %ax #; struc_cdemu_spec.load_seg
movw %ax, %es
xorw %di, %di
movw edd30_cdemu_spec - int13_handler + 14, %cx #; struc_cdemu_spec.sect_count
addw $3, %cx #; convert virtual sect count ...
shrw $2, %cx #; ... to cdrom sect count
movl edd30_cdemu_spec - int13_handler + 4, %edx #; struc_cdemu_spec.image_lba
call read_atapi
jnc .emu_boot_read_ok
movb $0x04, %ah
jmp edd30_for_cdrom_fail_out
.emu_boot_read_ok:
// movw $copyright_msg, %si
// call draw_string_tty
call read_bios_time
movw %ax, %dx
addw $(18 * 2), %dx
#; sti
.loop_delay:
call read_bios_time
cmpw %ax, %dx
ja .loop_delay
#; cli
movb edd30_cdemu_spec - int13_handler + 2, %dl #; struc_cdemu_spec.emu_drvid
movb %dl, -6(%bp) #; set the driver id to dl
movw %es, %ax
shlw $4, %ax
movw %ax, 6(%bp) #; set new ip
xorw %ax, %ax
movw %ax, 8(%bp) #; set new cs
movw %ax, (%bp) #; clear bp
movw $0xAA55, %ax
movw %ax, 2(%bp)
jmp edd30_for_cdrom_success_out_no_ah
edd30_for_cdrom_init_disk_emu:
call edd30_init_disk_emu
jnc edd30_for_cdrom_success_out
jmp edd30_for_cdrom_fail_out
edd30_for_cdrom_return_boot_catalog:
call edd30_return_boot_catalog
jnc edd30_for_cdrom_success_out
jmp edd30_for_cdrom_fail_out
edd30_for_cdrom_get_drv_param:
call edd30_get_cdrom_param
jnc edd30_for_cdrom_success_out
jmp edd30_for_cdrom_invalid_cmd
edd30_for_cdrom_ext_write:
movb $0x03, %ah
jmp edd30_for_cdrom_fail_out
edd30_for_cdrom_ext_read:
call edd30_read_cdrom
jnc edd30_for_cdrom_success_out
jmp edd30_for_cdrom_fail_out
edd30_for_cdrom_install_check:
movw $0xAA55, -8(%bp) #; bx=0xaa55
#ifdef EDD_3_0
movb $0x30, %ah #; ax=0x30 edd-3.0
#else
movb $0x21, %ah #; ax=0x21 edd-2.1
#endif
movb $0x07, -4(%bp) #; cx= 0x01 | 0x04, ext disk access and edd ok
jmp edd30_for_cdrom_success_out_no_ah
edd30_for_cdrom_get_disk_type:
movb $0x02, %ah
jmp edd30_for_cdrom_success_out_no_ah
edd30_for_cdrom_get_last_stat:
movb int13_last_stat - int13_handler, %ah
jmp edd30_for_cdrom_success_out_no_ah
edd30_for_cdrom_reset:
call reg_reset
movw atapi_cur_dev - int13_handler, %ax # device serial number
call select_atapi_force
edd30_for_cdrom_success_out:
xorb %ah, %ah
edd30_for_cdrom_success_out_no_ah:
andb $0xFE, 10(%bp)
jmp .done
edd30_for_cdrom_invalid_cmd:
movb $0x01, %ah
edd30_for_cdrom_fail_out:
orb $1, 10(%bp) // set CF=1 for ERROR
.done:
/* set error code AH */
movb %ah, int13_last_stat - int13_handler
movb %ah, 3(%bp)
popl %edx
popw %es
popw %ds
popaw
popw %bp
popl %eax
iret
get_atapi_base_io:
#;input: ax = dev
#;return: bx = base io 1, dx = base io 2, cx = device number
# save si, ax
pushw %si
pushw %ax
shlw $2, %ax
movw $atapi_dev_base - int13_handler, %si
addw %ax, %si
movw %cs:(%si), %bx
movw %cs:2(%si), %cx
movw %cs:2(%bx), %dx
movw %cs:(%bx), %bx
popw %ax
popw %si
ret
#;============================================================================
edd30_get_cdrom_param:
#;return cf =0 ok, cf =1 fail
pushw %ds
movw -18(%bp), %ax #; restore ds
movw %ax, %ds
movw (%si), %ax
cmpw $26, %ax
jae .packet_ok3
popw %ds
setb %ah
//.fail_out:
stc
ret
.packet_ok3:
#ifdef EDD_3_0
cmpw $66, %ax
jb .below_3_0
movw $66, %ax
jmp .set_packet_size
.below_3_0:
#endif
cmpw $30, %ax
jb .below_2_0
movw $30, %ax
jmp .set_packet_size
.below_2_0:
movw $26, %ax
.set_packet_size:
movw %ax, (%si)
movw $0x74, 2(%si) #; struc_extparam.flags: removable, lock, chg line
movw $0x800, 24(%si) #; CDSECTOR_SIZE, struc_extparam.bytes_per_sect
xorw %bx, %bx
decw %bx
movw %bx, 4(%si) #; struc_extparam.cylinders
movw %bx, 8(%si) #; struc_extparam.heads
movw %bx, 12(%si) #; struc_extparam.sectors
#ifdef EDD_3_0
cmpw $66, %ax
jb .no_dpi
movw $0xBEDD, 30(%si) #; struc_extparam.dpi_key: dpi signature
movw $0x24, 32(%si) #; struc_extparam.dpi_length: dpi length
movl $0x415349, 36(%si) #; 'ISA', struc_extparam.host_bus_type"
movl $0x50415441, 40(%si) #; 'ATAP', struc_extparam.interface_type
movw $0x49, 44(%si) #; 'I', struc_extparam.interface_type + 4
movw %cs:(atapi_cur_dev - int13_handler), %ax # device serial number
call get_atapi_base_io
movw %cx, 56(%si) #; struc_extparam.device_path
movw %bx, 48(%si) #; struc_extparam.interface_path
pushw %si
addw $30, %si #; struc_extparam.dpi_key
movw $(57 - 30), %cx #; struc_extparam.checksum - struc_extparam.dpi_key
call edd30_checksum
movb %ah, (%si)
popw %si
.no_dpi:
#endif
cmpw $30, %ax
jb .no_dpte
movw $atapi_dpte_buffer - int13_handler, 26(%si) #; struc_extparam.dpte_addr
movw %cs, 28(%si) #; struc_extparam.dpte_addr + 2
.no_dpte:
popw %ds
movw atapi_cur_dev - int13_handler, %ax # device serial number
call get_atapi_base_io
movw $atapi_dpte_buffer - int13_handler, %si
movw %bx, (%si)
movw %dx, 2(%si)
movb $0xE0, %al
shlb $4, %cl
orb %cl, %al
movb %al, 4(%si) #; struc_dpte.flags: LBA enable, bit 4 = slave drv
movb $0x60, 10(%si) #; struc_dpte.bios_spec: ATAPI and removable
movw $15, %cx #; struc_dpte.checksum
call edd30_checksum
movb %ah, (%si)
clc
ret
edd30_checksum:
xorb %ah, %ah
.loop_checksum:
lodsb
addb %al, %ah
loop .loop_checksum
negb %ah
ret
#;============================================================================
edd30_return_boot_catalog:
#;return cf =0 ok, cf =1 fail
call test_atapi_ready
jnc .atapi_ok2
movb $0xAA, %ah
jmp .fail_out2
.atapi_ok2:
pushw %ds
movw -18(%bp), %ax #; restore ds
movw %ax, %ds
cmpb $8, (%si)
popw %ds
jae .packet_ok2
setb %ah
.fail_out2:
stc
ret
.packet_ok2:
pushw %cs
popw %es
movw $edd30_disk_buffer - int13_handler, %di
xorw %cx, %cx
incw %cx
movw $0x11, %dx
movzwl %dx, %edx
call read_atapi #; read boot record volume descriptor
jc .read_fail
cmpb $0, (%di)
jne .non_bootable
cmpl $0x54204C45, 7(%di) #; 'EL T'
jne .non_bootable
cmpl $0x5449524F, 11(%di) #; 'ORIT'
jne .non_bootable
movl 0x47(%di), %edx
orl %edx, %edx
jz .non_bootable
pushw %ds
movw -18(%bp), %ax #; restore ds
movw %ax, %ds
movzwl 6(%si), %eax #; struc_cdbc_cmd.begnning_sect
addl %eax, %edx
movb 1(%si), %cl #; struc_cdbc_cmd.sector_count
movw 4(%si), %bx #; struc_cdbc_cmd.buf_addr_seg
movw 2(%si), %di #; struc_cdbc_cmd.buf_addr_off
popw %ds
movw %bx, %es
call read_atapi
jc .read_fail
ret
.non_bootable:
.read_fail:
movb $0x0C, %ah
stc
ret
#;============================================================================
edd30_init_disk_emu:
call test_atapi_ready
jnc .atapi_ok
movb $0xAA, %ah
jmp .fail_out1
.atapi_ok:
pushw %ds
movw -18(%bp), %ax #; restore ds
movw %ax, %ds
cmpb $0x13, (%si) #; SIZE_OF_CDEMU_SPEC
jae .packet_ok
popw %ds
.invalid_cmd1:
setb %ah
.fail_out1:
stc
ret
.packet_ok:
pushw %es
pushw %cs
popw %es
movw $edd30_cdemu_spec - int13_handler, %di
pushw %di
movw $0x13, %cx #; SIZE_OF_CDEMU_SPEC
repz movsb
popw %si
popw %es
popw %ds
movb %dl, emu_cdrom_id - int13_handler
movb 2(%si), %al #; struc_cdemu_spec.emu_drvid
movb 1(%si), %al #; struc_cdemu_spec.media_type
andb $0x0F, %al
cmpb $4, %al
jae .invalid_cmd1
shlb $1, %al
movb %al, emu_disk_type - int13_handler
movw 10(%si), %ax #; struc_cdemu_spec.user_bufseg
orw %ax, %ax
jnz .has_user_buf
movw %cs, emu_buf_seg - int13_handler
movw $edd30_disk_buffer - int13_handler, %ax
movw %ax, emu_buf_off - int13_handler
jmp .cont
.has_user_buf:
movw %ax, emu_buf_seg - int13_handler
xorw %ax, %ax
movw %ax, emu_buf_off - int13_handler
.cont:
movw 12(%si), %ax #; struc_cdemu_spec.load_seg
orw %ax, %ax
jnz .has_load_seg
movw $0x07C0, 12(%si) #; struc_cdemu_spec.load_seg
.has_load_seg:
movb 17(%si), %ah #; struc_cdemu_spec.sectors
movb %ah, %bl
shlb $6, %ah
movb 16(%si), %al #; struc_cdemu_spec.cylinders
movw %ax, emu_cyl - int13_handler
andw $63, %bx
movw %bx, emu_sec - int13_handler
movb 18(%si), %bl #; struc_cdemu_spec.heads
movw %bx, emu_head - int13_handler
#if 1
cmpb $0, emu_disk_type - int13_handler
jz .no_disk_swap
cmpb $0, 2(%si) #; struc_cdemu_spec.emu_drvid
jnz .no_disk_swap
/* map (fd0) (fd1) */
#if 0
xorw %ax, %ax
movw %ax, drive_map - int13_handler + 2
incw %ax
movw %ax, drive_map - int13_handler #; install the swap drive map
#else
/* find an empty slot */
xorl %eax, %eax
movw $(hooked_drive_map - int13_handler - DRIVE_MAP_SLOT_SIZE), %bx
1:
addw $DRIVE_MAP_SLOT_SIZE, %bx
cmpw $(hooked_drive_map - int13_handler + (DRIVE_MAP_SIZE*DRIVE_MAP_SLOT_SIZE)), %bx
jnb .no_disk_swap #; not found
cmpl %eax, (%bx)
jne 1b
cmpl %eax, 4(%bx)
jne 1b
cmpl %eax, 8(%bx)
jne 1b
cmpl %eax, 12(%bx)
jne 1b
cmpl %eax, 16(%bx)
jne 1b
cmpl %eax, 20(%bx)
jne 1b
#; found
incw %ax
movw %ax, (%bx)
#endif
#; increase the floppy number
pushw %ds
pushw $0x0040
popw %ds
orb $0x41, 0x0010 #; physical address 0x410
popw %ds
#endif
.no_disk_swap:
clc
ret
#;============================================================================
cdemu_int13h:
// ; Stack layout:
// ; +10 INT flags
// ; +8 INT CS
// ; +6 INT IP
// ; +2 EAX
// ; BP+0 BP
// ; -2 ax
// ; -4 cx
// ; -6 dx
// ; -8 bx
// ; -10 sp
// ; -12 bp
// ; -14 si
// ; -16 di
// ; -18 ds
// ; -20 es
// ; -24 edx
sti #; for cdemu_int13h
cld
pushaw
pushw %ds
pushw %es
pushl %edx
pushw %cs
popw %ds
pushw %ax
movzbw %cs:emu_cdrom_id - int13_handler, %ax
subb %cs:min_cdrom_id - int13_handler, %al
call select_atapi
popw %ax
jnc 1f
movb $0xAA, %ah
jmp edd30_for_cdrom_fail_out
1:
movw $(cdemu_act_table_end - cdemu_act_table), %cx
xorw %di, %di
1:
cmpb %ah, (cdemu_act_table - int13_handler)(%di)
je 1f
incw %di
loop 1b
jmp edd30_for_cdrom_invalid_cmd
1:
movw -4(%bp), %cx #; restore cx
shlw $1, %di
jmp *(cdemu_jmp_table - int13_handler)(%di)
#;============================================================================
cdemu_int13h_emu_get_param:
movb emu_disk_type - int13_handler, %ah
movb %ah, -8(%bp) #; bl = drive type
movw edd30_cdemu_spec - int13_handler + 16, %cx #; struc_cdemu_spec.cylinders
xchgb %cl, %ch
decb %ch
andb $0x3F, %cl
movb edd30_cdemu_spec - int13_handler + 18, %dh #; struc_cdemu_spec.heads
decb %dh
movb $0x02, %dl
movw %cx, -4(%bp)
movw %dx, -6(%bp)
xorw %ax, %ax
jmp edd30_for_cdrom_success_out
cdemu_int13h_emu_get_type:
movb $0x02, %ah
jmp edd30_for_cdrom_success_out_no_ah
cdemu_int13h_emu_read:
orb %al, %al
jz .emu_read_fail
cmpb emu_head - int13_handler, %dh
jnb .emu_read_fail
movb %cl, %dl #; dl = sector number
andb $63, %dl
orb %dl, %dl
jz .emu_read_fail
decb %dl
cmpb emu_sec - int13_handler, %dl
jnb .emu_read_fail
movzbw %ch, %cx
cmpw emu_cyl - int13_handler, %cx
jnb .emu_read_fail
movw %cx, %ax #; ax = cylinder
mulb emu_head - int13_handler #; (cylinder*head
movb %dh, %cl
addw %cx, %ax #; (cylinder*head + head)
movb %dl, %cl #; cl = sector
mulw emu_sec - int13_handler #; (cylinder*head + head)*sect_per_track
addw %cx, %ax #; (cyl*head + head)*sect_p_t + sector
movb 2(%bp), %cl #; number of sectors
movw emu_buf_off - int13_handler, %di
xorl %edx, %edx
movl %edx, emu_last_read - int13_handler
.emu_loop_read:
pushw %ax
pushw %bx
pushw %cx
pushw %di
movzwl %ax, %edx
shrl $2, %edx
addl edd30_cdemu_spec - int13_handler + 4, %edx #; struc_cdemu_spec.image_lba
cmpl emu_last_read - int13_handler, %edx
je .emu_have_read
movl %edx, emu_last_read - int13_handler
movw $1, %cx
pushw %es
pushw emu_buf_seg - int13_handler
popw %es
call read_atapi
popw %es
jc .emu_atapi_fail
.emu_have_read:
andw $3, %ax #; sector = sector % 4
shlw $9, %ax #; sector = sector * 512
movw emu_buf_off - int13_handler, %si
addw %ax, %si
movw %bx, %di
pushw %ds
pushw emu_buf_seg - int13_handler
popw %ds
movw $512, %cx
repz movsb
popw %ds
popw %di
popw %cx
popw %bx
popw %ax
addw $512, %bx
incw %ax
loop .emu_loop_read
jmp edd30_for_cdrom_success_out
.emu_atapi_fail:
popw %di
popw %cx
popw %bx
popw %ax
.emu_read_fail:
movb $0x04, %ah
jmp edd30_for_cdrom_fail_out
#;============================================================================
cdemu_act_table:
.byte 0x0, 0x1, 0x2, 0x4, 0x8, 0x15, 0x16, 0x4B
cdemu_act_table_end:
cdemu_jmp_table:
.word edd30_for_cdrom_success_out - int13_handler #; 0 reset
.word edd30_for_cdrom_get_last_stat - int13_handler #; 1 get last state
.word cdemu_int13h_emu_read - int13_handler #; 2 read
.word cdemu_int13h_emu_read - int13_handler #; 4 verify
.word cdemu_int13h_emu_get_param - int13_handler #; 8 get param
.word cdemu_int13h_emu_get_type - int13_handler #; 0x15 get type
.word edd30_for_cdrom_success_out - int13_handler #; 0x16 detect disk change
.word edd30_for_cdrom_stop_disk_emu - int13_handler #; 0x4b stop disk emu
edd30_act_table:
.byte 0x0, 0x1, 0x15, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4D, 0x4A, 0x4C, 0x4E, 0x4B
edd30_act_table_end:
edd30_jmp_table:
.word edd30_for_cdrom_reset - int13_handler
.word edd30_for_cdrom_get_last_stat - int13_handler
.word edd30_for_cdrom_get_disk_type - int13_handler
.word edd30_for_cdrom_install_check - int13_handler
.word edd30_for_cdrom_ext_read - int13_handler
.word edd30_for_cdrom_ext_write - int13_handler
.word edd30_for_cdrom_ext_read - int13_handler
.word edd30_for_cdrom_success_out - int13_handler #; lock / unlock
.word edd30_for_cdrom_success_out - int13_handler #; eject
.word edd30_for_cdrom_success_out - int13_handler #; extended seek
.word edd30_for_cdrom_get_drv_param - int13_handler
.word edd30_for_cdrom_success_out - int13_handler #; check media change
.word edd30_for_cdrom_return_boot_catalog - int13_handler
.word edd30_for_cdrom_init_disk_emu - int13_handler
.word edd30_for_cdrom_init_disk_emu_and_boot - int13_handler
.word edd30_for_cdrom_success_out - int13_handler #; set hardware configuration
.word edd30_for_cdrom_stop_disk_emu - int13_handler #; 0x4b stop disk emu
int13_last_stat: .byte 0 #; the error code in AH
//drive_map: .word 0, 0, 0
emu_buf_off: .word 0
emu_buf_seg: .word 0
emu_cdrom_id: .byte 0
emu_disk_type: .byte 0 #;1=360 2=1.2 3=720 4=1.44 6=2.88 10h=atapi
emu_cyl: .word 0
emu_sec: .word 0
emu_head: .word 0
emu_last_read: .long 0
// .bss
start_of_atapi_data:
atapi_cur_dev: .word 0 #; device serial number
reg_cur_dev: .byte 0 #; 0xA0 for master, 0xB0 for slave
time_out: .long 0
reg_dev_info: .byte 0, 0
atapi_dev_base: .word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
atapi_dev_base_bak: .word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
.word 0, 0 #; base pointer, device
reg_addr: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
atapi_cmd_buffer: .space 16
atapi_tmp_buffer: .space 256
//atapi_devinfo: .space 32 # SIZE_OF_ATAPI_DEVINFO
delay_repeat_num: .long 0 # loops per millisecond
end_of_atapi_data:
edd30_cdemu_spec: .space 19 # SIZE_OF_CDEMU_SPEC
atapi_dpte_buffer: .space 16 # SIZE_OF_DPTE
.align 16
edd30_disk_buffer: .space 0x800
//cmd_select_dev: .byte 0xA0, 0xB0 #; CB_DH_DEV0, CB_DH_DEV1
ENTRY(slow_atapi_device)
.long 0
/* void realmode_printf(const char *format, ...)
*
* input: format is offset in CS segment
*
* Usage example:
*
* pushw IntegerN
* ... ... ... ...
* pushw Integer2
* pushw Integer1
* pushw $format_string - int13_handler
* call realmode_printf
* addw $(2*(N+1)), %sp
*
* where int13_handle should be the base of the CS segment,
* and format_string like this:
*
* format_string:
* .string "Int1=%x, Int2=%x, ..., IntN=%x\r\n"
*
* Currently only %d, %x and %X are implemented.
*/
realmode_printf:
pushaw
movw %sp, %bp
# bp+18: format
# bp+20: variables
addw $18, %bp
movw (%bp), %si # points to format string
addw $2, %bp # (%bp) is the first variable
1:
cs lodsb
testb %al, %al
jz 1f
cmpb $'%', %al
jne 2f
#; %d, %x, %X
cs lodsb
testb %al, %al
jz 1f
cmpb $'d', %al
movw $10, %bx # base 10
jz 4f
cmpb $'x', %al
jz 3f
cmpb $'X', %al
jne 1b # unkown directive, continue
3:
/* print hexa number */
movw $16, %bx # base 16
4:
/* print decimal or hexa number */
pushl %edi
xorl %edi, %edi
xorw %cx, %cx # count the digits
movw (%bp), %ax
5:
xorw %dx, %dx
divw %bx # AX=quo, DX=rem
movw %dx, %di
rorl $4, %edi
incw %cx
testw %ax, %ax # end?
jnz 5b
/* print the digits in EDI */
xorw %bx, %bx /* video page 0 */
5:
roll $4, %edi
movw %di, %ax # get digit in AL
andb $0x0f, %al
cmpb $9, %al
jbe 6f
addb $7, %al # A, B, C, D, E, F
6:
addb $0x30, %al
movb $0x0e, %ah /* print it */
int $0x10 /* via TTY mode */
loop 5b
popl %edi
addw $2, %bp # (%bp) is the next variable
jmp 1b # continue
2:
/* print char in AL */
xorw %bx, %bx /* video page 0 */
movb $0x0e, %ah /* print it */
int $0x10 /* via TTY mode */
jmp 1b # continue
1:
popaw
ret
//int13_handler_code_end:
//#if 0
// .align 4
//#else
// .space (8 - ((int13_handler_code_end - int13_handler) % 8)) % 8
//#endif
.align 16
GDT_data: /* used by int15/ah=87h */
/*00*/ .long 0, 0 /* unused descriptor, must be 0 */
/*08*/ .long 0, 0 /* GDT descriptor, must be 0 */
/* source descriptor */
/*10*/ .word 0xffff /* segment limit, 64K */
/*12*/ .byte 0, 0, 0 /* physical address(low 24 bits) */
/*15*/ .byte 0x93 /* access rights, 93h=readable/writable */
/*16*/ .byte 0 /* low 4 bits are high 4 bits of segment limit(here
should be 0). high 4 bits are flags, also should
be set to 0 for this function call */
/*17*/ .byte 0 /* physical address(high 8 bits) */
/* destination descriptor */
/*18*/ .word 0xffff /* segment limit, 64K */
/*1A*/ .byte 0, 0, 0 /* physical address(low 24 bits) */
/*1D*/ .byte 0x93 /* access rights, 93h=readable/writable */
/*1E*/ .byte 0 /* low 4 bits are high 4 bits of segment limit(here
should be 0). high 4 bits are flags, also should
be set to 0 for this function call */
/*1F*/ .byte 0 /* physical address(high 8 bits) */
/*20*/ .long 0, 0 /* code segment descriptor, must be 0 */
/*28*/ .long 0, 0 /* stack segment descriptor, must be 0 */
/*30*/
.align 16
MyGDT: .word MyGDTEnd - MyGDT - 1
.long 0 /* Pointer to self */
.word 0
MyRMDS: .long 0x0000ffff /* 64K data segment */
.long 0x00009200
MyPMDS: .long 0x0000ffff /* 4GB data segment */
.long 0x00cf9200
MyGDTEnd:
#;*******************************************************************
#; ATA Registers Layout
#;*******************************************************************
#;
#; Address Read Write
#; --------- --------------------- -----------------------------
#; A N 0 0 0 16-bit data port for both read and write
#; ------------------------------------------------------------------
#; A N 0 0 1 ATAPI/ATA Error(Read) ATAPI/ATA Feature(Write)
#; ------------------------------------------------------------------
#; A N 0 1 0 ATAPI Interrupt Reason(Read) / ATA Sector Count
#; ------------------------------------------------------------------
#; A N 0 1 1 Reserved for SAM TAG Byte / ATA Sector Number
#; ------------------------------------------------------------------
#; A N 1 0 0 ATAPI Byte Count LSB(R/W) / ATA Cylinder Low(R/W)
#; ------------------------------------------------------------------
#; A N 1 0 1 ATAPI Byte Count MSB(R/W) / ATA Cylinder High(R/W)
#; ------------------------------------------------------------------
#; A N 1 1 0 ATAPI Drive Select(R/W) / ATA Drive/Head Select(R/W)
#; ------------------------------------------------------------------
#; A N 1 1 1 ATAPI/ATA Status(Read) ATA Command(Write)
#; ------------------------------------------------------------------
#; N A 1 1 0 Alternate Status(Read) Device Control(Write)
#; ------------------------------------------------------------------
reg_base_addr:
.word 0x01f0, 0x03f6
.word 0x0170, 0x0376
.word 0x00f0, 0x02f6
#;.word 0x0070, 0x0276 //port 70 and 71 is for CMOS
.word 0x0180, 0x0386
.word 0x6b00, 0x6f00
.word 0x7300, 0x7700
ENTRY(reg_base_addr_append)
.word 0, 0, 0, 0
/* EBIOS_disk_address_packet should be at the end of the handler because some
* buggy BIOSes could destroy the memory that immediately follows.
*/
EBIOS_disk_address_packet:
.byte 0x10 /* packet size, 16 or more */
.byte 0 /* reserved, must be 0 */
.byte 0 /* number of sectors, must be from 1 to 127 */
.byte 0 /* reserved, must be 0 */
.word 0 /* displacement of memory address */
.word 0 /* segment of memory address */
.long 0 /* 64bit, start logical sector number */
.long 0
/* Don't insert code or data here! Buggy BIOSes could overwrite this area! */
int13_handler_end:
// /* confirm the handler routine does not exceed the 0x1000(4KB) size */
// . = . - (int13_handler_end - int13_handler) / 0x1001
#define CDROM_INIT
#ifdef CDROM_INIT
/* unsigned long
* init_atapi() : return number of atapi cdrom devices.
*/
ENTRY(init_atapi)
.code32
push %ebp
push %ebx
call EXT_C(prot_to_real) /* enter real mode */
.code16
sti /* enable interrupt for ATAPI */
cld
/* set CS base to be int13_handler */
#if 0
ljmp $((ABS(int13_handler)) >> 4), $(1f - int13_handler)
#else
.byte 0xEA
.word 1f - int13_handler
.word (ABS(int13_handler)) >> 4
#endif
1:
pushaw
pushw %es
pushw %ds
pushw %cs
popw %ds
pushw %cs
popw %es
xorw %ax, %ax
# initialize variables
movw $(start_of_atapi_data - int13_handler), %di
movw $(end_of_atapi_data - start_of_atapi_data), %cx
repz stosb
decw atapi_cur_dev - int13_handler # device serial number initialized to 0xFFFF
#; begin init_timer
call read_bios_time
movw %ax, %bx #; store initial timer in BX
1:
call read_bios_time
cmpw %ax, %bx #; timer just changed?
je 1b #; no, continue.
/* now AX=BX+1 */
movw %ax, %bx #; store initial timer in BX
xorl %ecx, %ecx
1:
call read_bios_time
cmpw %ax, %bx #; timer changed?
addr32 loope 1b
negl %ecx
/* ECX loops=a tick=1/18.2second=1000/18.2(=54.9) milliseconds */
movl %ecx, %eax
xorl %edx, %edx
xorl %ecx, %ecx
movw $55, %cx # a tick is about 55 milliseconds
divl %ecx
/* EAX=loops per millisecond */
movl %eax, delay_repeat_num - int13_handler
#; end init_timer
call init_atapi_cdroms
//movw $-1, atapi_cur_dev - int13_handler # device serial number initialized to 0xFFFF
popw %ds
popw %es
movw %sp, %bp
movw %cx, 8(%bp) #; 8(%bp) is old BX on stack !
popaw
ljmp $0, $ABS(1f)
1:
DATA32 call EXT_C(real_to_prot)
.code32
movzwl %bx, %eax
pop %ebx
pop %ebp
ret
#endif /* CDROM_INIT */
#ifdef CDROM_INIT
init_atapi_cdroms:
.code16
#; return: CF=0 success, CX=number of cdroms
#; CF=1 failed, no cdrom found
#; push cs
#; pop es
call reg_probe #; return cx = number of atapi devices
orw %cx, %cx
jnz 1f
call reg_probe #; return cx = number of atapi devices
orw %cx, %cx
jnz 1f
stc
ret
1:
#if 1
movw $atapi_dev_base - int13_handler, %si #; array of ATAPI reg pointer and dev
movw $atapi_dev_base_bak - int13_handler, %di #; for CDROMs
cld
pushw %si
pushw %di
xorw %ax, %ax
xorw %bx, %bx
1:
call select_atapi_force #;input: ax = device serial number
#;return: cf=0 success, cf=1 failed
jc 2f
call check_atapi_cdrom #;return: cf=0 is cdrom, cf=1 not cdrom
jc 2f
/* It is CDROM */
incw %bx #;count the cdroms
movsw #;store CDROM base pointer
movsw #;store CDROM device number
subw $4, %si
# debug print the reg and dev
pushw %bx
movw (%si), %bx # BX=reg group pointer
pushw 2(%si) # CDROM device number
pushw 2(%bx) # base port of control block regs, i.e., device control port
pushw (%bx) # base port of command block regs, i.e., data port
pushw $cdrom_reg_dev - int13_handler # the format string
call realmode_printf
addw $8, %sp # adjust the stack pointer
popw %bx
2:
addw $4, %si
incw %ax #; try next ATAPI device
loop 1b
popw %si #; points to atapi_dev_base_bak, which is for CDROMs
popw %di #; points to atapi_dev_base, which is for ATAPIs
movw $64, %cx #; overwrite ATAPIs with CDROMs
repz movsb
movw %bx, atapi_dev_count - int13_handler
movw min_cdrom_id - int13_handler, %cx
addw %bx, %cx
decw %cx
movw %cx, max_cdrom_id - int13_handler
movw %bx, %cx
#endif
clc
ret
cdrom_reg_dev:
.ascii "CDROM device found: Data=%X, Ctrl=%X, dev=%X(Note: 0=master, 1=slave)\r\n\0"
#endif /* CDROM_INIT */
#ifdef CDROM_INIT
/* Called by init_atapi_cdroms
* Calls reg_setup_base_addr, reg_reset
*/
reg_probe:
.code16
#; return cx = number of atapi devices
pushw %ax
pushw %bx
pushw %si
pushw %di
cld
movw $0, atapi_dev_count - int13_handler # reset counter
movw $reg_base_addr - int13_handler, %bx # BX points to base address array
movw $atapi_dev_base - int13_handler, %di # will store base pointers and device numbers
1:
call reg_setup_base_addr # setup the base reg addresses(reg_addr) for reg base address BX
#; begin reg_probe_exist
movb $cmd_DC, %al
movw reg_addr - int13_handler + (8 * 2), %dx #; CB_DC
outb %al, %dx
movw $0, %ax
call reg_probe_dev_exist
movb %al, reg_dev_info - int13_handler # master device existence
movw $1, %ax
call reg_probe_dev_exist
movb %al, reg_dev_info - int13_handler + 1 # slave device existence
#; end reg_probe_exist
call reg_reset
xor %si, %si # SI=0 for master
2:
// movw %si, %ax
// call reg_probe_dev_exist
cmpb $0, (reg_dev_info - int13_handler)(%si) # check existence
// cmpb $0, %al # check existence
je 3f # device not exist, so skip
/* The device exists, so do a further check for device type. */
movw %si, %ax
#; begin reg_check_dev_type
#; call after a reset
call __reg_select_dev
movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register
inb %dx, %al
movb %al, %ah
movw reg_addr - int13_handler + (3 * 2), %dx #; CB_SN = reserved for SAM TAG byte
inb %dx, %al
cmpw $0x0101, %ax # success?
movw $1, %ax # REG_CONFIG_TYPE_UNKN
jne 4f # No. The device type is unknown.
movw reg_addr - int13_handler + (4 * 2), %dx #; CB_CL = byte count low
inb %dx, %al
movb %al, %ah
movw reg_addr - int13_handler + (5 * 2), %dx #; CB_CH = byte count high
inb %dx, %al
cmpw $0x14EB, %ax # Is ATAPI?
je 5f # yes.
testb %al, %al # 0 is for ATA
movw $1, %ax # REG_CONFIG_TYPE_UNKN
jnz 4f
# AX=1
movw reg_addr - int13_handler + (7 * 2), %dx #; CB_STAT
inb %dx, %al
# AH=0
//if AL==0, then AX=1=REG_CONFIG_TYPE_UNKN, else AX=2=REG_CONFIG_TYPE_ATA
testb %al, %al
setne %al
incw %ax
jmp 4f
5:
movw $3, %ax # REG_CONFIG_TYPE_ATAPI
4:
#; end reg_check_dev_type
movb %al, (reg_dev_info - int13_handler)(%si) #; added (%si) recently
cmpb $3, %al # Is ATAPI?
jne 3f # no, ignore it.
#; It is ATAPI, so add it to the list at atapi_dev_base
incw atapi_dev_count - int13_handler # count it
movw %bx, %ax # store the pointer to the base reg pair
stosw
movw %si, %ax # store the device number(master/slave)
stosw
# debug print the reg and dev
pushw %si
pushw 2(%bx) # base port of control block regs, i.e., device control port
pushw (%bx) # base port of command block regs, i.e., data port
pushw $atapi_reg_dev - int13_handler # the format string
call realmode_printf
addw $8, %sp # adjust the stack pointer
3:
incw %si # SI=1 for slave
cmpw $2, %si
jb 2b
addw $4, %bx # try next group of base reg
#; incw %bx # try next group of base reg
#; incw %bx
cmpw $0, (%bx) # end?
jnz 1b # no, probe next reg group
movw atapi_dev_count - int13_handler, %cx
movw %cx, %bx
decw %bx
movw min_cdrom_id - int13_handler, %ax
addw %bx, %ax
movw %ax, max_cdrom_id - int13_handler
popw %di
popw %si
popw %bx
popw %ax
ret
atapi_reg_dev:
.ascii "ATAPI device found: Data=%X, Ctrl=%X, dev=%X(Note: 0=master, 1=slave)\r\n\0"
#endif /* CDROM_INIT */
#ifdef CDROM_INIT
check_atapi_cdrom:
.code16
#;return: cf =0 is cdrom, cf =1 not cdrom
pushaw
#; push es
#; push cs
#; pop es
#; mov di, atapi_devinfo
call inquiry_atapi
jc 1f
clc
#define ATATYPE_CDR 0x4 //#; Write-once device
#define ATATYPE_CD 0x5 //#; CD-ROM device
cmpb $ATATYPE_CD, %al
je 1f
cmpb $ATATYPE_CDR, %al
je 1f
stc
1:
#; pop es
popaw
ret
#endif /* CDROM_INIT */
#ifdef CDROM_INIT
inquiry_atapi:
.code16
#;input: es:di -> atapi_devinfo
#;return: cf =0 success, al = device type,
#; cf =1 fail
#; save si, di, cx
call clear_atapi_buffer
movb $0x12, atapi_cmd_buffer - int13_handler
movb $128, atapi_cmd_buffer - int13_handler + 4
# invoke reg_packet,byte,0,cs, atapi_tmp_buffer, 128
pushw $128
pushw $atapi_tmp_buffer - int13_handler
pushw %cs
pushw $0
call reg_packet
addw $8, %sp
orw %ax, %ax
jnz 1f
movb atapi_tmp_buffer - int13_handler, %al
testb $0xe0, %al
jnz 1f
andb $0x1f, %al
#if 0
mov [es:di + struc_atapi_devinfo.dev_type], al
mov ah, [atapi_tmp_buffer+7]
mov [es:di + struc_atapi_devinfo.dev_flags], ah
add di, struc_atapi_devinfo.vender_id
mov si, atapi_tmp_buffer + 8
mov cx, 24
cld
rep movsb
#endif
clc
ret
1:
stc
ret
#endif /* CDROM_INIT */
#ifdef CDROM_INIT
reg_probe_dev_exist:
.code16
#; input ax=dev
#; return ax = 1: exist # <----- "ax=1" should be "al=1"
call __reg_select_dev
# outbytes CB_SC,0x55,CB_SN,0xaa,CB_SC,0xaa,CB_SN,0x55,CB_SC,0x55
movb $0x55, %al
movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register
outb %al, %dx
movb $0xAA, %al
movw reg_addr - int13_handler + (3 * 2), %dx #; CB_SN = reserved for SAM TAG byte
outb %al, %dx
movb $0xAA, %al
movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register
outb %al, %dx
movb $0x55, %al
movw reg_addr - int13_handler + (3 * 2), %dx #; CB_SN = reserved for SAM TAG byte
outb %al, %dx
movb $0x55, %al
movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register
outb %al, %dx
movb $0xAA, %al
movw reg_addr - int13_handler + (3 * 2), %dx #; CB_SN = reserved for SAM TAG byte
outb %al, %dx
movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register
inb %dx, %al
movb %al, %ah
movw reg_addr - int13_handler + (3 * 2), %dx #; CB_SN = reserved for SAM TAG byte
inb %dx, %al
cmpw $0x55AA, %ax
sete %al
ret
#endif /* CDROM_INIT */
.code32
/*
* 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
//sti /* it is not bad keeping interrupt off */
#ifdef ABSOLUTE_WITHOUT_ASTERISK
DATA32 ADDR32 ljmp (offset)
#else
DATA32 ADDR32 ljmp *(offset)
#endif
.code32
#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.
*/
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
/* set up to pass the partition where stage2 is located in */
movl EXT_C(current_partition), %eax
movl %eax, (EXT_C(install_partition)-EXT_C(main))(%ebx)
/* set up to pass the drive where stage2 is located in */
movb EXT_C(current_drive), %dl
/* set up to pass the second sector of stage2 */
movl 0xc(%esp), %ecx
call EXT_C(prot_to_real)
.code16
//sti /* it is not bad keeping interrupt off */
movl %ecx, %ebp
#ifdef ABSOLUTE_WITHOUT_ASTERISK
DATA32 ADDR32 ljmp (offset)
#else
DATA32 ADDR32 ljmp *(offset)
#endif
.code32
#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.
*/
ENTRY(real_to_prot)
.code16
#if 0
#ifndef STAGE1_5
/* ensure A20 is on when we come back to protected mode. */
pushal
cli /* yes, keep interrupt off when controlling A20 */
movw $0x00ff, %cx # try so many times on failure
movw $0x0001, %dx # non-zero means `enable'
call enable_disable_a20
//sete %dl # DL=1 means success
popal
#endif /* ! STAGE1_5 */
#endif
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 */
/* oh, don't enable interrupt when we are controlling gateA20 */
//sti
/* return on new stack! */
DATA32 ret
.code32
/*
* int biosdisk_int13_extensions (int ax, int drive, void *dap)
*
* Call IBM/MS INT13 Extensions (int 13 %ax=AX) for DRIVE. DAP
* is passed for disk address packet. If an error occurs, return
* non-zero, otherwise zero.
*/
ENTRY(biosdisk_int13_extensions)
pushl %ebp
movl %esp, %ebp
#; +16 dap
#; +12 drive
#; +8 ax
#; +4 EIP
#; ebp EBP
#; -4 ESI
#; -8 EBX
pushl %esi
pushl %ebx
pushl %ecx
pushl %edx
/* compute the address of disk_address_packet */
movl 0x10(%ebp), %eax #; linear address of dap
/* if DS can be 0x40, we can avoid AWARD BIOS bug of int13/AX=4B01 */
subl $0x400, %eax
shll $1, %eax
movw %ax, %si
shrw $1, %si #; low 15-bit for offset
xorw %ax, %ax
shrl $5, %eax #; segment value in AX
addw $0x40, %ax
movw %ax, %cx /* save the segment to cx */
/* drive */
movb 0xc(%ebp), %dl
/* ax */
movw 0x8(%ebp), %bx
/* enter real mode */
call EXT_C(prot_to_real)
.code16
sti #; cli should also work for biosdisk_int13_extensions
movw %bx, %ax
movw %cx, %ds
/* set additional registers to serve buggy BIOSes. */
pushw %di
pushw %bx
movw %cx, %es
movw %si, %di
movw %si, %bx
#ifdef STAGE1_5
int $0x13
#else
call safe_int13
#endif
popw %bx
popw %di
movb $1, %dl /* set error */
jc 1f
movb %ah, %dl /* save return value */
1:
/* clear the data segment */
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
#ifndef STAGE1_5
/* if it is not read/write operation, we can skip the A20 code. */
andb $0xFE, %bh
cmpb $0x42, %bh
jne 1f
/* ensure A20 is on when we come back to protected mode. */
pushal
cli /* yes, keep interrupt off when controlling A20 */
movw $0x00ff, %cx # try so many times on failure
movw $0x0001, %dx # non-zero means `enable'
call enable_disable_a20
//sete %dl # DL=1 means success
popal
1:
#endif /* ! STAGE1_5 */
/* back to protected mode */
DATA32 call EXT_C(real_to_prot)
.code32
movzbl %dl, %eax /* return value in %eax */
popl %edx
popl %ecx
popl %ebx
popl %esi
popl %ebp
ret
/*
* int biosdisk_standard (int ah, int drive, int coff, int hoff, int soff,
* int nsec, int segment)
*
* Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write
* NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs,
* return non-zero, otherwise zero.
*/
ENTRY(biosdisk_standard)
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %edi
pushl %esi
/* set up CHS information */
movl 0x10(%ebp), %eax
movb %al, %ch
movb 0x18(%ebp), %al
shlb $2, %al
shrw $2, %ax
movb %al, %cl
movb 0x14(%ebp), %dh
/* drive */
movb 0xc(%ebp), %dl
/* segment */
movw 0x20(%ebp), %bx
/* save nsec and ah to %di */
movb 0x8(%ebp), %ah
movb 0x1c(%ebp), %al
movw %ax, %di
/* enter real mode */
call EXT_C(prot_to_real)
.code16
//sti #; biosdisk_standard won't require sti
sti #; added 2006-11-30
movw %bx, %es
movw $3, %si /* attempt at least three times */
1:
pushw %si
pushw %di
pushw %cx
pushw %dx
xorw %bx, %bx
movw %di, %ax
#ifdef STAGE1_5
int $0x13
#else
call safe_int13
#endif
popw %dx
popw %cx
popw %di
popw %si
movb %ah, %bl /* save return value */
jc 3f /* check if successful */
testb %ah, %ah
jz 2f
3:
movw %di, %ax
cmpb $0x04, %ah /* verify sectors? */
je 4f /* yes, do not retry */
/* if fail, reset the disk system */
pushw %si
pushw %di
pushw %cx
pushw %dx
xorw %ax, %ax
#ifdef STAGE1_5
int $0x13
#else
call safe_int13
#endif
popw %dx
popw %cx
popw %di
popw %si
decw %si
jnz 1b /* retry */
4:
movb $1, %bl
2:
#ifndef STAGE1_5
/* ensure A20 is on when we come back to protected mode. */
pushal
cli /* yes, keep interrupt off when controlling A20 */
movw $0x00ff, %cx # try so many times on failure
movw $0x0001, %dx # non-zero means `enable'
call enable_disable_a20
//sete %dl # DL=1 means success
popal
#endif /* ! STAGE1_5 */
/* back to protected mode */
DATA32 call EXT_C(real_to_prot)
.code32
movb %bl, %al /* return value in %eax */
popl %esi
popl %edi
popl %ebx
popl %ebp
ret
/*
* int check_int13_extensions (int drive)
*
* Check if LBA is supported for DRIVE. If it is supported, then return
* the major version and API support bits of extensions, otherwise zero.
*/
ENTRY(check_int13_extensions)
pushl %ebp
movl %esp, %ebp
pushl %ebx
/* drive */
movb 0x8(%ebp), %dl
/* enter real mode */
call EXT_C(prot_to_real)
.code16
//sti #; check_int13_extensions won't require sti
sti #; added 2006-11-30
pushw %cx
pushw %dx
movb $0x41, %ah
movw $0x55aa, %bx
#ifdef STAGE1_5
int $0x13
#else
call safe_int13
#endif
/* check the result */
jc 1f
cmpw $0xaa55, %bx
jne 1f
movb %ah, %bl /* save the major version into %bl */
/* check if AH=0x42 is supported if FORCE_LBA is zero */
movb EXT_C(force_lba), %al
testb %al, %al
#if 0
jnz 2f
andw $1, %cx
jnz 2f
#else
setnz %al
orb %al, %cl
jmp 2f
#endif
1:
xorw %bx, %bx
xorw %cx, %cx
2:
roll $16, %ebx #; version number in high word
movw %cx, %bx #; API subset support bitmap in low word
popw %dx
popw %cx
/* back to protected mode */
DATA32 call EXT_C(real_to_prot)
.code32
xchgl %eax, %ebx /* return value in %eax */
popl %ebx
popl %ebp
ret
/*
* 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.
*/
ENTRY(get_diskinfo_standard)
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %edi
/* drive */
movb 0x8(%ebp), %dl
/* enter real mode */
call EXT_C(prot_to_real)
.code16
//sti #; get_diskinfo_standard won't require sti
sti #; added 2006-11-30
xorw %cx, %cx
movb $0x8, %ah
#ifdef STAGE1_5
int $0x13
#else
call safe_int13
#endif
jnc 3f
/* failure */
testb %ah, %ah
jz 2f
jmp 1f
3:
/* check if successful */
testb %ah, %ah
jnz 1f /* Error number in AH */
/* bogus BIOSes may not return an error number */
testb $0x3f, %cl /* 0 sectors means no disk */
jnz 1f /* if non-zero, then succeed */
2:
/* XXX 0x60 is one of the unused error numbers */
movb $0x60, %ah
1:
movb %ah, %bl /* save return value in %bl */
/* back to protected mode */
DATA32 call EXT_C(real_to_prot)
.code32
testb %bl, %bl /* check failure */
jnz 1f /* failure */
/* restore %ebp */
leal 0x8(%esp), %ebp
/* heads */
xorl %eax, %eax
movb %dh, %al
incl %eax /* number of heads is counted from zero */
movl 0x10(%ebp), %edi
//movl %eax, (%edi)
stosl
/* sectors */
xorl %eax, %eax
movb %cl, %al
andb $0x3f, %al
movl 0x14(%ebp), %edi
//movl %eax, (%edi)
stosl
/* cylinders */
xorl %eax, %eax
shrb $6, %cl
movb %cl, %ah
movb %ch, %al
incl %eax /* number of cylinders is counted from zero */
movl 0x0C(%ebp), %edi
//movl %eax, (%edi)
stosl
1:
movzbl %bl, %eax /* return value in %eax */
popl %edi
popl %ebx
popl %ebp
ret
#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.
*/
ENTRY(get_diskinfo_floppy)
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %esi
/* drive */
movb 0x8(%ebp), %dl
/* enter real mode */
call EXT_C(prot_to_real)
.code16
//sti /* it is not bad keeping interrupt off */
sti #; added 2006-11-30
/* init probe value */
movl $probe_values-1, %esi
1:
xorw %ax, %ax
#ifdef STAGE1_5
int $0x13
#else
call safe_int13
#endif
incw %si
movb (%si), %cl
cmpb $0, %cl /* probe failed if zero */
je 2f
/* perform read */
movw $SCRATCHSEG, %ax
movw %ax, %es
xorw %bx, %bx
movw $0x0201, %ax
movb $0, %ch
movb $0, %dh
#ifdef STAGE1_5
int $0x13
#else
call safe_int13
#endif
/* FIXME: Read from floppy may fail even if the geometry is correct.
So should retry at least three times. */
jc 1b /* next value */
/* succeed */
jmp 2f
probe_values:
.byte 36, 18, 15, 9, 0
2:
/* back to protected mode */
DATA32 call EXT_C(real_to_prot)
.code32
/* restore %ebp */
leal 0x8(%esp), %ebp
/* cylinders */
movl 0xc(%ebp), %eax
movl $80, %ebx
movl %ebx, (%eax)
/* heads */
movl 0x10(%ebp), %eax
movl $2, %ebx
movl %ebx, (%eax)
/* sectors */
movl 0x14(%ebp), %eax
movzbl %cl, %ebx
movl %ebx, (%eax)
/* return value in %eax */
xorl %eax, %eax
cmpb $0, %cl
jne 3f
incl %eax /* %eax = 1 (non-zero) */
3:
popl %esi
popl %ebx
popl %ebp
ret
#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.
*/
ENTRY(get_code_end)
/* will be the end of the bss */
# if defined(HAVE_END_SYMBOL)
movl $end, %eax
# elif defined(HAVE_USCORE_END_SYMBOL)
movl $_end, %eax
# endif
shrl $2, %eax /* Round up to the next word. */
incl %eax
shll $2, %eax
ret
#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.
*
*/
ENTRY(get_memsize)
pushl %ebp
pushl %ebx
movl 0xc(%esp), %ebx
call EXT_C(prot_to_real) /* enter real mode */
.code16
//sti /* it is not bad keeping interrupt off */
cmpb $0x1, %bl
//DATA32 je xext
je 1f
int $0x12
//DATA32 jmp xdone
jmp 2f
//xext:
1:
movb $0x88, %ah
int $0x15
//xdone:
2:
movw %ax, %bx
DATA32 call EXT_C(real_to_prot)
.code32
movw %bx, %ax
popl %ebx
popl %ebp
ret
#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.
*
*/
ENTRY(get_eisamemsize)
pushl %ebp
pushl %ebx
pushl %ecx
pushl %edx
call EXT_C(prot_to_real) /* enter real mode */
.code16
//sti /* it is not bad keeping interrupt off */
movw $0xe801, %ax
int $0x15
jc 1f
testb $0x80, %ah
jnz 1f
shll $16, %ebx
movw %ax, %bx
testl %ebx, %ebx
jnz 3f
movw %cx, %ax
movw %dx, %bx
shll $16, %ebx
movw %ax, %bx
3:
jmp 2f
1:
movl $0xFFFFFFFF, %ebx
2:
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
DATA32 call EXT_C(real_to_prot)
.code32
movl %ebx, %eax
popl %edx
popl %ecx
popl %ebx
popl %ebp
ret
/*
*
* 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.
*/
ENTRY(get_mmap_entry)
pushl %ebp
pushl %ebx
pushl %edi
pushl %esi
/* place address (+4) in ES:DI */
movl 0x14(%esp), %eax
addl $4, %eax
movl %eax, %edi
andl $0xf, %edi
shrl $4, %eax
movl %eax, %esi
/* set continuation value */
movl 0x18(%esp), %ebx
pushl %ecx /* save ECX */
/* 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
//sti /* it is not bad keeping interrupt off */
movw %si, %es
/* set additional registers to serve buggy BIOSes. */
movw %si, %ds
movw %di, %si
movl $0xe820, %eax
int $0x15
//DATA32 jc xnosmap
jnc 1f
movl $0, %ebx /* set end indicator */
1:
cmpl $0x534d4150, %eax
//DATA32 jne xnosmap
jne 1f
/* 20-byte length is currently standard. So others are considered
* invalid.
*/
cmpl $0x14, %ecx
//DATA32 jb xnosmap
//
//cmpl $0x400, %ecx
//DATA32 jg xnosmap
//DATA32 jmp xsmap
je 2f
//xnosmap:
1:
movl $0, %ecx
//xsmap:
2:
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
DATA32 call EXT_C(real_to_prot)
.code32
popl %eax /* OLD ECX */
/* write length of buffer (zero if error) into "addr" */
movl 0x14(%esp), %edi
xchgl %eax, %ecx
stosl
/* set return value to continuation */
movl %ebx, %eax
popl %esi
popl %edi
popl %ebx
popl %ebp
ret
/*
* get_rom_config_table()
*
* Get the linear address of a ROM configuration table. Return zero,
* if fails.
*/
ENTRY(get_rom_config_table)
pushl %ebp
pushl %ebx
pushl %edx
/* zero %ebx for simplicity */
xorl %ebx, %ebx
call EXT_C(prot_to_real)
.code16
//sti /* it is not bad keeping interrupt off */
movb $0xc0, %ah
int $0x15
jc no_rom_table
testb %ah, %ah
jnz no_rom_table
movw %es, %dx
jmp found_rom_table
no_rom_table:
xorw %dx, %dx
xorw %bx, %bx
found_rom_table:
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
DATA32 call EXT_C(real_to_prot)
.code32
/* compute the linear address */
xorl %eax, %eax
movw %dx, %ax
shll $4, %eax
addl %ebx, %eax
popl %edx
popl %ebx
popl %ebp
ret
/*
* int get_vbe_controller_info (struct vbe_controller *controller_ptr)
*
* Get VBE controller information.
*/
ENTRY(get_vbe_controller_info)
pushl %ebp
movl %esp, %ebp
pushl %edi
pushl %ebx
pushl %esi
/* Convert the linear address to segment:offset */
movl 8(%ebp), %eax
movl %eax, %edi
andl $0x0000000f, %edi
shrl $4, %eax
movl %eax, %ebx
call EXT_C(prot_to_real)
.code16
//sti /* it is not bad keeping interrupt off */
movw %bx, %es
/* set additional registers to serve buggy BIOSes. */
movw %bx, %ds
movw %di, %si
movw $0x4F00, %ax
int $0x10
movw %ax, %bx
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
DATA32 call EXT_C(real_to_prot)
.code32
movzwl %bx, %eax
popl %esi
popl %ebx
popl %edi
popl %ebp
ret
/*
* int get_vbe_mode_info (int mode_number, struct vbe_mode *mode_ptr)
*
* Get VBE mode information.
*/
ENTRY(get_vbe_mode_info)
pushl %ebp
movl %esp, %ebp
pushl %edi
pushl %ebx
/* Convert the linear address to segment:offset */
movl 0xc(%ebp), %eax
movl %eax, %edi
andl $0x0000000f, %edi
shrl $4, %eax
movl %eax, %ebx
/* Save the mode number in %cx */
movl 0x8(%ebp), %ecx
call EXT_C(prot_to_real)
.code16
//sti /* it is not bad keeping interrupt off */
movw %bx, %es
movw $0x4F01, %ax
int $0x10
movw %ax, %bx
DATA32 call EXT_C(real_to_prot)
.code32
movzwl %bx, %eax
popl %ebx
popl %edi
popl %ebp
ret
/*
* int set_vbe_mode (int mode_number)
*
* Set VBE mode. Don't support user-specified CRTC information.
*/
ENTRY(set_vbe_mode)
pushl %ebp
movl %esp, %ebp
pushl %ebx
/* Save the mode number in %bx */
movl 0x8(%ebp), %ebx
/* Clear bit D11 */
andl $0xF7FF, %ebx
call EXT_C(prot_to_real)
.code16
//sti /* it is not bad keeping interrupt off */
movw $0x4F02, %ax
int $0x10
movw %ax, %bx
DATA32 call EXT_C(real_to_prot)
.code32
movzwl %bx, %eax
popl %ebx
popl %ebp
ret
/*
* int 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. :-(
*
* on call: linear=0 for a20 off and 1 for on
*
* return value: 0 for failure and 1 for success
*
*/
ENTRY(gateA20)
pushl %ebp
movl 8(%esp), %edx /* the value of `linear' */
#if 1
/* first, check if A20 status is already what we desired. */
/* disable CPU cache for the test to work reliably. */
movl %cr0, %eax
pushl %eax /* save old cr0 */
// andl $0x00000011, %eax
orl $0x60000000, %eax /* set CD and NW */
movl %eax, %cr0
movl %cr0, %eax
testl $0x60000000, %eax /* check if we can use wbinvd. */
jz 1f /* CPU has no wbinvd instruction. */
wbinvd
andl $0xDFFFFFFF, %eax /* clear NW */
movl %eax, %cr0
1:
movl 0x00000000, %eax
pushl %eax /* save old int0 vector */
cmpl 0x00100000, %eax
jne 1f /* A20 is on */
notl 0x00000000
movl 0x00000000, %eax
cmpl 0x00100000, %eax
notl 0x00000000 /* logical `NOT' won't touch flags */
1:
/* ZF=0(means not equal) for A20 on, ZF=1(means equal) for A20 off. */
sete %al /* save ZF to AL */
testl %edx, %edx
sete %ah /* save ZF to AH */
cmpb %al, %ah
/* ZF=1(means equal) for desired and we needn't do anything. */
popl %eax /* restore int0 vector */
movl %eax, 0x00000000
popl %eax /* restore cr0 */
movl %eax, %cr0
movl $1, %eax /* return value 1 means success */
jz 1f /* success */
#endif
/* failure, then call a real-mode function enable_disable_a20 */
call EXT_C(prot_to_real)
.code16
cli /* yes, keep interrupt off when controlling A20 */
movw $0x00ff, %cx # try so many times on failure
movb $0x01, %dh # non-zero means `reset keyborad'
call enable_disable_a20
sete %dl # DL=1 means success
#if 0 /* comment out to avoid hanging ONDA C51G board. */
pushal
#if 0
# qemu arrow keys will not work if we turn no NumLock in this way.
/* Turn on Number Lock */
orb $0x20, 0x417
#endif
/* reset mouse */
movw $0xC201, %ax
int $0x15
/* disable mouse */
movw $0xC200, %ax
xorw %bx, %bx /* BH=0 means disable */
int $0x15
popal
#endif
#if 0
movw $0x2400, %ax
testw %dx, %dx
jz 1f
incw %ax
1: stc
int $0x15
jnc 2f
/* set non-zero if failed */
movb $1, %ah
/* save the status */
2: movb %ah, %dl
#endif
DATA32 call EXT_C(real_to_prot)
.code32
movzbl %dl, %eax
1:
popl %ebp
ret
#if 0
popl %ebp
testb %dl, %dl
jnz 3f
ret
3: /* use keyboard controller */
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
/* output a dummy command (USB keyboard hack) */
movb $0xff, %al
outb $K_CMD
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
#endif
.code16
// #include "a20.inc"
.code32
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
/*
* linux_boot()
*
* Does some funky things (including on the stack!), then jumps to the
* entry point of the Linux setup code.
*/
VARIABLE(linux_text_len)
.long 0
VARIABLE(linux_data_tmp_addr)
.long 0
VARIABLE(linux_data_real_addr)
.long 0
ENTRY(linux_boot)
/* don't worry about saving anything, we're committed at this point */
cld /* forward copying */
/* copy kernel */
movl EXT_C(linux_text_len), %ecx
addl $3, %ecx
shrl $2, %ecx
movl $LINUX_BZIMAGE_ADDR, %esi # 0x100000
movl $LINUX_ZIMAGE_ADDR, %edi # 0x10000
rep movsl
ENTRY(big_linux_boot)
movl EXT_C(linux_data_real_addr), %ebx
/* copy the real mode part */
movl EXT_C(linux_data_tmp_addr), %esi
movl %ebx, %edi
movl $LINUX_SETUP_MOVE_SIZE, %ecx # 0x9100
cld
rep movsb
/* change %ebx to the segment address */
shrl $4, %ebx #; CS of LINUX SETUP, high word = 0
movl %ebx, %eax
addl $0x20, %eax #; one sector
movw %ax, 1f // linux_setup_seg
#; /* XXX new stack pointer in safe area for calling functions */
#; movl $0x4000, %esp
#; call EXT_C(stop_floppy)
/* final setup for linux boot */
call EXT_C(prot_to_real)
.code16
/* final setup for linux boot */
cli
movw %bx, %ss
movw $LINUX_SETUP_STACK, %sp # 0x9000
movw %bx, %ds
movw %bx, %es
movw %bx, %fs
movw %bx, %gs
#; /* Reset floppy. Not required. */
#; xorw %ax, %ax
#; xorb %dl, %dl
#; int $0x13
/* jump to start */
/* ljmp */
.byte 0xea
.word 0
1: //linux_setup_seg:
.word 0
.code32
/*
* multi_boot(int start, int mb_info)
*
* 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)
#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.
*/
ENTRY(console_putchar)
movl 0x4(%esp), %edx
pusha
#ifdef STAGE1_5
movb $0x07, %bl
#else
movl EXT_C(console_current_color), %ebx
#endif
call EXT_C(prot_to_real)
.code16
//sti /* it is not bad keeping interrupt off */
movb %dl, %al
xorb %bh, %bh
#ifndef STAGE1_5
/* 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
#endif /* ! STAGE1_5 */
1: movb $0xe, %ah
int $0x10
3: DATA32 call EXT_C(real_to_prot)
.code32
popa
ret
#ifndef STAGE1_5
/* this table is used in translate_keycode below */
translation_table:
.word KEY_LEFT, 2
.word KEY_RIGHT, 6
.word KEY_UP, 16
.word KEY_DOWN, 14
.word KEY_HOME, 1
.word KEY_END, 5
.word KEY_DC, 4
.word KEY_BACKSPACE, 8
.word KEY_PPAGE, 7
.word KEY_NPAGE, 3
.word 0
/*
* translate_keycode translates the key code %dx to an ascii code.
*/
.code16
translate_keycode:
pushw %bx
pushw %si
movw $ABS(translation_table), %si
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
/*
* remap_ascii_char remaps the ascii code %dl to another if the code is
* contained in ASCII_KEY_MAP.
*/
.code16
remap_ascii_char:
pushw %si
movw $ABS(EXT_C(ascii_key_map)), %si
1:
lodsw
/* check if this is the end */
testw %ax, %ax
jz 2f
/* check if this matches the ascii code */
cmpb %al, %dl
jne 1b
/* if so, perform the mapping */
movb %ah, %dl
2:
/* restore %si */
popw %si
ret
.code32
.align 4
ENTRY(ascii_key_map)
.space (KEY_MAP_SIZE + 1) * 2
/*
* 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
*/
ENTRY(console_getkey)
push %ebp
call EXT_C(prot_to_real)
.code16
sti /* getkey needs interrupt on */
#; work around for Apple BIOS getkey bug
#; check the keyboard buffer, until there is a keypress.
1:
movb $0x01, %ah #; check key
int $0x16
jz 1b #; no keypress
xorw %ax, %ax
int $0x16
movw %ax, %dx /* real_to_prot uses %eax */
call translate_keycode
call remap_ascii_char
DATA32 call EXT_C(real_to_prot)
.code32
movw %dx, %ax
pop %ebp
ret
/*
* 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
*/
ENTRY(console_checkkey)
push %ebp
xorl %edx, %edx
call EXT_C(prot_to_real) /* enter real mode */
.code16
sti /* checkkey needs interrupt on */
movb $0x1, %ah
int $0x16
DATA32 jz notpending
movw %ax, %dx
call translate_keycode
call remap_ascii_char
DATA32 jmp pending
notpending:
movl $0xFFFFFFFF, %edx
pending:
DATA32 call EXT_C(real_to_prot)
.code32
mov %edx, %eax
pop %ebp
ret
/*
* 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)
*/
ENTRY(console_getxy)
push %ebp
push %ebx /* save EBX */
call EXT_C(prot_to_real)
.code16
//sti /* it is not bad keeping interrupt off */
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 %ebx
pop %ebp
ret
/*
* 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)
*/
ENTRY(console_gotoxy)
push %ebp
push %ebx /* save EBX */
movb 0xc(%esp), %dl /* %dl = x */
movb 0x10(%esp), %dh /* %dh = y */
call EXT_C(prot_to_real)
.code16
//sti /* it is not bad keeping interrupt off */
xorb %bh, %bh /* set page to 0 */
movb $0x2, %ah
int $0x10 /* set cursor position */
DATA32 call EXT_C(real_to_prot)
.code32
pop %ebx
pop %ebp
ret
/*
* 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)
*/
ENTRY(console_cls)
push %ebp
push %ebx /* save EBX */
call EXT_C(prot_to_real)
.code16
//sti /* it is not bad keeping interrupt off */
/* 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 EXT_C(real_to_prot)
.code32
pop %ebx
pop %ebp
ret
/*
* 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
*/
console_cursor_state:
.byte 1
console_cursor_shape:
.word 0
ENTRY(console_setcursor)
push %ebp
push %ebx
/* check if the standard cursor shape has already been saved */
movw console_cursor_shape, %ax
testw %ax, %ax
jne 1f
call EXT_C(prot_to_real)
.code16
//sti /* it is not bad keeping interrupt off */
movb $0x03, %ah
xorb %bh, %bh
int $0x10
DATA32 call EXT_C(real_to_prot)
.code32
movw %cx, console_cursor_shape
1:
/* set %cx to the designated cursor shape */
movw $0x2000, %cx
movl 0xc(%esp), %ebx
testl %ebx, %ebx
jz 2f
movw console_cursor_shape, %cx
2:
call EXT_C(prot_to_real)
.code16
//sti /* it is not bad keeping interrupt off */
movb $0x1, %ah
int $0x10
DATA32 call EXT_C(real_to_prot)
.code32
movzbl console_cursor_state, %eax
movb %bl, console_cursor_state
pop %ebx
pop %ebp
ret
/* graphics mode functions */
#ifdef SUPPORT_GRAPHICS
VARIABLE(cursorX)
.word 0
VARIABLE(cursorY)
.word 0
VARIABLE(cursorCount)
.word 0
VARIABLE(cursorBuf)
.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.
*/
ENTRY(set_videomode)
push %ebp
push %ebx
push %ecx
movb 0x10(%esp), %cl
call EXT_C(prot_to_real)
.code16
//sti /* it is not bad keeping interrupt off */
xorw %bx, %bx
movb $0xf, %ah
int $0x10 /* Get Current Video mode */
movb %al, %ch
xorb %ah, %ah
movb %cl, %al
int $0x10 /* Set Video mode */
DATA32 call EXT_C(real_to_prot)
.code32
xorb %ah, %ah
movb %ch, %al
pop %ecx
pop %ebx
pop %ebp
ret
/*
* unsigned char * graphics_get_font()
* BIOS call "INT 10H Function 11h" to set font
* Call with %ah = 0x11
*/
ENTRY(graphics_get_font)
push %ebp
push %ebx
push %ecx
push %edx
call EXT_C(prot_to_real)
.code16
//sti /* it is not bad keeping interrupt off */
movw $0x1130, %ax
movb $6, %bh /* font 8x16 */
int $0x10
movw %bp, %dx
movw %es, %cx
DATA32 call EXT_C(real_to_prot)
.code32
xorl %eax, %eax
movw %cx, %ax
shll $4, %eax
movw %dx, %ax
pop %edx
pop %ecx
pop %ebx
pop %ebp
ret
/*
* 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)
*/
ENTRY(graphics_set_palette)
push %ebp
push %eax
push %ebx
push %ecx
push %edx
movw $0x3c8, %bx /* address write mode register */
/* wait vertical retrace */
movw $0x3da, %dx
l1b: inb %dx, %al /* wait vertical active display */
test $8, %al
jnz l1b
l2b: inb %dx, %al /* wait vertical retrace */
test $8, %al
jnz l2b
mov %bx, %dx
movb 0x18(%esp), %al /* index */
outb %al, %dx
inc %dx
movb 0x1c(%esp), %al /* red */
outb %al, %dx
movb 0x20(%esp), %al /* green */
outb %al, %dx
movb 0x24(%esp), %al /* blue */
outb %al, %dx
movw 0x18(%esp), %bx
call EXT_C(prot_to_real)
.code16
//sti /* it is not bad keeping interrupt off */
movb %bl, %bh
movw $0x1000, %ax
int $0x10
DATA32 call EXT_C(real_to_prot)
.code32
pop %edx
pop %ecx
pop %ebx
pop %eax
pop %ebp
ret
#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)
*/
ENTRY(getrtsecs)
push %ebp
call EXT_C(prot_to_real) /* enter real mode */
.code16
#;sti /* getrtsecs needs interrupt on */
sti #; added 2006-11-30
movb $0x2, %ah
/* Ralf Brown's Interrupt List says:
*
* BUG: Some BIOSes leave CF unchanged if successful,
* so CF should be cleared before calling this function
*/
clc
int $0x1a
DATA32 jnc gottime
movb $0xff, %dh
gottime:
DATA32 call EXT_C(real_to_prot)
.code32
movb %dh, %al
pop %ebp
ret
/*
* currticks()
* return the real time in ticks, of which there are about
* 18-20 per second
*/
ENTRY(currticks)
pushl %ebp
call EXT_C(prot_to_real) /* enter real mode */
.code16
#;sti /* currticks needs interrupt on */
sti #; added 2006-11-30
/* %ax is already zero */
int $0x1a
DATA32 call EXT_C(real_to_prot)
.code32
movl %ecx, %eax
shll $16, %eax
movw %dx, %ax
popl %ebp
ret
#endif /* STAGE1_5 */
/*
* This is the area for all of the special variables.
*/
.p2align 2 /* force 4-byte alignment */
protstack:
.long PROTSTACKINIT
VARIABLE(boot_drive)
#ifdef SUPPORT_DISKLESS
.long NETWORK_DRIVE
#else
.long 0
#endif
VARIABLE(install_second_sector)
.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
VARIABLE(apm_bios_info)
.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.
*/
.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 */
.code32
/* this code will be moved to and get executed at HMA_ADDR=0x2B0000 */
/* our gdt starts at HMA_ADDR=0x2B0000 */
ENTRY(HMA_start)
/* the first entry of GDT, i.e., the default null entry,
* can be any value. it never get used. So we use these
* 8 bytes for our jmp and GDT descriptor
*/
jmp 1f /* two-byte short jmp */
. = HMA_start + 2
/* 6-byte GDT descriptor */
gdtdescHMA:
.word 0x27 /* limit */
.long HMA_ADDR /* linear base address */
// this is the default null entry in GDT
//
// .word 0, 0
// .byte 0, 0, 0, 0
. = HMA_start + 8
/* code segment, although it is no use here for now */
.word 0xFFFF, 0
.byte 0, 0x9A, 0xCF, 0
/* data segment, although it is no use here for now */
.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
//
// . = (HMA_start - 0x10) + 0xfff0
#if 0
/* real mode code segment base=0xffff0 */
.word 0xFFFF, 0xfff0
.byte 0x0f, 0x9E, 0, 0
/* real mode data segment base=0xffff0 */
.word 0xFFFF, 0xfff0
.byte 0x0f, 0x92, 0, 0
#else
/* 16-bit code segment base=0x2B0000 */
.word 0xFFFF, 0x0000
.byte 0x2B, 0x9E, 0, 0
/* real mode data segment base=0x200 */
.word 0xFFFF, 0x0200
.byte 0x00, 0x92, 0, 0
#endif
1:
/* set up to pass boot drive */
movb EXT_C(boot_drive), %dl
/* check if the --ebx option is given. */
movl (chain_ebx_set - HMA_start + HMA_ADDR), %eax
testl %eax, %eax
jz 1f
movl (chain_ebx - HMA_start + HMA_ADDR), %ebx
1:
/* check if the --edx option is given. */
movl (chain_edx_set - HMA_start + HMA_ADDR), %eax
testl %eax, %eax
jz 1f
movl (chain_edx - HMA_start + HMA_ADDR), %edx
1:
/* move new loader from extended memory to conventional memory.
* this will overwrite our GRUB code, data and stack, so we should not
* use instuctions like push/pop/call/ret, and we should not use
* functions like gateA20().
*/
// /* clear the memory range 0000:7e00 - 2000:0000 */
// movl $0x00007e00, %edi
// movl $((0x00020000 - 0x00007e00) / 4), %ecx
// xorl %eax, %eax
// cld
// repz stosl
/* the new loader is currently at 0x200000 */
movl $0x00200000, %esi
xorl %eax, %eax
xorl %edi, %edi
movw (chain_load_segment - HMA_start + HMA_ADDR), %di
shll $4, %edi
movw (chain_load_offset - HMA_start + HMA_ADDR), %ax
addl %eax, %edi
//movl $0x00007c00, %edi
movl (chain_load_length - HMA_start + HMA_ADDR), %ecx
cld
repz movsb
//#define WRAPCODEBEGIN 0xb0
//#define WRAPCODESIZE 0x50
#if 0
movl (chain_enable_gateA20 - HMA_start + HMA_ADDR), %eax
testl %eax, %eax
jnz 1f
#if 1
/* disable A20 */
#else
/* move real mode code(running when gateA20 is disabled) to 0x300 */
movl $(HMA_ADDR + WrapCodeBegin - HMA_start), %esi
movl $0x00000300, %edi
movl $(WrapCodeEnd - WrapCodeBegin), %ecx
repz movsb
#endif
1:
#endif
/* switch to real mode */
/* set new GDT */
lgdt (gdtdescHMA - HMA_start + HMA_ADDR)
/* set up segment limits */
movw $PSEUDO_RM_DSEG, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
movl $0x200, %esp /* points to end of interrupt vector table */
/* SS base=0x200, so SS:SP=physical 0x400 */
/* jump to a 16 bit segment, this might be an extra step:
* set up CS limit, also clear high word of EIP
*/
#if 0
ljmp $PSEUDO_RM_CSEG, $(1f - HMA_start + 0x10)
#else
ljmp $PSEUDO_RM_CSEG, $(1f - HMA_start)
#endif
1:
.code16
/* clear the PE bit of CR0 */
movl %cr0, %eax
andl $CR0_PE_OFF, %eax
movl %eax, %cr0
#if 1
/* setup DS, ES, SS, FS and GS before loading CS */
xorl %eax, %eax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movl $0x400, %esp
movw %ax, %fs
movw %ax, %gs
#endif
/* flush prefetch queue, reload %cs */
#if 1
.byte 0xEA /* ljmp 0000:7c00 */
VARIABLE(chain_boot_IP)
.word 0x7c00 /* offset */
VARIABLE(chain_boot_CS)
.word 0x0000 /* segment */
#else
ljmp $0xffff, $(1f - HMA_start + 0x10)
1:
/* 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
movl %eax, %esp /* clear high word of ESP */
movw $0x400, %sp /* points to end of interrupt vector table */
///* restore interrupts */
//sti
movl %cs:(chain_enable_gateA20 - HMA_start + 0x10), %eax
testl %eax, %eax
jnz 2f
pushal
ljmp $0, $0x300 //jmp WrapCodeBegin
#endif
VARIABLE(chain_load_offset)
.word 0x7c00
VARIABLE(chain_load_segment)
.word 0x0000
VARIABLE(chain_load_length)
.long 0x200
VARIABLE(chain_ebx)
.long 0
VARIABLE(chain_ebx_set)
.long 0
VARIABLE(chain_edx)
.long 0
VARIABLE(chain_edx_set)
.long 0
VARIABLE(chain_enable_gateA20)
.long 0
//. = HMA_start + WRAPCODEBEGIN //. = HMA_start + 0xb0
/* this final piece of code will also be copied to absolute
* address 0x300, occupying part of the real mode IDT table */
#if 0
WrapCodeBegin:
call 1f
popal
2:
.byte 0xEA /* ljmp 0000:7c00 */
VARIABLE(chain_boot_IP)
.word 0x7c00 /* offset */
VARIABLE(chain_boot_CS)
.word 0x0000 /* segment */
1: /* routine to disable gateA20 */
/* first, try a BIOS call */
// pushl %ebp
// movl 8(%esp), %edx
// call EXT_C(prot_to_real)
// .code16
// //sti /* it is not bad keeping interrupt off */
movw $0x2400, %ax
stc
int $0x15
jc 1f
testb %ah, %ah
jnz 1f
ret
// jnc 2f
/* set non-zero if failed */
// movb $1, %ah
/* save the status */
//2: movb %ah, %dl
// DATA32 call EXT_C(real_to_prot)
// .code32
// popl %ebp
// testb %dl, %dl
// jnz 3f
// ret
1:
/* use keyboard controller */
// pushl %eax
call 1f//gloop1
movb $KC_CMD_WOUT, %al
outb $K_CMD
2://gloopint1:
inb $K_STATUS
andb $K_IBUF_FUL, %al
jnz 2b//gloopint1
movb $KB_OUTPUT_MASK, %al
// cmpb $0, 0x8(%esp)
// jz gdoit
// orb $KB_A20_ENABLE, %al
//gdoit:
outb $K_RDWR
call 1f//gloop1
/* output a dummy command (USB keyboard hack) */
movb $0xff, %al
outb $K_CMD
call 1f//gloop1
// popl %eax
ret
1://gloop1:
inb $K_STATUS
andb $K_IBUF_FUL, %al
jnz 1b//gloop1
1://gloop2:
inb $K_STATUS
andb $K_OBUF_FUL, %al
jz 1f//gloop2ret
inb $K_RDWR
jmp 1b//gloop2
1://gloop2ret:
ret
WrapCodeEnd:
//. = HMA_start + WRAPCODEBEGIN + WRAPCODESIZE
/* ensure this not exceed 0xc0 */
. = . - ((WrapCodeEnd - WrapCodeBegin) / (0xc0 + 1))
#endif
/* ensure this not exceed 0xfff0, i.e., (64K -16bytes) */
. = . - ((. - HMA_start) / 0xfff1)
/* ensure this resides in the first 64KB */
. = . - (ABS(.) / 0x10001)
.code32