This tutorial is meant for public information and may be copied and distributed so long as it remains whole and intact. Please give credit where credit is due. Take caution. Tampering with bootsectors may render disks unusable. It is advisable to experiment using floppy disks rather than hard drives until the code is sound.
The bootstrap is a short program loaded by the BIOS upon system startup. The BIOS has no information about the environment required by the operating system and therefore can do nothing to initialize the system beyond putting the hardware into a known state. This is where the bootstrap program comes into play. The BIOS loads the bootstrap from a known location and transfers control. Operating system specific bootstraps either load the operating system itself or load a more advanced initialization program. It is the bootstrap?s responsibility to load an appropriate operating environment.
A bootstrap is loaded from the first sector on a disk, track zero, head zero, sector one to be specific. Which disk the bootstrap is loaded from is dependent upon the BIOS configuration. This single 512 byte sector is loaded into memory at physical address 0000:7C00. The BIOS will then examine the final two bytes of the bootstrap (offset 1FEh) for the value AA55h. This indicates the bootstrap is valid. A bootstrap must be exactly 512 bytes long because of the two byte check and the one sector limitation. After this verification, the BIOS will jump to 0000:7C00 and turn control over to the bootstrap.
The simplest bootstrap can be written as follows:
;************************************** [BITS 16] ORG 0 INT 0x18 TIMES 510-($-$$) DB 0 DW 0xAA55 ;**************************************
Henceforth, all code examples will be referred to as bootstrap.asm. This example can be compiled using NASM, the Netwide Assembler, available on-linAe from http://www.cyrogen.com/nasmfor free. The code must be compiled into plain binary:
NASM -O BOOTSTRAP.BIN BOOTSTRAP.ASM
The quickest way to put the binary file onto the disk is to use DEBUG.EXE. The following example demonstrates using debug to write at memory offset 100h one sector starting at sector zero on disk zero.
C:\DEBUG.EXE BOOTSTRAP.BIN -W 100 0 0 1 -Q C:\
The command "w" tells debug to write to the disk. It will begin by copying bytes from memory to the designated disk and sector. The final parameter indicates how many sectors to write. The "l" command uses the same parameters but reads from the disk. Together, these can be used to write new bootsectors or examine existing ones. Type "?" to get a listing of more commands. Look at http://www.strangecreations.com/strange/library/assembly/debug.txtfor more information on using this program.
Under the various versions of UNIX, the DD command can be used to copy the image onto the bootsector. Type man dd for detailed usage information. (Thanks to Samuel Bronson for the UNIX inclusion)
dd if=BOOTSTRAP.BIN of=/dev/fd0
Bootstrap programs are put to a variety of uses. They are capable of initializing certain pieces of hardware, putting the processor into advanced operating modes, or performing a dedicated processing task. Usually, however, bootstrap programs are used to load a larger file into memory with more functionality than can be placed into a 512 byte block. There are two separate techniques for making this happen. The first assumes that the file to be loaded is located immediately after the bootsector on the disk. The bootstrap only needs to know how many sectors to load and can immediately load the appropriate sectors into memory and transfer control to the loaded file. This, howeveAr, typically destroys existing file systems on the disk. Although this makes coding the bootsectoor easier, it is more difficult to put the secondary file onto the disk.
The more advanced solution requires a bootstrap program of greater complexity. A common solution is to write the bootstrap to be compliant with an existing file system. Doing so allows the secondary file to be copied or edited directly on the disk. A bootstrap must therefore be able to browse the file systems to both determine the presence of and the location of the secondary file.
The FAT12 file system is commonly used on floppy disks. The source code example below demonstrates how to read the root directory to search for the file, how to traverse the FAT to load the file into memory, and how to begin executing the loaded code.
;************************************************************************* [BITS 16] ORG 0 jmp START OEM_ID db "QUASI-OS" BytesPerSector dw 0x0200 SectorsPerCluster db 0x01 ReservedSectors dw 0x0001 TotalFATs db 0x02 MaxRootEntries dw 0x00E0 TotalSectorsSmall dw 0x0B40 MediaDescriptor db 0xF0 SectorsPerFAT dw 0x0009 SectorsPerTrack dw 0x0012 NumHeads dw 0x0002 HiddenSectors dd 0x00000000 TotalSectorsLarge dd 0x00000000 DriveNumber db 0x00 Flags db 0x00 Signature db 0x29 VolumeID dd 0xFFFFFFFF VolumeLabel db "QUASI BOOT" SystemID db "FAT12 " START: ; code located at 0000:7C00, adjust segment registers cli mov ax, 0x07C0 mov ds, ax mov es, ax mov fs, ax mov gs, ax ; create stack mov ax, 0x0000 mov ss, ax mov A sp, 0xFFFF sti ; post message mov si, msgLoading call DisplayMessage LOAD_ROOT: ; compute size of root directory and store in ?cx? xor cx, cx xor dx, dx mov ax, 0x0020 ; 32 byte directory entry mul WORD [MaxRootEntries] ; total size of directory div WORD [BytesPerSector] ; sectors used by directory xchg ax, cx ; compute location of root directory and store in ?ax? mov al, BYTE [TotalFATs] ; number of FATs mul WORD [SectorsPerFAT] ; sectors used by FATs add ax, WORD [ReservedSectors] ; adjust for bootsector mov WORD [datasector], ax ; base of root directory add WORD [datasector], cx ; read root directory into memory (7C00:0200) mov bx, 0x0200 ; copy root dir above bootcode call ReadSectors ; browse root directory for binary image mov cx, WORD [MaxRootEntries] ; load loop counter mov di, 0x0200 ; locate first root entry .LOOP: push cx mov cx, 0x000B ; eleven character name mov si, ImageName ; image name to find push di rep cmpsb ; test for entry match pop di je LOAD_FAT pop cx add di, 0x0020 ; queue next directory entry loop .LOOP jmp FAILURE LOAD_FAT: ; save starting cluster of boot image mov si, msgCRLF call DisplayMessage mov dx, WORD [di + 0x001A] mov WORD [cluster], dx ; file?s first cluster A; compute size of FAT and store in ?cx? xor ax, ax mov al, BYTE [TotalFATs] ; number of FATs mul WORD [SectorsPerFAT] ; sectors used by FATs mov cx, ax ; compute location of FAT and store in ?ax? mov ax, WORD [ReservedSectors] ; adjust for bootsector ; read FAT into memory (7C00:0200) mov bx, 0x0200 ; copy FAT above bootcode call ReadSectors ; read image file into memory (0050:0000) mov si, msgCRLF call DisplayMessage mov ax, 0x0050 mov es, ax ; destination for image mov bx, 0x0000 ; destination for image push bx LOAD_IMAGE: mov ax, WORD [cluster] ; cluster to read pop bx ; buffer to read into call ClusterLBA ; convert cluster to LBA xor cx, cx mov cl, BYTE [SectorsPerCluster] ; sectors to read call ReadSectors push bx ; compute next cluster mov ax, WORD [cluster] ; identify current cluster mov cx, ax ; copy current cluster mov dx, ax ; copy current cluster shr dx, 0x0001 ; divide by two add cx, dx ; sum for (3/2) mov bx, 0x0200 ; location of FAT in memory add bx, cx ; index into FAT mov dx, WORD [bx] ; read two bytes from FAT test ax, 0x0001 jnz .ODD_CLUSTER .EVEN_CLUSTER: and dx, 0000111111111111b ; take low tAwelve bits jmp .DONE .ODD_CLUSTER: shr dx, 0x0004 ; take high twelve bits .DONE: mov WORD [cluster], dx ; store new cluster cmp dx, 0x0FF0 ; test for end of file jb LOAD_IMAGE DONE: mov si, msgCRLF call DisplayMessage push WORD 0x0050 push WORD 0x0000 retf FAILURE: mov si, msgFailure call DisplayMessage mov ah, 0x00 int 0x16 ; await keypress int 0x19 ; warm boot computer ;************************************************************************* ; PROCEDURE DisplayMessage ; display ASCIIZ string at ds:si via BIOS ;************************************************************************* DisplayMessage: lodsb ; load next character or al, al ; test for NUL character jz .DONE mov ah, 0x0E ; BIOS teletype mov bh, 0x00 ; display page 0 mov bl, 0x07 ; text attribute int 0x10 ; invoke BIOS jmp DisplayMessage .DONE: ret ;************************************************************************* ; PROCEDURE ReadSectors ; reads ?cx? sectors from disk starting at ?ax? into memory location ?es:bx? ;************************************************************************* ReadSectors: .MAIN mov di, 0x0005 ; five retries for error .SECTORLOOP push ax push bx push cx call LBAACHS mov ah, 0x02 ; BIOS read sector mov al, 0x01 ; read one sector mov ch, BYTE [absoluteTrack] ; track mov cl, BYTE [absoluteSector] ; sector mov dh, BYTE [absoluteHead] ; head mov dl, BYTE [DriveNumber] ; drive int 0x13 ; invoke BIOS jnc .SUCCESS ; test for read error xor ax, ax ; BIOS reset disk int 0x13 ; invoke BIOS dec di ; decrement error counter pop cx pop bx pop ax jnz .SECTORLOOP ; attempt to read again int 0x18 .SUCCESS mov si, msgProgress call DisplayMessage pop cx pop bx pop ax add bx, WORD [BytesPerSector] ; queue next buffer inc ax ; queue next sector loop .MAIN ; read next sector ret ;************************************************************************* ; PROCEDURE ClusterLBA ; convert FAT cluster into LBA addressing scheme ; LBA = (cluster - 2) * sectors per cluster ;************************************************************************* ClusterLBA: sub ax, 0x0002 ; zero base cluster number xor cx, cx mov cl, BYTE [SectorsPerCluster] ; convert byte to word mul cx add ax, WORD [datasector] ; base data sector ret ;************************************************************************* ; PROACEDURE LBACHS ; convert ?ax? LBA addressing scheme to CHS addressing scheme ; absolute sector = (logical sector / sectors per track) + 1 ; absolute head = (logical sector / sectors per track) MOD number of heads ; absolute track = logical sector / (sectors per track * number of heads) ;************************************************************************* LBACHS: xor dx, dx ; prepare dx:ax for operation div WORD [SectorsPerTrack] ; calculate inc dl ; adjust for sector 0 mov BYTE [absoluteSector], dl xor dx, dx ; prepare dx:ax for operation div WORD [NumHeads] ; calculate mov BYTE [absoluteHead], dl mov BYTE [absoluteTrack], al ret absoluteSector db 0x00 absoluteHead db 0x00 absoluteTrack db 0x00 datasector dw 0x0000 cluster dw 0x0000 ImageName db "LOADER BIN" msgLoading db 0x0D, 0x0A, "Loading Boot Image ", 0x0D, 0x0A, 0x00 msgCRLF db 0x0D, 0x0A, 0x00 msgProgress db ".", 0x00 msgFailure db 0x0D, 0x0A, "ERROR : Press Any Key to Reboot", 0x00 TIMES 510-($-$$) DB 0 DW 0xAA55 ;*************************************************************************
Bass Demon. "Operating System Resources." [on-line] (accessed 3 JUN 1999); available from http://home.c2i.net/tkjoerne/os/Internet.
Fine, John. "John Fine?s Home Page." [on-line] (accessed 3 JUN 1999); available from http://users.erols.com/johnfine/Internet.
Jourdain, Robert. "Programmer?s Problem Solver for the IBM PC, XT & AT." Simon & Schuster Publishing : New York, NY. 1986.
Microsoft. "Detailed Explanation of FAT Boot Sector." [on-line] (accessed 3 JUN 1999); available from http://support.microsoft.com/support/kb/articles/Q140/4/18.aspInternet.
mjvines. "Quick Hack Bootsector."
Norton, Peter. "Inside the IBM PC Access to Advanced Features and Programming." Prentice-Hall Publishing : Bowie, MD. 1983.
Owen, Gareth. "OS Development." [on-line] (accessed 3 JUN 1999) available from http://members.xoom.com/_XOOM/gaztek/osdev/bootsect/index.htmlInternet.
Tash, Sean. "NYAOS Boot Sector." 1998.
Weeks, AJeff. "PolyOS Boot Loader Code." 1997.