blob: 23718e37e9ab3be6393b8dbb5f8135f66df37cb1 [file] [log] [blame]
/*
* bootsect.s Copyright (C) 1991, 1992 Linus Torvalds
*
* bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
* itself out of the way to address 0x90000, and jumps there.
*
* It then loads 'setup' directly after itself (0x90200), and the system
* at 0x10000, using BIOS interrupts.
*
* The loader has been made as simple as possible, and continuos
* read errors will result in a unbreakable loop. Reboot by hand. It
* loads pretty fast by getting whole tracks at a time whenever possible.
*
* 1-Jan-96 Modified by Chris Brady for use as a boot loader for MemTest-86.
*/
#include "defs.h"
ROOT_DEV = 0
.code16
.section ".bootsect", "ax", @progbits
_boot:
# ld86 requires an entry symbol. This may as well be the usual one.
.globl _main
_main:
movw $BOOTSEG, %ax
movw %ax, %ds
movw $INITSEG, %ax
movw %ax, %es
movw $256, %cx
subw %si, %si
subw %di, %di
cld
rep
movsw
ljmp $INITSEG, $go - _boot
go:
movw %cs, %ax
movw $(0x4000-12), %dx # 0x4000 is arbitrary value >= length of
# bootsect + length of setup + room for stack
# 12 is disk parm size
# bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde). We
# wouldn't have to worry about this if we checked the top of memory. Also
# my BIOS can be configured to put the wini drive tables in high memory
# instead of in the vector table. The old stack might have clobbered the
# drive table.
movw %ax, %ds
movw %ax, %es
movw %ax, %ss # put stack at INITSEG:0x4000-12.
movw %dx, %sp
/*
* Many BIOS's default disk parameter tables will not
* recognize multi-sector reads beyond the maximum sector number
* specified in the default diskette parameter tables - this may
* mean 7 sectors in some cases.
*
* Since single sector reads are slow and out of the question,
* we must take care of this by creating new parameter tables
* (for the first disk) in RAM. We will set the maximum sector
* count to 18 - the most we will encounter on an HD 1.44.
*
* High doesn't hurt. Low does.
*
* Segments are as follows: ds=es=ss=cs - INITSEG,
* fs = 0, gs = parameter table segment
*/
pushw $0
popw %fs
movw $0x78, %bx # fs:bx is parameter table address
lgs %fs:(%bx),%si # gs:si is source
movw %dx, %di # es:di is destination
movw $6, %cx # copy 12 bytes
cld
rep movsw %gs:(%si), (%di)
movw %dx, %di
movb $18, 4(%di) # patch sector count
movw %di, %fs:(%bx)
movw %es, %fs:2(%bx)
movw %cs, %ax
movw %ax, %fs
movw %ax, %gs
xorb %ah, %ah # reset FDC
xorb %dl, %dl
int $0x13
# load the setup-sectors directly after the bootblock.
# Note that 'es' is already set up.
load_setup:
xorw %dx, %dx # drive 0, head 0
movw $0x0002, %cx # sector 2, track 0
movw $0x0200, %bx # address = 512, in INITSEG
movw $(0x0200 + SETUPSECS), %ax # service 2, nr of sectors
# (assume all on head 0, track 0)
int $0x13 # read it
jnc ok_load_setup # ok - continue
pushw %ax # dump error code
call print_nl
movw %sp, %bp
call print_hex
popw %ax
xorb %dl, %dl # reset FDC
xorb %ah, %ah
int $0x13
jmp load_setup
ok_load_setup:
# Get disk drive parameters, specifically nr of sectors/track
/* It seems that there is no BIOS call to get the number of sectors. Guess
* 18 sectors if sector 18 can be read, 15 if sector 15 can be read.
* Otherwise guess 9
*/
xorw %dx, %dx # drive 0, head 0
movw $0x0012, %cx # sector 18, track 0
movw $(0x200+(SETUPSECS*0x200)), %bx # address after setup (es = cs)
movw $0x0201, %ax # service 2, 1 sector
int $0x13
jnc got_sectors
movb $0x0f, %cl # sector 15
movw $0x0201, %ax # service 2, 1 sector
int $0x13
jnc got_sectors
movb $0x09, %cl
got_sectors:
movw %cx, %cs:sectors - _boot
movw $INITSEG, %ax
movw %ax, %es
# Print some inane message
movb $0x03, %ah # read cursor pos
xorb %bh, %bh
int $0x10
movw $9, %cx
movw $0x0007, %bx # page 0, attribute 7 (normal)
movw $msg1 - _boot, %bp
movw $0x1301, %ax # write string, move cursor
int $0x10
# ok, we've written the message, now
# we want to load the system (at 0x10000)
movw $TSTLOAD, %ax
movw %ax, %es # segment of 0x010000
call read_it
call kill_motor
call turnoffcursor
call print_nl
# after that (everyting loaded), we jump to
# the setup-routine loaded directly after
# the bootblock:
ljmp $SETUPSEG,$0
# This routine loads the system at address 0x10000, making sure
# no 64kB boundaries are crossed. We try to load it as fast as
# possible, loading whole tracks whenever we can.
#
# in: es - starting address segment (normally 0x1000)
#
sread: .word 1+SETUPSECS # sectors read of current track
head: .word 0 # current head
track: .word 0 # current track
read_it:
movw %es, %ax
testw $0x0fff, %ax
die:
jne die # es must be at 64kB boundary
xorw %bx,%bx # bx is starting address within segment
rp_read:
movw %es, %ax
subw $TSTLOAD, %ax # have we loaded all yet?
cmpw syssize - _boot, %ax
jbe ok1_read
ret
ok1_read:
movw %cs:sectors - _boot, %ax
subw sread - _boot, %ax
movw %ax, %cx
shlw $9, %cx
addw %bx, %cx
jnc ok2_read
je ok2_read
xorw %ax, %ax
subw %bx, %ax
shrw $9, %ax
ok2_read:
call read_track
movw %ax, %cx
add sread - _boot, %ax
cmpw %cs:sectors - _boot, %ax
jne ok3_read
movw $1, %ax
subw head - _boot, %ax
jne ok4_read
incw track - _boot
ok4_read:
movw %ax, head - _boot
xorw %ax, %ax
ok3_read:
movw %ax, sread - _boot
shlw $9, %cx
addw %cx, %bx
jnc rp_read
movw %es, %ax
addb $0x10, %ah
movw %ax, %es
xorw %bx, %bx
jmp rp_read
read_track:
pusha
pusha
movw $0xe2e, %ax # loading... message 2e = .
movw $7, %bx
int $0x10
popa
movw track - _boot, %dx
movw sread - _boot, %cx
incw %cx
movb %dl, %ch
movw head - _boot, %dx
movb %dl, %dh
andw $0x0100, %dx
movb $2, %ah
pushw %dx # save for error dump
pushw %cx
pushw %bx
pushw %ax
int $0x13
jc bad_rt
addw $8, %sp
popa
ret
bad_rt:
pushw %ax # save error code
call print_all # ah = error, al = read
xorb %ah, %ah
xorb %dl, %dl
int $0x13
addw $10, %sp
popa
jmp read_track
/*
* print_all is for debugging purposes.
* It will print out all of the registers. The assumption is that this is
* called from a routine, with a stack frame like
* dx
* cx
* bx
* ax
* error
* ret <- sp
*
*/
print_all:
movw $5, %cx # error code + 4 registers
movw %sp, %bp
print_loop:
pushw %cx # save count left
call print_nl # nl for readability
cmpb 5, %cl # see if register name is needed
jae no_reg
movw $(0xe05 + 'A' - 1), %ax
subb %cl, %al
int $0x10
movb $'X', %al
int $0x10
movb $':', %al
int $0x10
no_reg:
addw $2, %bp # next register
call print_hex # print it
popw %cx
loop print_loop
ret
print_nl:
movw $0xe0d, %ax # CR
int $0x10
movb $0x0a, %al # LF
int $0x10
ret
/*
* print_hex is for debugging purposes, and prints the word
* pointed to by ss:bp in hexadecmial.
*/
print_hex:
movw $4, %cx # 4 hex digits
movw (%bp), %dx # load word into dx
print_digit:
rolw $4, %dx # rotate so that lowest 4 bits are used
movb $0xe, %ah
movb %dl, %al # mask off so we have only next nibble
andb $0xf, %al
addb $'0', %al # convert to 0-based digit
cmpb $'9', %al # check for overflow
jbe good_digit
addb $('A' - '0' - 10), %al
good_digit:
int $0x10
loop print_digit
ret
/*
* This procedure turns off the floppy drive motor, so
* that we enter the kernel in a known state, and
* don't have to worry about it later.
*/
kill_motor:
pushw %dx
movw $0x3f2, %dx
xorb %al, %al
outb %al, %dx
popw %dx
ret
turnoffcursor:
movb $0x01, %ah # turn off the cursor
movb $0x00, %bh
movw $0x2000, %cx
int $0x10
ret
sectors:
.word 0
msg1:
.byte 13,10
.ascii "Loading"
.org 497
setup_sects:
.byte SETUPSECS
.org 500
syssize:
.word _syssize
.org 508
root_dev:
.word ROOT_DEV
boot_flag:
.word 0xAA55
_eboot: