root/stand/i386/mbr/mbr.S
#
# Copyright (c) 1999 Robert Nordier
# All rights reserved.
#
# Redistribution and use in source and binary forms are freely
# permitted provided that the above copyright notice and this
# paragraph and the following disclaimer are duplicated in all
# such forms.
#
# This software is provided "AS IS" and without any express or
# implied warranties, including, without limitation, the implied
# warranties of merchantability and fitness for a particular
# purpose.
#

# A 512 byte MBR boot manager that simply boots the active partition.

                .set LOAD,0x7c00                # Load address
                .set EXEC,0x600                 # Execution address
                .set PT_OFF,0x1be               # Partition table
                .set MAGIC,0xaa55               # Magic: bootable
                .set FL_PACKET,0x80             # Flag: try EDD

                .set NHRDRV,0x475               # Number of hard drives

                .globl start                    # Entry point
                .code16

#
# Setup the segment registers for flat addressing and setup the stack.
#
start:          cld                             # String ops inc
                xorw %ax,%ax                    # Zero
                movw %ax,%es                    # Address
                movw %ax,%ds                    #  data
                movw %ax,%ss                    # Set up
                movw $LOAD,%sp                  #  stack
#
# Relocate ourself to a lower address so that we are out of the way when
# we load in the bootstrap from the partition to boot.
# 
                movw $main-EXEC+LOAD,%si        # Source
                movw $main,%di                  # Destination
                movw $0x200-(main-start),%cx    # Byte count
                rep                             # Relocate
                movsb                           #  code
#
# Jump to the relocated code.
#
                jmp main-LOAD+EXEC              # To relocated code
#
# Scan the partition table looking for an active entry.  Note that %ch is
# zero from the repeated string instruction above.  We save the offset of
# the active partition in %si and scan the entire table to ensure that only
# one partition is marked active.
#
main:           xorw %si,%si                    # No active partition
                movw $partbl,%bx                # Partition table
                movb $0x4,%cl                   # Number of entries
main.1:         cmpb %ch,(%bx)                  # Null entry?
                je main.2                       # Yes
                jg err_pt                       # If 0x1..0x7f
                testw %si,%si                   # Active already found?
                jnz err_pt                      # Yes
                movw %bx,%si                    # Point to active
main.2:         addb $0x10,%bl                  # Till
                loop main.1                     #  done
                testw %si,%si                   # Active found?
                jnz main.3                      # Yes
                int $0x18                       # BIOS: Diskless boot
#
# Ok, we've found a possible active partition.  Check to see that the drive
# is a valid hard drive number.
#
main.3:         cmpb $0x80,%dl                  # Drive valid?
                jb main.4                       # No
                movb NHRDRV,%dh                 # Calculate the highest
                addb $0x80,%dh                  #  drive number available
                cmpb %dh,%dl                    # Within range?
                jb main.5                       # Yes
main.4:         movb (%si),%dl                  # Load drive
#
# Ok, now that we have a valid drive and partition entry, load the CHS from
# the partition entry and read the sector from the disk.
#
main.5:         movw %sp,%di                    # Save stack pointer
                movb 0x1(%si),%dh               # Load head
                movw 0x2(%si),%cx               # Load cylinder:sector
                movw $LOAD,%bx                  # Transfer buffer
                testb $FL_PACKET,flags          # Try EDD?
                jz main.7                       # No.
                pushw %cx                       # Save %cx
                pushw %bx                       # Save %bx
                movw $0x55aa,%bx                # Magic
                movb $0x41,%ah                  # BIOS: EDD extensions
                int $0x13                       #  present?
                jc main.6                       # No.
                cmpw $0xaa55,%bx                # Magic ok?
                jne main.6                      # No.
                testb $0x1,%cl                  # Packet mode present?
                jz main.6                       # No.
                popw %bx                        # Restore %bx
                pushl $0x0                      # Set the LBA
                pushl 0x8(%si)                  #  address
                pushw %es                       # Set the address of
                pushw %bx                       #  the transfer buffer
                pushw $0x1                      # Read 1 sector
                pushw $0x10                     # Packet length
                movw %sp,%si                    # Packer pointer
                movw $0x4200,%ax                # BIOS: LBA Read from disk
                jmp main.8                      # Skip the CHS setup
main.6:         popw %bx                        # Restore %bx
                popw %cx                        # Restore %cx
main.7:         movw $0x201,%ax                 # BIOS: Read from disk
main.8:         int $0x13                       # Call the BIOS
                movw %di,%sp                    # Restore stack
                jc err_rd                       # If error
#
# Now that we've loaded the bootstrap, check for the 0xaa55 signature.  If it
# is present, execute the bootstrap we just loaded.
#
                cmpw $MAGIC,0x1fe(%bx)          # Bootable?
                jne err_os                      # No
                jmp *%bx                        # Invoke bootstrap
#
# Various error message entry points.
#
err_pt:         movw $msg_pt,%si                # "Invalid partition
                jmp putstr                      #  table"

err_rd:         movw $msg_rd,%si                # "Error loading
                jmp putstr                      #  operating system"

err_os:         movw $msg_os,%si                # "Missing operating
                jmp putstr                      #  system"
#
# Output an ASCIZ string to the console via the BIOS.
# 
putstr.0:       movw $0x7,%bx                   # Page:attribute
                movb $0xe,%ah                   # BIOS: Display
                int $0x10                       #  character
putstr:         lodsb                           # Get character
                testb %al,%al                   # End of string?
                jnz putstr.0                    # No
putstr.1:       jmp putstr.1                    # Await reset

msg_pt:         .asciz "Invalid partition table"
msg_rd:         .asciz "Error loading operating system"
msg_os:         .asciz "Missing operating system"

                .org PT_OFF-1,0x90
flags:          .byte FLAGS                     # Flags

partbl:         .fill 0x10,0x4,0x0              # Partition table
                .word MAGIC                     # Magic number