/* * 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 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 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: