]> git.saurik.com Git - apple/boot.git/blobdiff - i386/boot0/boot0.s
boot-93.tar.gz
[apple/boot.git] / i386 / boot0 / boot0.s
index dcf9c843bbc58e1923066690516c63d083159762..f1eb72ed999698bf4de2ceee612e17ced99d45ca 100644 (file)
 ; responsibility is to locate the booter partition, load the
 ; booter into memory, and jump to the booter's entry point.
 ; The booter partition can be a primary or a logical partition.
-; But the booter partition must reside within the 8GB limit
-; imposed by CHS addressing + translation.
 ; 
 ; This boot loader can be placed at any of the following places:
-; * Master Boot Record (MBR)
-; * Boot sector of an extended partition
-; * Boot sector of a primary partition
-; * Boot sector of a logical partition
+; 1. Master Boot Record (MBR)
+; 2. Boot sector of an extended partition
+; 3. Boot sector of a primary partition
+; 4. Boot sector of a logical partition
 ;
 ; In order to coexist with a fdisk partition table (64 bytes), and
 ; leave room for a two byte signature (0xAA55) in the end, boot0 is
 ; restricted to 446 bytes (512 - 64 - 2). If boot0 did not have to
-; live in the MBR, then we would have 510 bytes to play with.
+; live in the MBR, then we would have 510 bytes to work with.
 ;
-; boot0 is always loaded by the BIOS or another first level booter
-; to 0:7C00h.
+; boot0 is always loaded by the BIOS or another booter to 0:7C00h.
 ;
 ; This code is written for the NASM assembler.
 ;   nasm boot0.s -o boot0
 
-;--------------------------------------------------------------------------
-; Constants.
 
-DEBUG           EQU  0                  ; enable debugging output
+;
+; Set to 1 to enable obscure debug messages.
+;
+DEBUG                EQU  0
+
+;
+; Set to 1 to support loading the booter (boot2) from a
+; logical partition.
+;
+EXT_PART_SUPPORT     EQU  1
+
+;
+; Various constants.
+;
+kBoot0Segment        EQU  0x0000
+kBoot0Stack          EQU  0xFFF0        ; boot0 stack pointer
+kBoot0LoadAddr       EQU  0x7C00        ; boot0 load address
+kBoot0RelocAddr      EQU  0xE000        ; boot0 relocated address
+
+kMBRBuffer           EQU  0x1000        ; MBR buffer address
+kExtBuffer           EQU  0x1200        ; EXT boot block buffer address
 
-BOOTSEG         EQU  0x0                ; our sole segment
-BOOTSP          EQU  0xFFF0             ; stack pointer
-BOOTLOAD        EQU  0x7C00             ; booter load address
-BOOTRELOC       EQU  0xE000             ; booter is relocated here
-BOOTSIG         EQU  0xAA55             ; booter signature
+kPartTableOffset     EQU  0x1be
+kMBRPartTable        EQU  kMBRBuffer + kPartTableOffset
+kExtPartTable        EQU  kExtBuffer + kPartTableOffset
 
-BOOT2_SIZE      EQU  88                 ; load this many blocks for boot2
-BOOT2_ADDR      EQU  0x3000             ; where to load boot2
+kBoot2Sectors        EQU  112           ; sectors to load for boot2
+kBoot2Address        EQU  0x0000        ; boot2 load address
+kBoot2Segment        EQU  0x2000        ; boot2 load segment
 
-DRIVE_NUM       EQU  0x80               ; "C" drive
-SECTOR_BYTES    EQU  512                ; sector size in bytes
+kSectorBytes         EQU  512           ; sector size in bytes
+kBootSignature       EQU  0xAA55        ; boot sector signature
 
-BUF_MBR         EQU  0x1000             ; memory buffer for MBR
-BUF_EXT         EQU  0x1200             ; memory buffer for extended partition
+kPartCount           EQU  4             ; number of paritions per table
+kPartTypeBoot        EQU  0xab          ; boot2 partition type
+kPartTypeExtDOS      EQU  0x05          ; DOS extended partition type
+kPartTypeExtWin      EQU  0x0f          ; Windows extended partition type
+kPartTypeExtLinux    EQU  0x85          ; Linux extended partition type
 
-TABLE_MAIN      EQU  BUF_MBR + 0x1be    ; location of main partition table
-TABLE_EXT       EQU  BUF_EXT + 0x1be    ; location of ext partition table
-ENTRY_SIZE      EQU  16                 ; size of each fdisk partition entry
-TYPE_BOOT       EQU  0xab               ; partition type we are looking for
-TYPE_EXT        EQU  0x05               ; extended partition type
-TYPE_EXT_1      EQU  0x0f               ; Windows extended partition
-TYPE_EXT_2      EQU  0x85               ; Linux extended partition
-EXT_LEVELS_MAX  EQU  128                ; max extended partition levels
+%ifdef FLOPPY
+kDriveNumber         EQU  0x00
+%else
+kDriveNumber         EQU  0x80
+%endif
+
+;
+; In memory variables.
+;
+ebios_lba       dd   0   ; starting LBA of the intial extended partition.
+read_func       dw   0   ; pointer to the LBA or CHS read function
 
-; Disk parameters gathered through INT13/F8 call.
 ;
-max_sectors     db   0                  ; number of sectors per track
-max_heads       db   0                  ; number of heads
+; Format of fdisk partition entry.
+;
+; The symbol 'part_size' is automatically defined as an `EQU'
+; giving the size of the structure.
+;
+           struc part
+.bootid:   resb 1      ; bootable or not 
+.head:     resb 1      ; starting head, sector, cylinder
+.sect:     resb 1      ;
+.cyl:      resb 1      ;
+.type:     resb 1      ; partition type
+.endhead   resb 1      ; ending head, sector, cylinder
+.endsect:  resb 1      ;
+.endcyl:   resb 1      ;
+.lba:      resd 1      ; starting lba
+.sectors   resd 1      ; size in sectors
+           endstruc
 
-; Parameters to our load function.
 ;
-chs_cx          dw   0                  ; cx register for INT13/F2 call
-chs_dx          dw   0                  ; dx register for INT13/F2 call
+; Macros.
+;
+%macro DebugCharMacro 1
+    mov   al, %1
+    call  print_char
+%endmacro
 
+%if DEBUG
+%define DebugChar(x)  DebugCharMacro x
+%else
+%define DebugChar(x)
+%endif
 
 ;--------------------------------------------------------------------------
 ; Start of text segment.
 
     SEGMENT .text
 
-    ORG     0xE000              ; must match BOOTRELOC
+    ORG     0xE000                  ; must match kBoot0RelocAddr
 
 ;--------------------------------------------------------------------------
-; Loaded at 0:7c00h.
+; Boot code is loaded at 0:7C00h.
 ;
 start
-    ; Set up the stack to grow down from BOOTSEG:BOOTSP.
+    ;
+    ; Set up the stack to grow down from kBoot0Segment:kBoot0Stack.
     ; Interrupts should be off while the stack is being manipulated.
     ;
-    cli                         ; interrupts off
-    mov     ax, BOOTSEG         ;
-    mov     ss, ax              ; ss <- BOOTSEG
-    mov     sp, BOOTSP          ; sp <- BOOTSP
-    sti                         ; reenable interrupts
+    cli                             ; interrupts off
+    xor     ax, ax                  ; zero ax
+    mov     ss, ax                  ; ss <- 0
+    mov     sp, kBoot0Stack         ; sp <- top of stack
+    sti                             ; reenable interrupts
+
+    mov     es, ax                  ; es <- 0
+    mov     ds, ax                  ; ds <- 0
 
-    ; Relocate the booter code from DS:SI to ES:DI,
-    ; or from 0:7C00h to BOOTSEG:BOOTRELOC.
     ;
-    mov     es, ax              ; es <- BOOTSEG
-    xor     ax, ax
-    mov     ds, ax              ; ds <- 0
-    mov     si, BOOTLOAD        ; si <- BOOTLOAD (source)
-    mov     di, BOOTRELOC       ; di <- BOOTRELOC (destination)
+    ; Relocate boot0 code.
     ;
-    cld                         ; auto-increment SI and/or DI registers
-    mov     cx, 256             ; copy 256 words (512 bytes)
-    repnz   movsw               ; repeat string move (word) operation
+    mov     si, kBoot0LoadAddr      ; si <- source
+    mov     di, kBoot0RelocAddr     ; di <- destination
+    ;
+    cld                             ; auto-increment SI and/or DI registers
+    mov     cx, kSectorBytes/2      ; copy 256 words
+    repnz   movsw                   ; repeat string move (word) operation
 
     ; Code relocated, jump to start_reloc in relocated location.
     ;
-    jmp     BOOTSEG:start_reloc
+    jmp     0:start_reloc
 
 ;--------------------------------------------------------------------------
 ; Start execution from the relocated location.
 ;
-start_reloc
-    mov     ax, BOOTSEG
-    mov     ds, ax              ; ds <- BOOTSEG
+start_reloc:
+
+    DebugChar('>')
 
-    mov     al, '='             ; indicate execution start
-    call    putchar
+    mov     dl, kDriveNumber        ; starting BIOS drive number
+
+.loop:
+
+%if DEBUG
+    mov     al, dl
+    call    print_hex
+%endif
 
-    ; Get disk parameters (CHS) using INT13/F8 call.
     ;
-    mov     dl, DRIVE_NUM       ; boot drive is drive C
-    mov     ah, 8               ; Read Disk Driver Parameter function
-    int     0x13
-    and     cl, 0x3f            ; sectors/track
-    mov     [max_sectors], cl
-    mov     [max_heads], dh
-    jc      error
-
-    mov     al, '>'             ; indicate INT13/F8 success
-    call    putchar
-
-    ; Since this code may not always reside in the MBR, we will always
-    ; start by loading the MBR to BUF_MBR.
-    ;
-    mov     WORD [chs_cx], 0x0001       ; cyl = 0, sect = 1
-    mov     BYTE [chs_dx + 1], 0        ; head = 0
-    xor     cx, cx                      ; skip 0 sectors
-    mov     ax, 1                       ; read 1 sector
-    mov     bx, BUF_MBR                 ; load buffer
+    ; Clear various flags in memory.
+    ;
+    xor     eax, eax
+    mov     [ebios_lba], eax            ; clear EBIOS LBA offset
+    mov     WORD [read_func], read_chs  ; assume CHS support
+
+    ;
+    ; Since this code may not always reside in the MBR, always start by
+    ; loading the MBR to kMBRBuffer.
+    ;
+    mov     al, 1                   ; load one sector
+    xor     bx, bx
+    mov     es, bx                  ; MBR load segment = 0
+    mov     bx, kMBRBuffer          ; MBR load address
+    mov     si, bx                  ; pointer to fake partition entry
+    mov     WORD [si], 0x0000       ; CHS DX: head = 0
+    mov     WORD [si + 2], 0x0001   ; CHS CX: cylinder = 0, sector = 1
+
     call    load
-    jc      error
+    jc      .next_drive             ; MBR load error
 
-    mov     di, TABLE_MAIN              ; argument for find_booter
-    cmp     WORD [di + 64], BOOTSIG     ; correct signature found?
-    jne     error                       ; Oops! no signature!
-    mov     bl, TYPE_BOOT               ; look for this partition type
-    mov     bh, 0                       ; initial nesting level is 0
-    call    find_booter
+    ;
+    ; Check if EBIOS is supported for this hard drive.
+    ;
+    mov     ah, 0x41                ; Function 0x41
+    mov     bx, 0x55AA              ; check signature
+;   mov     dl, kDriveNumber        ; Drive number
+    int     0x13
 
-error
-    mov     si, load_error
-    call    message
-hang_1
-    jmp     hang_1
+    ;
+    ; If successful, the return values are as follows:
+    ;
+    ; carry = 0
+    ; ah    = major version of EBIOS extensions (0x21 = version 1.1)
+    ; al    = altered
+    ; bx    = 0xAA55
+    ; cx    = support bits. bit 0 must be set for function 0x42.
+    ;
+    jc      .ebios_check_done
+    cmp     bx, 0xAA55              ; check BX = 0xAA55
+    jnz     .ebios_check_done
+    test    cl, 0x01                ; check enhanced drive read support
+    mov     WORD [read_func], read_lba  ; use read_lba for reads
+    DebugChar('E')                  ; EBIOS supported
+.ebios_check_done:
+
+    ;
+    ; Look for the booter partition in the MBR partition table,
+    ; which is at offset kMBRPartTable.
+    ;
+    mov     di, kMBRPartTable       ; pointer to partition table
+    mov     ah, 0                   ; initial nesting level is 0
+    call    find_boot               ; will not return on success
+
+.next_drive:
+    inc     dl                      ; next drive number
+    test    dl, 0x4                 ; went through all 4 drives?
+    jz      .loop                   ; not yet, loop again
+
+    mov     si, boot_error_str
+    call    print_string
+
+hang:
+    jmp     SHORT hang
 
 ;--------------------------------------------------------------------------
-; Locate the booter partition and load the booter.
+; Find the boot partition and load the booter from the partition.
 ;
 ; Arguments:
-;   di - pointer to fdisk partition table.
-;   bl - partition type to look for.
+;   AH = recursion nesting level
+;   DL = drive number (0x80 + unit number)
+;   DI = pointer to fdisk partition table.
 ;
-; The following registers are modified:
-;   ax, bh
+; Clobber list:
+;   AX, BX, EBP
 ;
-find_booter
-    push    cx
+find_boot:
+    push    cx                      ; preserve CX and SI
     push    si
 
-    mov     si, di              ; si <- pointer to partition table
-    mov     cx, 4               ; 4 partition entries per table
-
-find_booter_pri
     ;
-    ; Hunt for a fdisk partition type that matches the value in bl.
+    ; Check for boot block signature 0xAA55 following the 4 partition
+    ; entries.
     ;
-%IF DEBUG
-    mov     al, bh              ; log partition type seen
-    call    putspace
-    mov     al, [si + 4]
-    call    display_byte
-%ENDIF
-
-    cmp     BYTE [si + 4], bl   ; Is this the booter partition?
-    je      load_booter         ; yes, load the booter
+    cmp     WORD [di + part_size * kPartCount], kBootSignature
+    jne     .exit                   ; boot signature not found
 
-    add     si, ENTRY_SIZE      ; si <- next partition entry
-    loop    find_booter_pri     ; loop while cx is not zero
+    mov     si, di                  ; make SI a pointer to partition table
+    mov     cx, kPartCount          ; number of partition entries per table
 
-    ; No primary (or perhaps logical) booter partition found in the
-    ; current partition table. Restart and look for extended partitions.
+.loop:
     ;
-    mov     si, di              ; si <- pointer to partition table
-    mov     cx, 4               ; 4 partition entries per table
+    ; First scan through the partition table looking for the boot
+    ; partition. Postpone walking the extended partition chain for
+    ; the second pass. Do not merge the two without changing the
+    ; buffering scheme used to store extended partition tables.
+    ;
+%if DEBUG
+    mov     al, ah                  ; indent based on nesting level
+    call    print_spaces
+    mov     al, [si + part.type]    ; print partition type
+    call    print_hex
+%endif
+
+    cmp     BYTE [si + part.type], kPartTypeBoot
+    jne     .continue
 
-find_booter_ext
     ;
-    ; Look for extended partition entries in the partition table.
+    ; Found boot partition, read boot2 image to memory.
     ;
-%IF DEBUG
-    mov     al, bh              ; log partition type seen
-    call    putspace
-    mov     al, 'E'
-    call    putchar
-    mov     al, [si + 4]
-    call    display_byte
-%ENDIF
+    mov     al, kBoot2Sectors
+    mov     bx, kBoot2Segment
+    mov     es, bx
+    mov     bx, kBoot2Address
+    call    load                    ; will not return on success
+    jc      .continue               ; load error, keep looking?
 
-    cmp     BYTE [si + 4], TYPE_EXT     ; Is this an extended partition?
-    je      find_booter_ext_2           ; yes, load its partition table
+    ;
+    ; Jump to boot2. The drive number is already in register DL.
+    ;
+    ; The first sector loaded from the disk is reserved for the boot
+    ; block (boot0), adjust the jump location by adding a sector offset.
+    ;
+    jmp     kBoot2Segment:kBoot2Address + kSectorBytes
 
-    cmp     BYTE [si + 4], TYPE_EXT_1   ; Is this an extended partition?
-    je      find_booter_ext_2           ; yes, load its partition table
-       
-    cmp     BYTE [si + 4], TYPE_EXT_2   ; Is this an extended partition?
-    je      find_booter_ext_2           ; yes, load its partition table
+.continue:
+    add     si, part_size           ; advance SI to next partition entry
+    loop    .loop                   ; loop through all partition entries
 
-find_booter_ext_1
+%if EXT_PART_SUPPORT
     ;
-    ; si is not pointing to an extended partition entry,
-    ; try the next entry in the partition table.
+    ; No primary (or logical) boot partition found in the current
+    ; partition table. Restart and look for extended partitions.
     ;
-    add     si, ENTRY_SIZE      ; si <- next partition entry
-    loop    find_booter_ext     ; loop while cx is not zero
+    mov     si, di                  ; make SI a pointer to partition table
+    mov     cx, kPartCount          ; number of partition entries per table
 
-    jmp     find_booter_end     ; give up
+.ext_loop:
 
-find_booter_ext_2
-    cmp     bh, EXT_LEVELS_MAX
-    ja      find_booter_end     ; in too deep!
+    mov     al, [si + part.type]    ; AL <- partition type
 
-    inc     bh                  ; increment nesting level counter
+    cmp     al, kPartTypeExtDOS     ; Extended DOS
+    je      .ext_load
 
-    ; Prepare the arguments for the load function call to
-    ; load the extended partition table into memory.
-    ; Note that si points to the extended partition entry.
-    ;
-    mov     ax, [si]            ; DH/DL
-    mov     [chs_dx], ax
-    mov     ax, [si + 2]        ; CH/CL
-    mov     [chs_cx], ax
-    pusha
-    xor     cx, cx              ; skip 0 sectors
-    mov     ax, 1               ; read 1 sector
-    mov     bx, BUF_EXT         ; load to BUF_EXT
-    call    load
-    popa
+    cmp     al, kPartTypeExtWin     ; Extended Windows(95)
+    je      .ext_load
 
-    jc      find_booter_ext_3   ; bail out if load failed
+    cmp     al, kPartTypeExtLinux   ; Extended Linux
+    je      .ext_load
 
-    mov     di, TABLE_EXT       ; di <- pointer to new partition table
-    cmp     WORD [di + 64], BOOTSIG
-    jne     find_booter_ext_3   ; OhOh! no signature!
+.ext_continue:
+    ;
+    ; Advance si to the next partition entry in the extended
+    ; partition table.
+    ;
+    add     si, part_size           ; advance SI to next partition entry
+    loop    .ext_loop               ; loop through all partition entries
+    jmp     .exit                   ; boot partition not found
 
-    call    find_booter         ; recursion...
+.ext_load:
+    ;
+    ; Setup the arguments for the load function call to bring the
+    ; extended partition table into memory.
+    ; Remember that SI points to the extended partition entry.
+    ;
+    mov     al, 1                   ; read 1 sector
+    xor     bx, bx
+    mov     es, bx                  ; es = 0
+    mov     bx, kExtBuffer          ; load extended boot sector
+    call    load
+    jc      .ext_continue           ; load error
 
-find_booter_ext_3
-    dec     bh                  ; decrement nesting level counter
+    ;
+    ; The LBA address of all extended partitions is relative based
+    ; on the LBA address of the extended partition in the MBR, or
+    ; the extended partition at the head of the chain. Thus it is
+    ; necessary to save the LBA address of the first extended partition.
+    ;
+    or      ah, ah
+    jnz     .ext_find_boot
+    mov     ebp, [si + part.lba]
+    mov     [ebios_lba], ebp
+
+.ext_find_boot:
+    ;
+    ; Call find_boot recursively to scan through the extended partition
+    ; table. Load DI with a pointer to the extended table in memory.
+    ;
+    inc     ah                      ; increment recursion level
+    mov     di, kExtPartTable       ; partition table pointer
+    call    find_boot               ; recursion...
+    ;dec    ah
 
-    ; If we got here, then we know there isn't a booter
-    ; partition linked from this partition entry.
+    ;
+    ; Since there is an "unwritten" rule that limits each partition table
+    ; to have 0 or 1 extended partitions, there is no point in looking for
+    ; any additional extended partition entries at this point. There is no
+    ; boot partition linked beyond the extended partition that was loaded
+    ; above.
+    ;
 
-    test    bh, bh              ; if we are at level 0, then
-    jz      find_booter_ext_1   ; look for next extended partition entry
+%endif ; EXT_PART_SUPPORT
 
-find_booter_end
+.exit:
+    ;
+    ; Boot partition not found. Giving up.
+    ;
     pop     si
     pop     cx
     ret
 
 ;--------------------------------------------------------------------------
-; Yeah! Found the booter partition. The first sector in this partition
-; is reserved for the boot sector code (us). So load the booter
-; starting from the second sector in the partition, then jump to the
-; start of the booter.
-;
-load_booter
-    mov     ax, [si]            ; DH/DL
-    mov     [chs_dx], ax
-    mov     ax, [si + 2]        ; CH/CL
-    mov     [chs_cx], ax
-
-    mov     cx, 1               ; skip the initial boot sector
-    mov     ax, BOOT2_SIZE      ; read BOOT2_SIZE sectors
-    mov     bx, BOOT2_ADDR      ; where to place boot2 code
-    call    load                ; load it...
-
-    xor     edx, edx            ; argument for boot2 (hard drive boot)
-    jmp     BOOTSEG:BOOT2_ADDR  ; there is no going back now!
-
-;--------------------------------------------------------------------------
-; Load sectors from disk using INT13/F2 call. The sectors are loaded
-; one sector at a time to avoid any BIOS bugs, and eliminate
-; complexities with crossing track boundaries, and other gotchas.
+; load - Load one or more sectors from a partition.
 ;
 ; Arguments:
-;   cx - number of sectors to skip
-;   ax - number of sectors to read
-;   bx - pointer to the memory buffer (must not cross a segment boundary)
-;   [chs_cx][chs_dx] - CHS starting position
+;   AL = number of 512-byte sectors to read.
+;   ES:BX = pointer to where the sectors should be stored.
+;   DL = drive number (0x80 + unit number)
+;   SI = pointer to the partition entry.
 ;
 ; Returns:
-;   CF = 0  success
-;   CF = 1  error
-;
-; The caller must save any registers it needs.
-;
-load
-    jcxz    load_sectors
-    call    next_sector         ; [chs_cx][chs_dx] <- next sector
-    loop    load
-
-load_sectors
-    mov     cx, ax              ; cx <- number of sectors to read
-
-load_loop
-    call    read_sector         ; load a single sector
-    jc      load_exit           ; abort if carry flag is set
-    add     bx, SECTOR_BYTES    ; increment buffer pointer
-    call    next_sector         ; [chs_cx][chs_dx] <- next sector
-    loop    load_loop
-    clc                         ; successful exit
-load_exit
+;   CF = 0 success
+;        1 error
+;
+load:
+    push    cx
+    mov     cx, 5                   ; number of times to retry
+
+.loop:
+    call    [read_func]             ; call either read_chs or read_lba
+    jnc     .exit                   ; load success
+    loop    .loop                   ; retry on error
+
+.exit:
+    pop     cx
     ret
 
 ;--------------------------------------------------------------------------
-; Read a single sector from the hard disk.
+; read_chs - Read sectors from a partition using CHS addressing.
 ;
 ; Arguments:
-;   [chs_cx][chs_dx] - CHS starting position
-;   bx - pointer to the sector memory buffer
-;        (must not cross a segment boundary)
+;   AL = number of 512-byte sectors to read.
+;   ES:BX = pointer to where the sectors should be stored.
+;   DL = drive number (0x80 + unit number)
+;   SI = pointer to the partition entry.
 ;
 ; Returns:
-;   CF = 0  success
-;   CF = 1  error
-;
-; Caller's cx register is preserved.
+;   CF = 0 success
+;        1 error
 ;
-read_sector
-    push    cx
-    mov     cx, 5               ; try 5 times to read the sector
-
-read_sector_1
-    mov     bp, cx              ; save cx
+read_chs:
+    pusha                           ; save all registers
 
-    mov     cx, [chs_cx]
-    mov     dx, [chs_dx]
-    mov     dl, DRIVE_NUM       ; drive number
-    mov     ax, 0x0201          ; Func 2, read 1 sector
-    int     0x13                ; read sector
-    jnc     read_sector_ok      ; CF = 0 indicates success
-
-    mov     al, '*'             ; sector read error indicator
-    call    putchar
+    ;
+    ; Read the CHS start values from the partition entry.
+    ;
+    mov     dh, [ si + part.head ]  ; drive head
+    mov     cx, [ si + part.sect ]  ; drive sector + cylinder
 
-    xor     ax, ax              ; Reset the drive and retry the read
-    int     0x13
+    ;
+    ; INT13 Func 2 - Read Disk Sectors
+    ;
+    ; Arguments:
+    ;   AH    = 2
+    ;   AL    = number of sectors to read
+    ;   CH    = lowest 8 bits of the 10-bit cylinder number
+    ;   CL    = bits 6 & 7: cylinder number bits 8 and 9
+    ;           bits 0 - 5: starting sector number (1-63)
+    ;   DH    = starting head number (0 to 255)
+    ;   DL    = drive number (80h + drive unit)
+    ;   es:bx = pointer where to place sectors read from disk
+    ;
+    ; Returns:
+    ;   AH    = return status (sucess is 0)
+    ;   AL    = burst error length if ah=0x11 (ECC corrected)
+    ;   carry = 0 success
+    ;           1 error
+    ;
+;   mov     dl, kDriveNumber
+    mov     ah, 0x02                ; Func 2
+    int     0x13                    ; INT 13
+    jnc     .exit
 
-    mov     cx, bp
-    loop    read_sector_1       ; retry while cx is not zero
+    DebugChar('e')                  ; indicate INT13/F2 error
 
-    stc                         ; set carry flag to indicate error
-    pop     cx
-    ret
+    ;
+    ; Issue a disk reset on error.
+    ; Should this be changed to Func 0xD to skip the diskette controller
+    ; reset?
+    ;
+    xor     ax, ax                  ; Func 0
+    int     0x13                    ; INT 13
+    stc                             ; set carry to indicate error
 
-read_sector_ok
-    mov     al, '.'             ; successful sector read indicator
-    call    putchar
-    clc                         ; success, clear carry flag
-    pop     cx
+.exit:
+    popa
     ret
 
 ;--------------------------------------------------------------------------
-; Given the current CHS position stored in [chs_cx][chs_dx], update
-; it so that the value in [chs_cx][chs_dx] points to the following
-; sector.
+; read_lba - Read sectors from a partition using LBA addressing.
 ;
 ; Arguments:
-;   [chs_cx][chs_dx] - CHS position
-;
-;   [max_sectors] and [max_heads] must be valid.
+;   AL = number of 512-byte sectors to read (valid from 1-127).
+;   ES:BX = pointer to where the sectors should be stored.
+;   DL = drive number (0x80 + unit number)
+;   SI = pointer to the partition entry.
 ;
-; Caller's ax and bx registers are preserved.
+; Returns:
+;   CF = 0 success
+;        1 error
 ;
-next_sector
-    push    ax
-    push    bx
+read_lba:
+    pusha                           ; save all registers
+    mov     bp, sp                  ; save current SP
 
-    ; Extract the CHS values from the packed register values in memory.
     ;
-    mov     al, [chs_cx]
-    and     al, 0x3f            ; al <- sector number (1-63)
+    ; Create the Disk Address Packet structure for the
+    ; INT13/F42 (Extended Read Sectors) on the stack.
+    ;
 
-    mov     bx, [chs_cx]
-    rol     bl, 2
-    ror     bx, 8
-    and     bx, 0x03ff          ; bx <- cylinder number
+    ; push    DWORD 0               ; offset 12, upper 32-bit LBA
+    push    ds                      ; For sake of saving memory,
+    push    ds                      ; push DS register, which is 0.
 
-    mov     ah, [chs_dx + 1]    ; ah <- head number
+    mov     ecx, [ebios_lba]        ; offset 8, lower 32-bit LBA
+    add     ecx, [si + part.lba]
+    push    ecx
 
-    inc     al                  ; Increment CHS by one sector.
-    cmp     al, [max_sectors]
-    jbe     next_sector_done
+    push    es                      ; offset 6, memory segment
 
-    inc     ah
-    cmp     ah, [max_heads]
-    jbe     next_sector_new_head
+    push    bx                      ; offset 4, memory offset
 
-    inc     bx
+    xor     ah, ah                  ; offset 3, must be 0
+    push    ax                      ; offset 2, number of sectors
 
-next_sector_new_cyl
-    xor     ah, ah              ; head number starts at 0
+    push    WORD 16                 ; offset 0-1, packet size
 
-next_sector_new_head
-    mov     al, 1               ; sector number starts at 1
+    ;
+    ; INT13 Func 42 - Extended Read Sectors
+    ;
+    ; Arguments:
+    ;   AH    = 0x42
+    ;   DL    = drive number (80h + drive unit)
+    ;   DS:SI = pointer to Disk Address Packet
+    ;
+    ; Returns:
+    ;   AH    = return status (sucess is 0)
+    ;   carry = 0 success
+    ;           1 error
+    ;
+    ; Packet offset 2 indicates the number of sectors read
+    ; successfully.
+    ;
+;   mov     dl, kDriveNumber
+    mov     si, sp
+    mov     ah, 0x42
+    int     0x13
+
+    jnc     .exit
 
-next_sector_done
-    ; Reassemble the CHS values back into the packed representation
-    ; in memory.
+    DebugChar('E')                  ; indicate INT13/F42 error
+
+    ;
+    ; Issue a disk reset on error.
+    ; Should this be changed to Func 0xD to skip the diskette controller
+    ; reset?
     ;
-    mov     [chs_cx + 1], bl    ; lower 8-bits of the 10-bit cylinder
-    ror     bh, 2
-    or      bh, al
-    mov     [chs_cx], bh        ; cylinder & sector number
-    mov     [chs_dx + 1], ah    ; head number
+    xor     ax, ax                  ; Func 0
+    int     0x13                    ; INT 13
+    stc                             ; set carry to indicate error
 
-    pop     bx
-    pop     ax
+.exit:
+    mov     sp, bp                  ; restore SP
+    popa
     ret
 
 ;--------------------------------------------------------------------------
 ; Write a string to the console.
 ;
 ; Arguments:
-;   ds:si   pointer to a NULL terminated string.
-;
-; The following registers are modified:
-;   ax, bx, si
-;
-message
-    mov     bx, 1               ; bh=0, bl=1 (blue)
-    cld                         ; increment SI after each lodsb call
-message_loop
-    lodsb                       ; load a byte from DS:SI into al
-    cmp     al, 0               ; Is it a NULL?
-    je      message_done        ; yes, all done
-    mov     ah, 0xE             ; bios INT10 Func 0xE
-    int     0x10                ; bios display a byte in tty mode
-    jmp     short message_loop
-message_done
+;   DS:SI   pointer to a NULL terminated string.
+;
+; Clobber list:
+;   AX, BX, SI
+;
+print_string
+    mov     bx, 1                   ; BH=0, BL=1 (blue)
+    cld                             ; increment SI after each lodsb call
+.loop
+    lodsb                           ; load a byte from DS:SI into AL
+    cmp     al, 0                   ; Is it a NULL?
+    je      .exit                   ; yes, all done
+    mov     ah, 0xE                 ; INT10 Func 0xE
+    int     0x10                    ; display byte in tty mode
+    jmp     short .loop
+.exit
     ret
 
+%if DEBUG
+
 ;--------------------------------------------------------------------------
 ; Write a ASCII character to the console.
 ;
 ; Arguments:
-;   al   contains the ASCII character printed.
-;
-putchar
-    push    bx
-    mov     bx, 1               ; bh=0, bl=1 (blue)
-    mov     ah, 0x0e            ; bios int 10, function 0xe
-    int     0x10                ; bios display a byte in tty mode
-    pop     bx
-    ret
-
-%IF DEBUG
-;==========================================================================
-; DEBUG FUNCTION START
+;   AL = ASCII character.
 ;
-; If DEBUG is set to 1, this booter will become too large for the MBR,
-; but it will still be less than 510 bytes, which is fine for a partition's
-; boot sector.
-;==========================================================================
+print_char
+    pusha
+    mov     bx, 1                   ; BH=0, BL=1 (blue)
+    mov     ah, 0x0e                ; bios INT 10, Function 0xE
+    int     0x10                    ; display byte in tty mode
+    popa
+    ret
 
 ;--------------------------------------------------------------------------
 ; Write a variable number of spaces to the console.
 ;
 ; Arguments:
-;   al   number to spaces to display
+;   AL = number to spaces.
 ;
-putspace
-    push    cx
+print_spaces:
+    pusha
     xor     cx, cx
-    mov     cl, al              ; use cx as the loop counter
-    mov     al, ' '             ; character to print
-putspace_loop
-    jcxz    putspace_done
-    call    putchar
-    loop    putspace_loop
-putspace_done
-    pop     cx
+    mov     cl, al                  ; use CX as the loop counter
+    mov     al, ' '                 ; character to print
+.loop:
+    jcxz    .exit
+    call    print_char
+    loop    .loop
+.exit:
+    popa
     ret
 
 ;--------------------------------------------------------------------------
-; Write the hex byte value to the console.
+; Write the byte value to the console in hex.
 ;
 ; Arguments:
-;   al   contains the byte to be displayed. e.g. if al is 0x3f, then 3F
-;        will be displayed on screen.
+;   AL = Value to be displayed in hex.
 ;
-display_byte
+print_hex:
     push    ax
     ror     al, 4
-    call    display_nibble      ; display upper nibble
+    call    print_nibble            ; display upper nibble
     pop     ax
-    call    display_nibble      ; display lower nibble
-    ;
-    mov     ax, 10              ; display carriage return
-    call    putchar
-    mov     ax, 13
-    call    putchar
+    call    print_nibble            ; display lower nibble
+
+    mov     al, 10                  ; carriage return
+    call    print_char
+    mov     al, 13
+    call    print_char
     ret
 
-display_nibble
+print_nibble:
     and     al, 0x0f
     add     al, '0'
     cmp     al, '9'
-    jna     display_nibble_1
+    jna     .print_ascii
     add     al, 'A' - '9' - 1
-display_nibble_1
-    call    putchar
+.print_ascii:
+    call    print_char
     ret
 
-;==========================================================================
-; DEBUG FUNCTION END
-;==========================================================================
-%ENDIF
+%endif ; DEBUG
 
 ;--------------------------------------------------------------------------
 ; NULL terminated strings.
 ;
-load_error   db  10, 13, 'Load Error', 0
+boot_error_str   db  10, 13, 'Boot Error', 0
 
 ;--------------------------------------------------------------------------
 ; Pad the rest of the 512 byte sized booter with zeroes. The last
@@ -558,10 +660,31 @@ load_error   db  10, 13, 'Load Error', 0
 ; that the 'times' argument is negative.
 
 pad_boot
-       times 446-($-$$) db 0
+    times 446-($-$$) db 0
+
+%ifdef FLOPPY
+;--------------------------------------------------------------------------
+; Put fake partition entries for the bootable floppy image
+;
+part1bootid     db    0x80          ; first partition active
+part1head       db    0x00          ; head #
+part1sect       db    0x02          ; sector # (low 6 bits)
+part1cyl        db    0x00          ; cylinder # (+ high 2 bits of above)
+part1systid     db    0xab          ; Apple boot partition
+times   3       db    0x00          ; ignore head/cyl/sect #'s
+part1relsect    dd    0x00000001    ; start at sector 1
+part1numsect    dd    0x00000080    ; 64K for booter
+part2bootid     db    0x00          ; not active
+times   3       db    0x00          ; ignore head/cyl/sect #'s
+part2systid     db    0xa8          ; Apple UFS partition
+times   3       db    0x00          ; ignore head/cyl/sect #'s
+part2relsect    dd    0x00000082    ; start after booter
+; part2numsect  dd    0x00000abe    ; 1.44MB - 65K
+part2numsect    dd    0x000015fe    ; 2.88MB - 65K
+%endif
 
 pad_table_and_sig
     times 510-($-$$) db 0
-    dw BOOTSIG
+    dw    kBootSignature
 
     END