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