bootsect.S
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:10k
- /*
- * bootsect.S Copyright (C) 1991, 1992 Linus Torvalds
- *
- * modified by Drew Eckhardt
- * modified by Bruce Evans (bde)
- * modified by Chris Noe (May 1999) (as86 -> gas)
- *
- * 360k/720k disk support: Andrzej Krzysztofowicz <ankry@green.mif.pg.gda.pl>
- *
- * BIG FAT NOTE: We're in real mode using 64k segments. Therefore segment
- * addresses must be multiplied by 16 to obtain their respective linear
- * addresses. To avoid confusion, linear addresses are written using leading
- * hex while segment addresses are written as segment:offset.
- *
- * bde - should not jump blindly, there may be systems with only 512K low
- * memory. Use int 0x12 to get the top of memory, etc.
- *
- * It then loads 'setup' directly after itself (0x90200), and the system
- * at 0x10000, using BIOS interrupts.
- *
- * NOTE! currently system is at most (8*65536-4096) bytes long. This should
- * be no problem, even in the future. I want to keep it simple. This 508 kB
- * kernel size should be enough, especially as this doesn't contain the
- * buffer cache as in minix (and especially now that the kernel is
- * compressed :-)
- *
- * The loader has been made as simple as possible, and continuous
- * read errors will result in a unbreakable loop. Reboot by hand. It
- * loads pretty fast by getting whole tracks at a time whenever possible.
- */
- #include <asm/boot.h>
- SETUPSECTS = 4 /* default nr of setup-sectors */
- BOOTSEG = 0x07C0 /* original address of boot-sector */
- INITSEG = DEF_INITSEG /* we move boot here - out of the way */
- SETUPSEG = DEF_SETUPSEG /* setup starts here */
- SYSSEG = DEF_SYSSEG /* system loaded at 0x10000 (65536) */
- SYSSIZE = DEF_SYSSIZE /* system size: # of 16-byte clicks */
- /* to be loaded */
- ROOT_DEV = 0 /* ROOT_DEV is now written by "build" */
- SWAP_DEV = 0 /* SWAP_DEV is now written by "build" */
- #ifndef SVGA_MODE
- #define SVGA_MODE ASK_VGA
- #endif
- #ifndef RAMDISK
- #define RAMDISK 0
- #endif
- #ifndef ROOT_RDONLY
- #define ROOT_RDONLY 1
- #endif
- .code16
- .text
- .global _start
- _start:
- # First things first. Move ourself from 0x7C00 -> 0x90000 and jump there.
- movw $BOOTSEG, %ax
- movw %ax, %ds # %ds = BOOTSEG
- movw $INITSEG, %ax
- movw %ax, %es # %ax = %es = INITSEG
- movw $256, %cx
- subw %si, %si
- subw %di, %di
- cld
- rep
- movsw
- ljmp $INITSEG, $go
- # 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.
- go: movw $0x4000-12, %di # 0x4000 is an arbitrary value >=
- # length of bootsect + length of
- # setup + room for stack;
- # 12 is disk parm size.
- movw %ax, %ds # %ax and %es already contain INITSEG
- movw %ax, %ss
- movw %di, %sp # put stack at INITSEG:0x4000-12.
- # 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 36 - the most we will encounter on an ED 2.88.
- #
- # High doesn't hurt. Low does.
- #
- # Segments are as follows: %cs = %ds = %es = %ss = INITSEG, %fs = 0,
- # and %gs is unused.
- movw %cx, %fs # %fs = 0
- movw $0x78, %bx # %fs:%bx is parameter table address
- pushw %ds
- ldsw %fs:(%bx), %si # %ds:%si is source
- movb $6, %cl # copy 12 bytes
- pushw %di # %di = 0x4000-12.
- rep # don't worry about cld
- movsw # already done above
- popw %di
- popw %ds
- movb $36, 0x4(%di) # patch sector count
- movw %di, %fs:(%bx)
- movw %es, %fs:2(%bx)
- # Get disk drive parameters, specifically number of sectors/track.
- # It seems that there is no BIOS call to get the number of sectors.
- # Guess 36 sectors if sector 36 can be read, 18 sectors if sector 18
- # can be read, 15 if sector 15 can be read. Otherwise guess 9.
- # Note that %cx = 0 from rep movsw above.
- movw $disksizes, %si # table of sizes to try
- probe_loop:
- lodsb
- cbtw # extend to word
- movw %ax, sectors
- cmpw $disksizes+4, %si
- jae got_sectors # If all else fails, try 9
- xchgw %cx, %ax # %cx = track and sector
- xorw %dx, %dx # drive 0, head 0
- movw $0x0200, %bx # address = 512, in INITSEG (%es = %cs)
- movw $0x0201, %ax # service 2, 1 sector
- int $0x13
- jc probe_loop # try next value
- got_sectors:
- movb $0x03, %ah # read cursor pos
- xorb %bh, %bh
- int $0x10
- movw $9, %cx
- movb $0x07, %bl # page 0, attribute 7 (normal)
- # %bh is set above; int10 doesn't
- # modify it
- movw $msg1, %bp
- movw $0x1301, %ax # write string, move cursor
- int $0x10 # tell the user we're loading..
- # Load the setup-sectors directly after the moved bootblock (at 0x90200).
- # We should know the drive geometry to do it, as setup may exceed first
- # cylinder (for 9-sector 360K and 720K floppies).
- movw $0x0001, %ax # set sread (sector-to-read) to 1 as
- movw $sread, %si # the boot sector has already been read
- movw %ax, (%si)
- xorw %ax, %ax # reset FDC
- xorb %dl, %dl
- int $0x13
- movw $0x0200, %bx # address = 512, in INITSEG
- next_step:
- movb setup_sects, %al
- movw sectors, %cx
- subw (%si), %cx # (%si) = sread
- cmpb %cl, %al
- jbe no_cyl_crossing
- movw sectors, %ax
- subw (%si), %ax # (%si) = sread
- no_cyl_crossing:
- call read_track
- pushw %ax # save it
- call set_next # set %bx properly; it uses %ax,%cx,%dx
- popw %ax # restore
- subb %al, setup_sects # rest - for next step
- jnz next_step
- pushw $SYSSEG
- popw %es # %es = SYSSEG
- call read_it
- call kill_motor
- call print_nl
- # After that we check which root-device to use. If the device is
- # defined (!= 0), nothing is done and the given device is used.
- # Otherwise, one of /dev/fd0H2880 (2,32) or /dev/PS0 (2,28) or /dev/at0 (2,8)
- # depending on the number of sectors we pretend to know we have.
- # Segments are as follows: %cs = %ds = %ss = INITSEG,
- # %es = SYSSEG, %fs = 0, %gs is unused.
- movw root_dev, %ax
- orw %ax, %ax
- jne root_defined
- movw sectors, %bx
- movw $0x0208, %ax # /dev/ps0 - 1.2Mb
- cmpw $15, %bx
- je root_defined
- movb $0x1c, %al # /dev/PS0 - 1.44Mb
- cmpw $18, %bx
- je root_defined
- movb $0x20, %al # /dev/fd0H2880 - 2.88Mb
- cmpw $36, %bx
- je root_defined
- movb $0, %al # /dev/fd0 - autodetect
- root_defined:
- movw %ax, root_dev
- # After that (everything loaded), we jump to the setup-routine
- # loaded directly after the bootblock:
- ljmp $SETUPSEG, $0
- # These variables are addressed via %si register as it gives shorter code.
- sread: .word 0 # sectors read of current track
- head: .word 0 # current head
- track: .word 0 # current track
- # This routine loads the system at address SYSSEG, making sure
- # no 64kB boundaries are crossed. We try to load it as fast as
- # possible, loading whole tracks whenever we can.
- read_it:
- movw %es, %ax # %es = SYSSEG when called
- testw $0x0fff, %ax
- die: jne die # %es must be at 64kB boundary
- xorw %bx, %bx # %bx is starting address within segment
- rp_read:
- #ifdef __BIG_KERNEL__ # look in setup.S for bootsect_kludge
- bootsect_kludge = 0x220 # 0x200 + 0x20 which is the size of the
- lcall bootsect_kludge # bootsector + bootsect_kludge offset
- #else
- movw %es, %ax
- subw $SYSSEG, %ax
- movw %bx, %cx
- shr $4, %cx
- add %cx, %ax # check offset
- #endif
- cmpw syssize, %ax # have we loaded everything yet?
- jbe ok1_read
- ret
- ok1_read:
- movw sectors, %ax
- subw (%si), %ax # (%si) = sread
- 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
- call set_next
- jmp rp_read
- read_track:
- pusha
- pusha
- movw $0xe2e, %ax # loading... message 2e = .
- movw $7, %bx
- int $0x10
- popa
- # Accessing head, track, sread via %si gives shorter code.
- movw 4(%si), %dx # 4(%si) = track
- movw (%si), %cx # (%si) = sread
- incw %cx
- movb %dl, %ch
- movw 2(%si), %dx # 2(%si) = head
- 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
- set_next:
- movw %ax, %cx
- addw (%si), %ax # (%si) = sread
- cmp sectors, %ax
- jne ok3_set
- movw $0x0001, %ax
- xorw %ax, 2(%si) # change head
- jne ok4_set
- incw 4(%si) # next track
- ok4_set:
- xorw %ax, %ax
- ok3_set:
- movw %ax, (%si) # set sread
- shlw $9, %cx
- addw %cx, %bx
- jnc set_next_fin
- movw %es, %ax
- addb $0x10, %ah
- movw %ax, %es
- xorw %bx, %bx
- set_next_fin:
- 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 remaining
- call print_nl # <-- for readability
- cmpb $5, %cl
- jae no_reg # see if register name is needed
-
- 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 $0xa, %al # LF
- int $0x10
- ret
- # print_hex is for debugging purposes, and prints the word
- # pointed to by %ss:%bp in hexadecimal.
- print_hex:
- movw $4, %cx # 4 hex digits
- movw (%bp), %dx # load word into %dx
- print_digit:
- rolw $4, %dx # rotate to use low 4 bits
- movw $0xe0f, %ax # %ah = request
- andb %dl, %al # %al = mask for nybble
- addb $0x90, %al # convert %al to ascii hex
- daa # in only four instructions!
- adc $0x40, %al
- daa
- 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.
- # NOTE: Doesn't save %ax or %dx; do it yourself if you need to.
- kill_motor:
- #if 1
- xorw %ax, %ax # reset FDC
- xorb %dl, %dl
- int $0x13
- #else
- movw $0x3f2, %dx
- xorb %al, %al
- outb %al, %dx
- #endif
- ret
- sectors: .word 0
- disksizes: .byte 36, 18, 15, 9
- msg1: .byte 13, 10
- .ascii "Loading"
- # XXX: This is a fairly snug fit.
- .org 497
- setup_sects: .byte SETUPSECTS
- root_flags: .word ROOT_RDONLY
- syssize: .word SYSSIZE
- swap_dev: .word SWAP_DEV
- ram_size: .word RAMDISK
- vid_mode: .word SVGA_MODE
- root_dev: .word ROOT_DEV
- boot_flag: .word 0xAA55