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