/* * bootflop.S Copyright (C) 1991, 1992 Linus Torvalds * * derived from linux/arch/i386/boot/bootsect.S * by Randy Dunlap * so that the boot floppy only includes first-stage bootloader, * then loads the next loader from a hard drive (like /dev/hdb in * a case where /dev/hda contains NO Linux or Linux bootloader code) */ /* Don't touch these, unless you really know what you're doing. */ #define DEF_INITSEG 0x9000 #define DEF_SETUPSEG 0x9020 #define DEF_SYSSEG 0x1000 /////#define DEF_SYSSIZE 0x7F00 #define DEF_SYSSIZE 32 /* 512 bytes / 16-byte clicks = 32 */ /////SETUPSECTS = 4 /* default nr of setup-sectors */ BOOTSECTS = 1 /* default nr of Boot sectors */ BOOTSEG = 0x07C0 /* original address of boot-sector */ INITSEG = DEF_INITSEG /* we move boot here - out of the way */ SYSSEG = DEF_SYSSEG /* system loaded at 0x10000 (65536) */ SETUPSEG = DEF_SETUPSEG /* setup starts here */ SYSSIZE = DEF_SYSSIZE /* system size: # of 16-byte clicks */ /* to be loaded */ ROOT_DEV = 0x81 /* ROOT_DEV is now written by "build" */ /* default value is /dev/hdb */ /* TBD: check build program */ .code16 .text .global _start _start: # First things first. Move ourself from 0x7C00 -> 0x90000 and jump there. # Then read next boot sector into 0x7c00 (TBD: leave as is since it works). 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 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. # Segments are as follows: %cs = %ds = %es = %ss = INITSEG, %fs = 0, # and %gs is unused. movw %cx, %fs # %fs = 0 # Make sure that root_dev is non-zero. # After that we check which root-device to use. If the device is # defined (!= 0), nothing is done and the given device is used. # (rdd) Otherwise default to /dev/hdb (MBR, on BIOS drive 0x81). movw root_dev, %ax orw %ax, %ax jne root_defined movw $0x81, %ax # set default root_defined: movw %ax, root_dev # reset root_drive xorw %ax, %ax # BIOS format, like 0x81 movw root_dev, %dx int $0x13 # ignore any error movw $63, %ax movw %ax, 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 $loading_msg, %bp movw $0x1301, %ax # write string, move cursor int $0x10 # tell the user we're loading.. # Load the target boot [setup-] sectors directly after the # moved bootblock (at 0x90200). xorw %ax, %ax # set sread (sectors-read) to 0 => # read sector # 1 next (inc. before read) movw $sread, %si movw %ax, (%si) ##### movw $0x0200, %bx # address = 512, in INITSEG movw $0x0, %bx # address = 0, in INITSEG next_step: movb boot_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, boot_sects # rest - for next step jnz next_step pushw $SYSSEG popw %es # %es = SYSSEG call kill_motor call print_nl # Segments are as follows: %cs = %ds = %ss = INITSEG, # %es = SYSSEG, %fs = 0, %gs is unused. # 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 read_track: pusha pusha movw $0xe23, %ax # "Loading###" message 0x23 = '#' movw $7, %bx int $0x10 popa # Accessing head, track, sread via %si gives shorter code. pushw %bx movw 4(%si), %dx # 4(%si) = track movw (%si), %cx # (%si) = sread incw %cx movb %dl, %ch # %ch = cyl/track, %cl = sectnum movw root_dev, %dx movw 2(%si), %bx # 2(%si) = head movb %bl, %dh # %dh = head, %dl = drive popw %bx movb $2, %ah # read sectors (%al = numsectors) 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: xorw %ax, %ax # reset FDC xorb %dl, %dl int $0x13 ret sectors: .word 0 loading_msg: .byte 13, 10 .ascii "Loading" # WARNING: This is a fairly snug fit. .org 497 boot_sects: .byte BOOTSECTS root_flags: .word 0 syssize: .word SYSSIZE swap_dev: .word 0 ram_size: .word 0 vid_mode: .word 0 root_dev: .word ROOT_DEV boot_flag: .word 0xAA55 ##### end bootflop.S