        page    ,132
        title   BOOTSTRAP == Assembly of MS-DOS boot sector code
;******************************************************************************
;
;       NAME:       BOOTSTRP
;
;       FUNCTION:   This is the bootstrap loader for MS-DOS.  It is loaded
;                   by BIOS into memory beginning at 0000:7C00 and control
;                   is passed to its first byte.
;
;       ENTRY:      From ROM BIOS boot code.  The hardware is assumed to have
;                   executed its initialization procedures, and low memory
;                   has been set up to reflect the hardware features of
;                   interest.
;
;                   DL = disk ID from which this record was read (00h or 80h)
;                   Other register contents are undefined.
;
;
;       PROCESSING: This routine:
;
;                       a: Resets the disk system
;                       b: Builds a new *diskette* parm table in memory,
;                          patches the data to reflect the number of sectors
;                          per track on the *boot* device, and changes the
;                          INT 1E vector to point to this changed table.
;                       c: Reads in the first sector of the root directory
;                       d: Verifies that IO.SYS and DOS.SYS are the first
;                          two entries in the root directory
;                       e: Reads the image of IO.SYS into memory beginning
;                          at 700:0.
;
;                   If test (d) fails, or if an I/O error is encountered,
;                   the boot is rejected and the user is told to replace
;                   the disk.
;
;       EXIT:       Control is passed to IO.SYS at 700:0.
;
;                       AX:BX = 32-bit sector number of first data sector
;                       CH    = media descriptor byte
;                       DL    = disk ID (should be 00h or 80h)
;                       0:500 = first sector of root directory
;                       INT 1E vector -> a parm table with proper values
;                                        for sectors/track and head settle
;                                        for boot disk...invalid if boot
;                                        is from a fixed disk.
;
;       ERROR EXIT: None.  Control cannot pass until a successful load of
;                   IO.SYS; the only other exit possible is a three-finger
;                   salute.
;
;       VERSION:    This code reflects boot blocks written by MS-DOS 5.0
;
;       FORMATTING AND COMMENTS: Joe Morris 2/93
;
;******************************************************************************

.radix 16
;-------------;
; Definitions ;
;-------------;

INT1E   equ     078h                            ; INT 1E vector address
DIRBUFF equ     0500h                           ; address of buffer for
                                                ; root directory block

boot            segment byte
                assume cs:boot,ds:boot,ss:boot
                org     100h                    ; dummy
;--------------
;   NOTE: The layout of the boot data block is mandatory regardless of
;         the type of device on which it appears.  The locations are
;         fixed and are assumed by numerous other programs.
;--------------
;         On the other hand, the data beginning at "new_sect_ct" did not
;         exist prior to DOS 4.x and cannot be relied on if the boot block
;         was written by a prior release of DOS.  The determinant seems to
;         be whether "old_sector_ct" is zero (new format) or nonzero (old
;         format).
;-------------


main            proc near
                jmp     short start             ; jump over data block
                nop

;------------------;
;  Boot data block ;
;------------------;

oem_name        db      'MSDOS5.0'
bytesect        dw      512d                    ; bytes/sector
sectclust       db      04                      ; sectors/cluster
reserve_sect    dw      01                      ; Reserved sectors at
                                                ;   beginning of disk
fatcopies       db      02                      ; number of FAT copies
rootentries     dw      512d                    ; Number of root dir entries
old_sector_ct   dw      0                       ; Total # of sectors on disk
                                                ; (zero in new format block)
mediadesc       db      0f8h                    ; media description byte
sectorsfat      dw      64d                     ; sectors per FAT copy
secthead        dw      32d                     ; sectors per head
headcyl         dw      64d                     ; heads per cylinder
hiddensect      dd      32d                     ; special hidden sectors...
hiddensect_lo   equ     word ptr hiddensect     ; ... low bits
hiddensect_hi   equ     word ptr hiddensect+2   ; ... high bits
new_sect_ct     dd      65504d                  ; Total number of sectors on
                                                ;   disk (new format)...
new_sect_ct_lo  equ     word ptr new_sect_ct    ; ... low bits
new_sect_ct_hi  equ     word ptr new_sect_ct+2  ; ... high bits
disk_id         db      80                      ; Physical disk ID for INT 13
                                                ;   (will be 00h or 80h)
head            db      00                      ; Used as scratch work area
                                                ;   (documented as reserved)
ext_sig         db      29                      ; extended boot signature
volser          db      1ch,9eh,0adh,16h        ; volume serial
vol_label       db      11 dup (' ')            ; volume label (if diskette)
fat_type        db      'FAT16   '              ; Type of file allocation table

;---------------------;
; end boot data block ;
;---------------------;

;-----------------------------------------------------

; >>>> starts execution here <<<<

;Note: 'start' is both:
;       the first byte of executable code after the boot
;       data block (at mandatory address 7C3E), and
;       the location of the temporary diskette parm table.

start:
                cli                             ; make sure we're disabled
                xor     ax,ax                   ; clear a work register
                mov     ss,ax                   ; fake out stack logic...
                mov     sp,offset boot          ; Stack starts just ahead of us
                push    ss
                pop     es                      ; Now ss and es are both zero
;;;;
datasect_lo     equ     $+01h          ;<07c49> ; sector # of 1st data - low
;;;;
                mov     bx,INT1E                ; INT 1E vector address
;;;;
cylinder        equ     $+02h          ;<07c4d> ; scratch
datasect_hi     equ     $                       ; sector # of 1st data - high
;;;;
                lds     si,dword ptr ss:[bx]    ; -> disk parm table into ds:si
                push    ds                      ; segment...
;;;;
sector          equ     $                       ; scratch memory
;;;;
                push    si                      ; ...and offset
;;;;
rootsect_lo     equ     $                       ; sector # of root start - low
;;;;
                push    ss                      ; still zero
                push    bx                      ; -> INT 1E vector
;;;;
rootsect_hi     equ     $                       ; sector # of root start - high
;;;;
                mov     di,offset start         ; point to code no longer needed
                mov     cx,000bh                ; (can be overlaid by 11 bytes)
                cld                             ; make sure we're facing forward
                repz    movsb                   ; copy table over our entry code
                                                ;    Move DS:[SI] -> ES:[DI]

                push    es
                pop     ds                      ; zero ds again
                mov     byte ptr [di-02],0fh    ; force in head settle time
                mov     cx,word ptr secthead    ; force in sectors/track for
                mov     byte ptr [di-07],cl     ; *this* disk type
                mov     word ptr [bx+02],ax     ; ...and point INT 1E to our
                mov     word ptr [bx],offset start ; modified table
                sti                             ; OK, we'll take interrupts now
                int     13h                     ; AX is still zero: reset disk
                jc      fail                    ; Reset failed

                xor     ax,ax
                cmp     word ptr old_sector_ct,ax ; Test for old format header:
                                                ; is normal sector count zero?
                je      newcount                ; yes: new count must be valid
                mov     cx,word ptr old_sector_ct ; else copy valid count
                mov     word ptr new_sect_ct_lo,cx ;

newcount:       mov     al,byte ptr fatcopies   ; Number of FAT copies
                mul     word ptr sectorsfat     ; times sectors per FAT give
                                                ; sectors in FAT
                add     ax,word ptr hiddensect_lo ; Add in 32-bit hidden count
                adc     dx,word ptr hiddensect_hi
                add     ax,word ptr reserve_sect ; and reserved count
                adc     dx,00
                mov     word ptr rootsect_lo,ax ; 32-bit root sector number
                mov     word ptr rootsect_hi,dx

                mov     word ptr datasect_lo,ax ; Now calculate the sector #
                mov     word ptr datasect_hi,dx ; of the first data block
                mov     ax,0020h                ; size of a directory entry
                mul     word ptr rootentries    ; times number of root entries
                                                ; gives bytes in root
                mov     bx,word ptr bytesect    ; pick up bytes/sector
                add     ax,bx                   ; figure out how many sectors
                                                ; are in root.  Make sure any
                                                ; fractions are rounded up.
                dec     ax
                div     bx                      ; sectors in root
                add     word ptr datasect_lo,ax
                adc     word ptr datasect_hi,00

                mov     bx,DIRBUFF              ; -> scratch area for directory
                mov     dx,word ptr rootsect_hi ; build c/h/r of root
                mov     ax,word ptr rootsect_lo
                call    calc_chr
                jc      fail                    ; Bang

                mov     al,01                   ; read one record
                call    read_disk
                jb      fail                    ; bang.

                mov     di,bx                   ; -> first directory slot
                mov     cx,11d                  ; compare one filename+ext
                mov     si,offset io_fileid     ; -> "IO      SYS"
                repz    cmpsb                   ; It gotta be there...
                jne     fail                    ; ... or we're dead
                                                ; si now -> "MSDOS   SYS"
                                                ; (note this sneaky way of
                                                ;   saving a few bytes)
                lea     di,word ptr [bx+20h]    ; -> second directory slot
                mov     cx,11d                  ; 11 bytes again
                repz    cmpsb                   ; this too is required
                je      got_boot_files          ; we love it.

fail:           mov     si,offset no_system_msg ; else write nasty message
                call    write_msg
                xor     ax,ax                   ; wait for a keyboard event
                int     16h
                pop     si                      ; Recover POST registers
                pop     ds
                pop     word ptr [si]
                pop     word ptr [si+02]
                int     19h                     ; and go repeat the boot process

pop_fail:       pop     ax                      ; Come here for failure...
                pop     ax                      ; ...within a subroutine
                pop     ax
                jmp     short fail


got_boot_files: mov     ax,word ptr [bx+1ah]    ; cluster number of IO.SYS start
                dec     ax                      ; correct for FAT arithmetic
                dec     ax
                mov     bl,byte ptr sectclust   ; sectors/cluster
                xor     bh,bh
                mul     bx
                add     ax,word ptr datasect_lo ; sector # of first data - low
                adc     dx,word ptr datasect_hi ; sector # of first data - high
                mov     bx,0700h                ; load data into 70:xxx
                mov     cx,0003h                ; only 3 sectors required

read_io_pgm:    push    ax
                push    dx
                push    cx
                call    calc_chr                ; Calc cylinder/head/record #
                jb      pop_fail                ; Code fall down go boom
                mov     al,01
                call    read_disk
                pop     cx
                pop     dx
                pop     ax
                jb      fail

                add     ax,0001h
                adc     dx,00
                add     bx,word ptr bytesect    ; bytes/sector
                loop    read_io_pgm             ; Read three sectors in loop


;       Bootstrap is complete.  Load the information required
;       by IO.SYS for completion of second-level bootstrap
;       and pass control to it.

                mov     ch,byte ptr mediadesc   ; media description byte
                mov     dl,byte ptr disk_id     ; Physical disk ID for INT 13
                mov     bx,word ptr datasect_lo ; sector of first data - low
                mov     ax,word ptr datasect_hi ; sector of first data - high

                db      0eah,000h,000h,070h,000h ; JMP  FAR 0070:0000

main            endp

;<07d52> wr Teletype mode,AL=char
write_msg       proc near
                lodsb                           ; Get next character to output
                or      al,al                   ; Is there one?
                je      sub_exit                ; Exit if not (zero ends string)
                mov     ah,0eh                  ; Output one character to CRT...
                mov     bx,0007h                ; ...via INT 10/AH=3
                int     10h                     ;
                jmp     short write_msg         ; Back for another...

write_msg       endp

;<07d60> Calculate cylinder/head/record # from relative sector #
;        Input is flat sector # in DX:AX

calc_chr        proc near
                cmp     dx,word ptr secthead    ; make sure no divide check...
                jnb     chr_fail
                div     word ptr secthead       ; Develop sector within head
                inc     dl                      ; Sectors start at 1
                mov     byte ptr sector,dl
                xor     dx,dx
                div     word ptr headcyl        ; Develop head within cylinder
                mov     byte ptr head,dl
                mov     word ptr cylinder,ax    ; What's left is the cylinder
                clc                             ; Say all is well
                retn

;-----------------------------------------------------
chr_fail:       stc                             ; Complain via carry flag
sub_exit:       retn                            ; Common subroutine exit here

calc_chr        endp

;<07d81> * Read disk sector addressed by cylinder/head/sector

read_disk       proc near
                mov     ah,02
                mov     dx,word ptr cylinder    ; Load cylinder number
                mov     cl,06                   ; Adjust for BIOS call
                shl     dh,cl                   ; 000:7d89 d2e6
                or      dh,byte ptr sector      ; Sector # is in low bits
                mov     cx,dx
                xchg    ch,cl
                mov     dl,byte ptr disk_id     ; Indicate what disk to read
                mov     dh,byte ptr head        ; Specify the desired head
                int     13h                     ; Read into ES:BX
                retn

;-----------------------------------------------------

no_system_msg   db      0dh,0a
                db      'Non-System disk or disk error'
                db      0dh,0a
                db      'Replace and press any key when ready'
                db      0dh,0a,00

io_fileid       db      'IO      SYS'
                db      'MSDOS   SYS'

                db      00,00                   ; Padding

signature       db      55,0aa

read_disk       endp
boot            ends

                end

