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