BootStrap Tutorial

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
     ;*************************************************************************
      

BIBLIOGRAPHY

Bass Demon. "Operating System Resources." [on-line] (accessed 3 JUN 1999); available from http://home.c2i.net/tkjoerne/os/Internet.

  • links to documentation of FAT file system by Inbar Raz
  • documentation about hard disk partitioning, MBR, and LBA to CHS translation
  • source code examples of bootsectors for FAT12, FAT16, and FAT32 (disassembly of Windows95) alongside with documents about the boot parameter blocks (BPBA) fields.

    Fine, John. "John Fine?s Home Page." [on-line] (accessed 3 JUN 1999); available from http://users.erols.com/johnfine/Internet.

  • source code examples for various bootstraps that put the processor into protected mode, load files from known locations, load exe binaries, mbr multi-boot, etc.
  • links to programming resources and documentation

    Jourdain, Robert. "Programmer?s Problem Solver for the IBM PC, XT & AT." Simon & Schuster Publishing : New York, NY. 1986.

  • source code snippets demonstrating algorithms for traversing the FAT12 file system

    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.

  • thorough documentation of boot procedure and structures necessary for bootstraps to maintain FAT file system compatibility

    mjvines. "Quick Hack Bootsector."

  • source code example of basic bootstrap that displays values of processor registers
  • see Garth Owens "OS Development" page for the source code

    Norton, Peter. "Inside the IBM PC Access to Advanced Features and Programming." Prentice-Hall Publishing : Bowie, MD. 1983.

  • detailed walkthrough of the FAT12 file system
  • material is dated, however, the file system is the same

    Owen, Gareth. "OS Development." [on-line] (accessed 3 JUN 1999) available from http://members.xoom.com/_XOOM/gaztek/osdev/bootsect/index.htmlInternet.

  • source code example of basic bootstrap that detects the processor type

    Tash, Sean. "NYAOS Boot Sector." 1998.

  • source code example that reads a FAT12 formatted disk and loads a file from the file system
  • see Garth Owens "OS Development" page for the source code

    Weeks, AJeff. "PolyOS Boot Loader Code." 1997.

  • source code example that puts the processor into protected mode and loads a file from a known location.
  • see Garth Owens "OS Development" page for the source code
  •  


    Pierre's Library - Changelog :

    Analyse d'audience