;***********************************************************************
;*                                                                     *
;*  Fmt.asm                                             23-JAN-1990    *
;*                                                                     *
;*  Format Floppy Disk - version 1.00 - IBM(R) DOS 4.0 style format    *
;*                                                                     *
;*  Designed for use with IBM AT compatible sytems (including PS/2s)   *
;*                                                                     *
;*  Syntax: FMT d: size [v] [s] [n]                                    *
;*                                                                     *
;*    where: d = drive (A or B), size = floppy size in Kb,             *
;*                                                                     *
;*      the optional v enables format verification,                    *
;*                                                                     *
;*      the optional s copies the system files,                        *
;*                                                                     *
;*      and the optional n bypasses the `insert floppy' prompt         *
;*                                                                     *
;*    supported sizes are:                                             *
;*                                                                     *
;*      360 for a 360 Kb floppy in a 360 Kb or 1.2 Mb drive            *
;*      720 for a 720 Kb floppy in a 720 Kb or 1.44 Mb drive           *
;*      1200 for a 1.2 Mb floppy in a 1.2 Mb drive                     *
;*      1440 for a 1.44 Mb floppy in a 1.44 Mb drive                   *
;*                                                                     *
;*  Exit codes:                                                        *
;*                                                                     *
;*    0 = successful format                                            *
;*    1 = failure during format, verify, or write                      *
;*    2 = disk size / drive type combination is not supported          *
;*    3 = requested drive does not exist                               *
;*    4 = invalid or null command line                                 *
;*    5 = system files not found or error writing system files         *
;*    6 = computer is not AT compatible                                *
;*    7 = insufficient memory for file copy buffer (system transfer)   *
;*                                                                     *
;*  In order to use the system transfer option, the following files    *
;*                                                                     *
;*    must be in the root diectory of the default drive:               *
;*                                                                     *
;*      1: IBMBIO.COM (normally a hidden file)                         *
;*                                                                     *
;*      2: IBMDOS.COM (normally a hidden file)                         *
;*                                                                     *
;*      3: COMMAND.COM                                                 *
;*                                                                     *
;*  Compile with Borland Turbo Assembler(R) version 1.0 or greater     *
;*                                                                     *
;*      tasm /t fmt                     tasm /t /zi fmt                *
;*                              or                                     *
;*      tlink /t /x fmt                 tlink /v /x fmt                *
;*                                                                     *
;*                                      tdstrip -c -s fmt              *
;*                                                                     *
;*                                      for use with Turbo Debugger(R) *
;*    or                                                               *
;*                                                                     *
;*  Compile with Microsoft(R) Macro Assembler version 5.1 or greater   *
;*                                                                     *
;*      masm /t fmt;                                                   *
;*                                                                     *
;*      link fmt;                                                      *
;*                                                                     *
;*      exe2bin fmt fmt.com                                            *
;*                                                                     *
;*      del fmt.exe                                                    *
;*                                                                     *
;*  Copyright (C) 1990 by:                                             *
;*                                                                     *
;*  Clair Alan Hardesty                                                *
;*  IBM-PC Hardware/Software Consultant                                *
;*  10301 Johnson Av                                                   *
;*  Cupertino, CA 95014                                                *
;*  (408) 446-0550                                                     *
;*  CompuServe 75350,16                                                *
;*                                                                     *
;*  Microsoft(R) is a registered trademark of Microsoft Corporation    *
;*                                                                     *
;*  IBM(R) and PS/2 are registered trademarks of International         *
;*                                                                     *
;*   Business Machines Corporation                                     *
;*                                                                     *
;*  Turbo Assembler(R) and Turbo Debugger(R) are registered trademarks *
;*                                                                     *
;*   of Borland International                                          *
;*                                                                     *
;***********************************************************************

;***********************************************************************

                ; set-up for the far jump used in the boot record code

;***********************************************************************

ibmbio          segment at 0070h

                assume cs:ibmbio

                org 0000h

program_loader  label far

ibmbio          ends

;***********************************************************************

                ; set-up for system identification

;***********************************************************************

rom_bios        segment at 0f000h

                assume ds:rom_bios

                org 0fffeh

system_id       label byte

rom_bios        ends

;***********************************************************************

                ; main program code

;***********************************************************************

cseg            segment

                assume cs:cseg,ds:cseg,es:cseg,ss:cseg

                org 0100h
entry_point:
                jmp start               ; skip over data area

;***********************************************************************

                ; format data area

;***********************************************************************

                ; format sector tables
sector_table_0:
                db 000h,000h,001h,002h
                db 000h,000h,002h,002h
                db 000h,000h,003h,002h
                db 000h,000h,004h,002h
                db 000h,000h,005h,002h
                db 000h,000h,006h,002h
                db 000h,000h,007h,002h
                db 000h,000h,008h,002h
                db 000h,000h,009h,002h
                db 000h,000h,00Ah,002h
                db 000h,000h,00Bh,002h
                db 000h,000h,00Ch,002h
                db 000h,000h,00Dh,002h
                db 000h,000h,00Eh,002h
                db 000h,000h,00Fh,002h
                db 000h,000h,010h,002h
                db 000h,000h,011h,002h
                db 000h,000h,012h,002h

sector_table_1:
                db 000h,001h,001h,002h
                db 000h,001h,002h,002h
                db 000h,001h,003h,002h
                db 000h,001h,004h,002h
                db 000h,001h,005h,002h
                db 000h,001h,006h,002h
                db 000h,001h,007h,002h
                db 000h,001h,008h,002h
                db 000h,001h,009h,002h
                db 000h,001h,00Ah,002h
                db 000h,001h,00Bh,002h
                db 000h,001h,00Ch,002h
                db 000h,001h,00Dh,002h
                db 000h,001h,00Eh,002h
                db 000h,001h,00Fh,002h
                db 000h,001h,010h,002h
                db 000h,001h,011h,002h
                db 000h,001h,012h,002h

;***********************************************************************

temp_size:      db 4 dup (?)
binary_size:    dw ?
verify:         db 0            ; default is no verification
system:         db 0            ; default is no system file transfer
prompt:         db 1            ; default is prompt for floppy
drive_number:   db ?
disk_drive:     dw ?
max_track:      db ?
retry_count:    db ?
write_track:    dw ?
write_head:     db ?
write_sector:   db ?
cursor:         dw ?
stack_size:     dw 2048         ; used in buffer size calculation
minimum_buffer: dw 4096         ; minimum file copy buffer size
buffer_size:    dw ?            ; actual file copy buffer size

;***********************************************************************

disk_size:      db ?

                ; 1 = 360kb
                ; 2 = 1.2Mb
                ; 3 = 720kb
                ; 4 = 1.44Mb

drive_type:     db ?

                ; 0 = no drive
                ; 1 = 360kb
                ; 2 = 1.2Mb
                ; 3 = 720kb
                ; 4 = 1.44Mb

;***********************************************************************

parameter_pointer:

        dd ?    ; storage for the original disk parameter table pointer

;***********************************************************************

floppy_table:
                ; 360 Kb

                db 0FDh         ; media descriptor
                db 40           ; number of tracks
                dw 9            ; sectors per track
                dw 112          ; entries per root directory
                db 7            ; sectors per root directory
                db 2            ; sectors per cluster
                dw 2            ; sectors per FAT

                ; 1.2 Mb

                db 0F9h         ; media descriptor
                db 80           ; number of tracks
                dw 15           ; sectors per track
                dw 224          ; entries per root directory
                db 14           ; sectors per root directory
                db 1            ; sectors per cluster
                dw 7            ; sectors per FAT

                ; 720 Kb

                db 0F9h         ; media descriptor
                db 80           ; number of tracks
                dw 9            ; sectors per track
                dw 112          ; entries per root directory
                db 7            ; sectors per root directory
                db 2            ; sectors per cluster
                dw 3            ; sectors per FAT

                ; 1.44 Mb

                db 0F0h         ; media descriptor
                db 80           ; number of tracks
                dw 18           ; sectors per track
                dw 224          ; entries per root directory
                db 14           ; sectors per root directory
                db 1            ; sectors per cluster
                dw 9            ; sectors per FAT

;***********************************************************************

parameter_table:

        ; 360 Kb

        db 0D2h,002h,009h,002h,009h,02Ah,0FFh,050h,0F6h,001h,001h

        ; 1.2 Mb

        db 082h,002h,009h,002h,00Fh,01Bh,0FFh,054h,0F6h,001h,001h

        ; 720 Kb

        db 012h,002h,009h,002h,009h,02Ah,0FFh,050h,0F6h,001h,001h

        ; 1.44 Mb

        db 012h,002h,009h,002h,012h,01Bh,0FFh,06Ch,0F6h,001h,001h

;***********************************************************************

        ; informational messages

;***********************************************************************

cr_lf:
        db 00Dh,00Ah
        db "$"

insert_floppy:

        db "Put desired floppy in drive "

    insert_drive:

        db ?
        db ": and press any key"
        db "$"

clear_line:

        db 50 dup (008h)
        db 50 dup (020h)
        db 50 dup (008h)
        db "$"

format_display:

        db 26 dup (008h)
        db "Formatting track "

    format_track_number:

        db 2 dup (?)
        db " head "

    format_head_number:

        db ?
        db "$"

verify_display:

        db 26 dup (008h)
        db "Verifying  track "

    verify_track_number:

        db 2 dup (?)
        db " head "

    verify_head_number:

        db ?
        db "$"

boot_message:

        db 26 dup (008h)
        db 26 dup (020h)
        db 26 dup (008h)
        db "Writing boot record"
        db "$"

fat_message:

        db 19 dup (008h)
        db 19 dup (020h)
        db 19 dup (008h)
        db "Writing FATs"
        db "$"

dir_message:

        db 12 dup (008h)
        db 12 dup (020h)
        db 12 dup (008h)
        db "Writing root directory"
        db "$"

file_copy_message:

        db 22 dup (008h)
        db 22 dup (020h)
        db 22 dup (008h)
        db "Copying system files"
        db "$"

exit_message:

        db 22 dup (008h)
        db 22 dup (020h)
        db 22 dup (008h)
        db "Format complete"
        db 00Dh,00Ah
        db "$"

;***********************************************************************

        ; error messages

;***********************************************************************

computer_message:

        db 00Dh,00Ah,"IBM AT or compatible computer required",00Dh,00Ah
        db "$"

no_drive_error:

        db 00Dh,00Ah,"No such drive in system",00Dh,00Ah
        db "$"

reset_error:

        db 00Dh,00Ah,"Disk controller reset error",00Dh,00Ah
        db "$"

set_drive_error:

        db 00Dh,00Ah,"Disk drive type not supported by BIOS",00Dh,00Ah
        db "$"

no_floppy_error:

        db 00Dh,00Ah,"No floppy in drive",00Dh,00Ah
        db "$"

set_disk_error:

        db 00Dh,00Ah,"Disk / Drive combination not supported",00Dh,00Ah
        db "$"

write_boot_error:

        db 00Dh,00Ah,"Error writing boot record",00Dh,00Ah
        db "$"

write_fat_error:

        db 00Dh,00Ah,"Error writing FAT",00Dh,00Ah
        db "$"

write_dir_error:

        db 00Dh,00Ah,"Error writing directory",00Dh,00Ah
        db "$"

write_protect_error:

        db 00Dh,00Ah,"Disk is write protected",00Dh,00Ah
        db "$"

error_during_format:

        db 00Dh,00Ah,"Error during format",00Dh,00Ah
        db "$"

disk_type_error:

        db 00Dh,00Ah,"Probably wrong floppy type",00Dh,00Ah
        db "$"

verify_error:

        db 00Dh,00Ah,"Verification error",00Dh,00Ah
        db "$"

buffer_space_error:

        db 00Dh,00Ah,"No room for file copy buffer",00Dh,00Ah
        db "$"

help_message:

db 00Dh,00Ah,"FMT version 1.00, Copyright (C) 1990 Clair Alan Hardesty"
db 00Dh,00Ah
db 00Dh,00Ah," syntax: fmt d: size [v] [s] [n]"
db 00Dh,00Ah
db 00Dh,00Ah,"  where: d = drive (A or B), size = floppy size in Kb,"
db 00Dh,00Ah
db 00Dh,00Ah,"   the optional v enables format verification,"
db 00Dh,00Ah
db 00Dh,00Ah,"   the optional s copies the system files,"
db 00Dh,00Ah
db 00Dh,00Ah,"   and the optional n bypasses the `insert floppy' prompt"
db 00Dh,00Ah
db 00Dh,00Ah,"  supported sizes are:"
db 00Dh,00Ah
db 00Dh,00Ah,"   360 for a 360 Kb floppy in a 360 Kb or 1.2 Mb drive"
db 00Dh,00Ah,"   720 for a 720 Kb floppy in a 720 Kb or 1.44 Mb drive"
db 00Dh,00Ah,"   1200 for a 1.2 Mb floppy in a 1.2 Mb drive"
db 00Dh,00Ah,"   1440 for a 1.44 Mb floppy in a 1.44 Mb drive"
db 00Dh,00Ah
db "$"

;***********************************************************************

                ; check cpu and computer type

;***********************************************************************

start:
                ; check CPU for 80286 or above

                xor     ax,ax
                push    ax
                popf
                pushf
                pop     ax
                and     ax,08000h
                cmp     ax,08000h

                jnz two_eighty_six

                mov dx,offset computer_message
                mov ah,09h

                int 21h

                ; exit

                jmp short not_an_at
two_eighty_six:
                ; check the model byte in ROM

                push ds
                mov ax,rom_bios
                mov ds,ax

                assume ds:rom_bios

                cmp system_id,0fch      ; AT or compatible

                je at_or_better

                cmp system_id,0fah      ; most P/S-2 (80286) models

                je at_or_better

                cmp system_id,0f8h      ; P/S-2 model 80

                je at_or_better

                ; computer is not AT compatible

                pop ds

                assume ds:cseg

                mov dx,offset computer_message
                mov ah,09h

                int 21h
not_an_at:
                ; set exit code to 6 (not an AT or compatible)

                mov ax,4c06h

                int 21h

at_or_better:

;***********************************************************************

.286            ; enable the 80286 instruction set

;***********************************************************************

                pop ds

                assume ds:cseg

                ; initialize some variables

                mov cl,0                ; parameter counter
                mov dl,0                ; disk size digit counter
                mov di,offset temp_size ; temporary disk size storage
                mov bx,0080h            ; pointer to command line

;***********************************************************************

                ; parse the command line

;***********************************************************************

parse_loop:
                inc bx
                cmp byte ptr [bx],0dh   ; carriage return

                jne not_finished

                jmp got_command
not_finished:
                cmp byte ptr [bx],' '   ; space

                je parse_loop

                cmp byte ptr [bx],09h   ; tab

                je parse_loop

                cmp cl,0

                jg number_check

                and byte ptr [bx],5fh   ; capitalize drive letter
                cmp byte ptr [bx],'A'

                jnl check_for_b

                jmp help                ; invalid drive
check_for_b:
                cmp byte ptr [bx],'B'

                jng store_drive

                jmp help                ; invalid drive
store_drive:
                mov al,byte ptr [bx]
                sub al,'A'
                mov byte ptr [drive_number],al
colon_check:
                cmp byte ptr [bx+1],':'

                je found_colon

                jmp help                ; no colon after drive letter
found_colon:
                inc bx                  ; skip over the colon
                inc cl                  ; increment parameter count

                jmp parse_loop
number_check:
                cmp cl,1

                jg check_option

                cmp byte ptr [bx],'0'

                jnl maybe_a_number

                jmp help                ; not a number
maybe_a_number:
                cmp byte ptr [bx],'9'

                jng is_a_number

                jmp help                ; not a number
is_a_number:
                cmp byte ptr [bx+1],0Dh ; next character is a return

                je last_digit

                cmp byte ptr [bx+1],' ' ; next character is a space

                je last_digit

                cmp byte ptr [bx+1],09h ; next character is a tab

                jne store_digit
last_digit:
                inc cl                  ; increment parameter count
store_digit:
                mov al,byte ptr [bx]
                sub al,'0'              ; convert ASCII to BCD
                mov byte ptr [di],al
                inc di
                inc dl
                cmp dl,4

                jle jump_to_parse

                jmp help                ; too many digits in disk size
jump_to_parse:
                jmp parse_loop
check_option:
                cmp cl,5

                jng parse_option

                jmp help                ; too many parameters
parse_option:
                and byte ptr [bx],5fh
                cmp byte ptr [bx],'V'

                je set_verify

                cmp byte ptr [bx],'S'

                je set_system

                cmp byte ptr [bx],'N'

                je clear_prompt

                jmp help                ; invalid option
set_verify:
                mov byte ptr [verify],1 ; set format verify true
                inc cl

                jmp parse_loop
set_system:
                mov byte ptr [system],1 ; set transfer system true
                inc cl

                jmp parse_loop
clear_prompt:
                mov byte ptr [prompt],0 ; set transfer system true
                inc cl

                jmp parse_loop
got_command:
                cmp cl,2

                jnl two_parameters

                jmp help                ; too few parameters
two_parameters:
                cmp dl,3

                jl help                 ; too few digits in disk size

                ; convert the size parameter from BCD to binary

                mov cl,dl
                dec di
                xor ah,ah
                mov al,byte ptr [di]
                mov word ptr [binary_size],ax
                dec di
                dec cl
                xor ah,ah
                mov al,byte ptr [di]
                imul ax,10
                add word ptr [binary_size],ax
                dec di
                dec cl
                xor ah,ah
                mov al,byte ptr [di]
                imul ax,100
                add word ptr [binary_size],ax
                dec di
                dec cl

                jz test_size_360

                xor ah,ah
                mov al,byte ptr [di]
                imul ax,1000
                add word ptr [binary_size],ax
test_size_360:
                cmp word ptr [binary_size],360

                jne test_size_1200              ; not 360 Kb

                mov byte ptr [disk_size],1

                jmp short set_up                ; command line is valid
test_size_1200:
                cmp word ptr [binary_size],1200

                jne test_size_720               ; not 1.2 Mb

                mov byte ptr [disk_size],2

                jmp short set_up                ; command line is valid
test_size_720:
                cmp word ptr [binary_size],720

                jne test_size_1440              ; not 720 Kb

                mov byte ptr [disk_size],3

                jmp short set_up                ; command line is valid
test_size_1440:
                cmp word ptr [binary_size],1440

                jne help                        ; not 1.44 Mb

                mov byte ptr [disk_size],4

                jmp short set_up                ; command line is valid
help:
                ; command line is invalid or null

                mov dx,offset help_message
                mov ah,09h

                int 21h

                ; set exit code to 4 (null or invalid command line)

                mov ax,4c04h

                int 21h

;***********************************************************************

                ; set up the drive parameters and the parameter table

;***********************************************************************

set_up:
                ; if transfering system, check file copy buffer space

                cmp byte ptr [system],1

                jne cursor_off

                ; calculate copy buffer size

                mov ax,sp
                cmp ax,offset file_buffer

                jc no_buffer

                sub ax,offset file_buffer
                sbb ax,word ptr [stack_size]

                jc no_buffer

                cmp ax,word ptr [minimum_buffer]

                jnc buffer_ok
no_buffer:
                mov dx,offset buffer_space_error
                mov ah,09h

                int 21h

                ; set exit code to 7 (no room for file copy buffer)

                mov ax,4c07h

                int 21h
buffer_ok:
                mov word ptr [buffer_size],ax
cursor_off:
                ; save the current cursor and then turn the cursor off

                mov ah,03h

                int 10h

                mov word ptr [cursor],cx
                or cx,2000h
                mov ah,01h

                int 10h

                ; issue a carrriage-return / line-feed to stdout

                mov dx,offset cr_lf
                mov ah,09h

                int 21h

                ; check for prompt bypass

                cmp byte ptr [prompt],1

                jne no_prompt

                ; prompt for a floppy

                mov dx,offset insert_floppy
                mov ah,09h
                mov al,byte ptr [drive_number]
                add al,41h
                mov byte ptr [insert_drive],al

                int 21h

                ; wait for a key press

                xor ah,ah

                int 16h

                ; clear the message line

                mov dx,offset clear_line
                mov ah,09h
                int 21h
no_prompt:
                ; get disk drive information

                push es
                push di
                push si
                mov ah,08h
                mov dl,byte ptr [drive_number]

                int 13h

                cmp bl,0

                jne drive_exists

                mov dx,offset no_drive_error
                mov ah,09h

                int 21h

                ; set exit code to 3 (drive does not exist)

                pop ax
                pop ax
                pop ax
                mov ax,4c03h

                int 21h
drive_exists:
                mov byte ptr [max_track],ch
                mov byte ptr [sectors_track],cl
                mov byte ptr [drive_type],bl
                mov ah,byte ptr [disk_size]
                mov al,bl
                mov word ptr [disk_drive],ax

                ; check for floppy size less than drive maximum capacity

                ; check for a 360 Kb floppy in a 1.2 Mb drive

                cmp word ptr [disk_drive],0102h

                je forty_tracks

                ; check for a 720 Kb floppy in a 1.44 Mb drive

                cmp word ptr [disk_drive],0304h

                je nine_sectors

                ; check for improper disk / drive cominations

                cmp word ptr [disk_drive],0201h

                je bad_size                     ; 1.2 Mb in 360 Kb

                cmp word ptr [disk_drive],0301h

                je bad_size                     ; 720 Kb in 360 Kb

                cmp word ptr [disk_drive],0401h

                je bad_size                     ; 1.44 Mb in 360 Kb

                cmp word ptr [disk_drive],0302h

                je bad_size                     ; 720 Kb in 1.2 Mb

                cmp word ptr [disk_drive],0402h

                je bad_size                     ; 1.44 Mb in 1.2 Mb

                cmp word ptr [disk_drive],0103h

                je bad_size                     ; 360 Kb in 720 Kb

                cmp word ptr [disk_drive],0203h

                je bad_size                     ; 1.2 Mb in 720 Kb

                cmp word ptr [disk_drive],0403h

                je bad_size                     ; 1.44 Mb in 720 Kb

                cmp word ptr [disk_drive],0104h

                je bad_size                     ; 360 Kb in 1.44 Mb

                cmp word ptr [disk_drive],0204h

                je bad_size                     ; 1.2 Mb in 1.44 Mb

                jmp short size_ok
bad_size:
                mov dx,offset set_disk_error
                mov ah,09h

                int 21h

                ; set exit code to 2 (disk / drive comination error)

                pop ax
                pop ax
                pop ax
                mov ax,4c02h

                int 21h
forty_tracks:
                mov byte ptr [max_track],39
nine_sectors:
                mov byte ptr [sectors_track],9
size_ok:
                ; save the original disk parameter table pointer

                xor ax,ax
                mov es,ax
                mov ax,word ptr es:[0078h]
                mov dx,word ptr es:[007ah]
                mov word ptr [parameter_pointer],ax
                mov word ptr [parameter_pointer+2],dx

                ; set the disk parameter table pointer for formatting

                xor ah,ah
                mov al,byte ptr [disk_size]
                dec ax
                imul ax,11
                add ax,offset parameter_table
                cli
                mov word ptr es:[0078h],ax
                mov word ptr es:[007ah],cs
                sti

                ; put the parameter table in the boot record

                push cs
                pop es
                mov si,ax
                mov di,offset disk_table
                mov cx,11
                cld

                rep movsb

                pop si
                pop di
                pop es

;***********************************************************************

                ; store the disk parameters in the boot record and FAT

;***********************************************************************

                push si

                ; calculate the offset to the parameters in floppy_table

                xor ah,ah
                mov al,byte ptr [disk_size]
                dec ax
                imul ax,10
                mov bx,ax
                mov si,offset floppy_table

                ; set the media descriptor byte

                mov al,byte ptr [si+bx]
                mov byte ptr [descriptor],al
                mov byte ptr [fat_record],al    ; first byte of FAT

                ; set the number of root directory entries

                mov ax,word ptr [si+bx+4]
                mov word ptr [directory_size],ax

                ; set the number of sectors per directory

                mov al,byte ptr [si+bx+6]
                mov byte ptr [sectors_directory],al

                ; set the number of sectors per cluster

                mov al,byte ptr [si+bx+7]
                mov byte ptr [sectors_cluster],al

                ; set the number of sectors per FAT

                mov ax,word ptr [si+bx+8]
                mov word ptr [sectors_fat],ax

                ; set the total number of sectors per disk

                xor dx,dx
                xor ah,ah
                mov al,byte ptr [max_track]
                inc ax
                mul word ptr [sectors_track]
                xor bh,bh
                mov bl,byte ptr [heads]
                mul bx
                mov word ptr [small_total],ax

                ; calculate the location of the first directory sector

                xor dx,dx
                xor ah,ah
                mov al,byte ptr [fats]
                mul word ptr [sectors_fat]
                add ax,word ptr [hidden_sectors]
                adc dx,word ptr [hidden_sectors+2]
                add ax,word ptr [reserved_sectors]
                adc dx,0

                ; store directory start sector (absolute sector number)

                mov word ptr [dir_start],ax
                mov word ptr [dir_start+2],dx

                ; calculate the location of the first data sector

                xor bh,bh
                mov bl,byte ptr [sectors_directory]
                add ax,bx
                adc dx,0

                ; store data start sector (absolute sector number)

                mov word ptr [data_start],ax
                mov word ptr [data_start+2],dx

                pop si

;***********************************************************************

                ; reset the disk controller

;***********************************************************************

reset:
                mov byte ptr [retry_count],3
reset_retry:
                xor ah,ah
                mov dl,byte ptr [drive_number]

                int 13h

                jnc short set_drive

                dec byte ptr [retry_count]

                jnz reset_retry

                mov dx,offset reset_error

                jmp write_error

;***********************************************************************

                ; set the drive type for format

;***********************************************************************

set_drive:
                mov byte ptr [retry_count],3
drive_retry:
                mov al,byte ptr [drive_type]
                cmp al,3

                jl five_inch

                mov al,4

                jmp short set_type
five_inch:
                cmp al,2

                jne low_density

                cmp byte ptr [disk_size],1

                jg high_density

                mov al,2

                jmp short set_type
high_density:
                mov al,3

                jmp short set_type
low_density:
                mov al,1
set_type:
                mov ah,17h

                int 13h

                jnc short set_disk

                cmp ah,80h

                jne floppy_exists

                mov dx,offset no_floppy_error

                jmp write_error
floppy_exists:
                dec byte ptr [retry_count]

                jnz drive_retry

                mov dx,offset set_drive_error

                jmp write_error

;***********************************************************************

                ; set the disk type for format

;***********************************************************************

set_disk:
                mov byte ptr [retry_count],3
disk_retry:
                push es
                push di
                mov ah,18h
                mov ch,byte ptr [max_track]
                mov cl,byte ptr [sectors_track]
                mov dl,byte ptr [drive_number]

                int 13h

                pop di
                pop es

                jnc format_disk

                dec byte ptr [retry_count]

                jnz disk_retry

                mov dx,offset set_disk_error

                jmp write_error

;***********************************************************************

                ; format the disk

;***********************************************************************

format_disk:
                ; set up the loop counter

                xor ch,ch
                mov cl,byte ptr [max_track]
                inc cx
format_loop:
                push cx

                call format_track

                jnc continue

                pop cx

                jmp write_error
continue:
                call next_track

                pop cx

                loop format_loop

;***********************************************************************

                ; set up the volume serial number in the boot record

;***********************************************************************

serial_number:
                ; get the time

                mov ah,2ch

                int 21h

                ; fiddle the serial number bytes (similar to DOS 4.0)
                ;
                ; I don't know why IBM does this.  It may be done to
                ;  hide the fact that the serial number is derived
                ;  from the system clock.
                ;
                ; I am not sure that the numbers that I have chosen are
                ;  the same as IBM's, but they are close.
                ;
                ; a more reasonable approach might be to simply add one
                ;  to each byte to avoid zero bytes in the serial number

                add cx,07c7h
                add dx,0707h

                ; store the serial number in the boot record

                mov word ptr [sn_low],cx
                mov word ptr [sn_high],dx

;***********************************************************************

                ; write the boot record and FAT1

;***********************************************************************

write_boot:
                mov dx,offset boot_message
                mov ah,09h

                int 21h

                mov byte ptr [retry_count],3
boot_retry:
                mov ax,0302h
                mov bx,offset boot_record
                mov cx,0001h
                xor dh,dh
                mov dl,byte ptr [drive_number]

                int 13h

                jnc short finish_fat1

                dec byte ptr [retry_count]

                jnz boot_retry

                mov dx,offset write_boot_error

                jmp write_error
finish_fat1:
                mov dx,offset fat_message
                mov ah,09h

                int 21h

                ; set the starting sector (absolute)

                mov ax,2
                xor dx,dx

                ; set the number of sectors to write

                mov cx,word ptr [sectors_fat]
                dec cx
fat1:
                push ax
                push cx
                push dx

                call write_ths

                jnc fat1_ths_ok

                pop ax
                pop ax
                pop ax

                mov dx,offset write_fat_error

                jmp write_error
fat1_ths_ok:
                mov bx,offset fat_dir_record
                mov byte ptr [retry_count],3
fat1_retry:
                call write_disk

                jnc fat1_write_ok

                dec byte ptr [retry_count]

                jnz fat1_retry

                pop ax
                pop ax
                pop ax

                mov dx,offset write_fat_error

                jmp write_error
fat1_write_ok:
                pop dx
                pop cx
                pop ax

                ; increment the absolute sector number

                add ax,1
                adc dx,0

                loop fat1

                jnc write_fat

                mov dx,offset write_fat_error

                jmp write_error

;***********************************************************************

                ; write FAT2

;***********************************************************************

write_fat:
                mov byte ptr [retry_count],3
fat_retry:
                mov ax,0301h
                mov bx,offset fat_record
                xor ch,ch
                mov cl,byte ptr [sectors_fat]
                add cl,2
                xor dh,dh
                mov dl,byte ptr [drive_number]

                int 13h

                jnc finish_fat2

                dec byte ptr [retry_count]

                jnz fat_retry

                mov dx,offset write_fat_error

                jmp write_error
finish_fat2:
                ; set the starting sector (absolute)

                mov ax,2
                xor dx,dx
                add ax,word ptr [sectors_fat]
                adc dx,0

                ; set the number of sectors to write

                mov cx,word ptr [sectors_fat]
                dec cx
fat2:
                push ax
                push cx
                push dx

                call write_ths

                jnc fat2_ths_ok

                pop ax
                pop ax
                pop ax

                mov dx,offset write_fat_error

                jmp write_error
fat2_ths_ok:
                mov bx,offset fat_dir_record
                mov byte ptr [retry_count],3
fat2_retry:
                call write_disk

                jnc fat2_write_ok

                dec byte ptr [retry_count]

                jnz fat2_retry

                pop ax
                pop ax
                pop ax

                mov dx,offset write_fat_error

                jmp short write_error
fat2_write_ok:
                pop dx
                pop cx
                pop ax

                ; increment the absolute sector number

                add ax,1
                adc dx,0

                loop fat2

                jnc write_dir

                mov dx,offset write_fat_error

                jmp short write_error

;***********************************************************************

                ; write the directory

;***********************************************************************

write_dir:
                mov dx,offset dir_message
                mov ah,09h

                int 21h

                ; set the starting sector (absolute)

                xor dx,dx
                mov ax,word ptr [sectors_fat]
                mul byte ptr [fats]
                add ax,word ptr [reserved_sectors]
                adc dx,0

                ; set the number of sectors to write

                xor ch,ch
                mov cl,byte ptr [sectors_directory]
dir:
                push ax
                push cx
                push dx

                call write_ths

                jnc dir_ths_ok

                pop ax
                pop ax
                pop ax

                mov dx,offset write_dir_error

                jmp short write_error
dir_ths_ok:
                mov bx,offset fat_dir_record
                mov byte ptr [retry_count],3
dir_retry:
                call write_disk

                jnc dir_write_ok

                dec byte ptr [retry_count]

                jnz dir_retry

                pop ax
                pop ax
                pop ax

                mov dx,offset write_dir_error

                jmp short write_error
dir_write_ok:
                pop dx
                pop cx
                pop ax

                ; increment the absolute sector number

                add ax,1
                adc dx,0

                loop dir

                jnc transfer

                mov dx,offset write_dir_error

                jmp short write_error
transfer:
                cmp byte ptr [system],1

                jne exit

                ; transfer the system files

                call load_system

                jnc exit

                ; set exit code to 5 (error copying system files)

                mov ax,4c05h
                push ax

                jmp short error_exit
write_error:
                mov ah,09h

                int 21h

                ; set exit code to 1 (format, verify, or write error)

                mov ax,4c01h
                push ax

                jmp short error_exit

;***********************************************************************

                ; clean up and exit

;***********************************************************************

exit:
                ; set exit code to 0 (Success)

                mov ax,4c00h
                push ax
error_exit:
                ; restore the original disk parameter table pointer

                mov cx,word ptr [parameter_pointer]
                mov dx,word ptr [parameter_pointer+2]
                push ds
                xor ax,ax
                mov ds,ax

                assume ds:0000h

                cli
                mov word ptr ds:[0078h],cx
                mov word ptr ds:[007ah],dx
                sti
                pop ds

                assume ds:cseg

                ; park the heads on track 0

                mov ah,0ch
                xor cl,cl
                xor dh,dh
                mov dl,byte ptr [drive_number]

                int 13h

                pop ax
                push ax
                cmp al,0

                jne error_out

                ; display format complete message

                mov dx,offset exit_message
                mov ah,09h

                int 21h
error_out:
                ; restore the cursor

                mov cx,word ptr [cursor]
                mov ah,01h

                int 10h

                ; terminate with exit code (0 = Success, else = Failure)

                pop ax

                int 21h

;***********************************************************************

                ; format subroutines

;***********************************************************************

;***********************************************************************

                ; information display during format

;***********************************************************************

display_format:
                xor ah,ah
                aam
                or ax,3030h
                mov byte ptr [format_track_number+1],al
                cmp ah,30h

                jne format_digit

                mov ah,20h
format_digit:
                mov byte ptr [format_track_number],ah
                mov al,bl
                xor ah,ah
                aam
                or ax,3030h
                mov byte ptr [format_head_number],al
                mov dx,offset format_display
                mov ah,09h

                int 21h

                ret

;***********************************************************************

                ; information display during verification

;***********************************************************************

display_verify:
                xor ah,ah
                aam
                or ax,3030h
                mov byte ptr [verify_track_number+1],al
                cmp ah,30h

                jne verify_digit

                mov ah,20h
verify_digit:
                mov byte ptr [verify_track_number],ah
                mov al,bl
                xor ah,ah
                aam
                or ax,3030h
                mov byte ptr [verify_head_number],al
                mov dx,offset verify_display
                mov ah,09h

                int 21h

                ret

;***********************************************************************

                ; increment the track number in the sector tables

;***********************************************************************

next_track:
                xor ch,ch
                mov cl,36
                mov bx,offset sector_table_0
track_counter:
                inc byte ptr [bx]
                add bx,4

                loop track_counter

                ret

;***********************************************************************

                ; format (and possibly verify) both heads on one track

;***********************************************************************

format_track:
                mov al,byte ptr [sector_table_0]
                mov bl,0

                ; display formatting message

                call display_format

                mov byte ptr [retry_count],3
head_0_retry:
                mov ah,5
                mov al,byte ptr [sectors_track]
                mov bx,offset sector_table_0
                mov ch,byte ptr [bx]
                mov cl,byte ptr [bx+2]
                mov dh,byte ptr [bx+1]
                mov dl,byte ptr [drive_number]

                int 13h

                jnc short head_0_ok

                cmp ah,80h

                jne no_timeout

                mov dx,offset no_floppy_error
                stc
                ret
no_timeout:
                cmp ah,03h

                jne not_protected

                mov dx,offset write_protect_error
                stc
                ret
not_protected:
                dec byte ptr [retry_count]

                jnz head_0_retry

                mov dx,offset error_during_format
                stc
                ret
head_0_ok:
                cmp byte ptr [verify],1

                je verify_head_0

                ; check for proper floppy type
                ;
                ; this is done via a verification of tracks 0,
                ;  59, and 79, for head 0 only, for all 1.2 Mb disks
                ;  not being verified, and on track 0 head 0 only, for
                ;  360 Kb, 720 Kb and 1.44 Mb disks not being verified.
                ;
                ; the following combinations will fail at track 0:
                ;
                ; 1: trying to format a 1.2 Mb floppy to 360 Kb
                ; 2: trying to format a 720 Kb floppy to 1.44 Mb *
                ; 3: trying to format a 1.44 Mb floppy to 720 Kb * !
                ;
                ; the following combination will fail at track 79:
                ;
                ; 1: trying to format a 360 Kb floppy to 1.2 Mb (it will
                ;     probably fail sooner, but it is sure to here)
                ;
                ; notes:
                ;
                ; * - these combinations may not fail if the drive-
                ;      controller combination does not properly detect
                ;      the the floppy type.
                ;
                ; ! - this combination may not fail in 720 Kb drives.
                ;
                ; While certain non-standard combinations may not fail,
                ;  either the data on the disk may be at risk, or your
                ;  pocketbook may suffer, if you insist on using them.

                cmp byte ptr [sector_table_0],0

                je verify_type

                cmp byte ptr [disk_size],2

                je one_point_two

                jmp short format_head_1
one_point_two:
                cmp byte ptr [sector_table_0],59

                je verify_type

                cmp byte ptr [sector_table_0],79

                je verify_type

                jmp short format_head_1
verify_type:
                mov ah,04h

                int 13h

                jnc format_head_1

                mov dx,offset disk_type_error
                stc
                ret
verify_head_0:
                pushf
                pusha
                mov al,byte ptr [sector_table_0]
                mov bl,0

                ; display verifying message

                call display_verify

                popa
                popf
                mov ah,04h

                int 13h

                jnc format_head_1

                mov dx,offset verify_error
                stc
                ret
format_head_1:
                pushf
                pusha
                mov al,byte ptr [sector_table_0]
                mov bl,1

                ; display formatting message

                call display_format

                popa
                popf
                mov byte ptr [retry_count],3
head_1_retry:
                mov ah,5
                mov al,byte ptr [sectors_track]
                mov bx,offset sector_table_1
                mov ch,byte ptr [bx]
                mov cl,byte ptr [bx+2]
                mov dh,byte ptr [bx+1]
                mov dl,byte ptr [drive_number]

                int 13h

                jnc short head_1_ok

                dec byte ptr [retry_count]

                jnz head_1_retry

                mov dx,offset error_during_format
                stc
                ret
head_1_ok:
                cmp byte ptr [verify],1

                je verify_head_1
format_return:
                clc
                ret
verify_head_1:
                pushf
                pusha
                mov al,byte ptr [sector_table_0]
                mov bl,1

                ; display verifying message

                call display_verify

                popa
                popf
                mov ah,04h

                int 13h

                jnc format_return

                mov dx,offset verify_error
                stc
                ret

;***********************************************************************

                ; calculate the track, head, and sector for a disk write

;***********************************************************************

write_ths:
                ; check for a valid absolute sector number

                cmp dx,word ptr [sectors_track]

                jb write_ths_ok

                stc
                ret
write_ths_ok:
                ; calculate the sector number

                div word ptr [sectors_track]
                inc dl

                ; store the sector number

                mov byte ptr [write_sector],dl

                ; calculate the head number and the track number

                xor dx,dx
                div word ptr [heads]

                ; store the head number

                mov byte ptr [write_head],dl

                ; store the track number

                mov word ptr [write_track],ax

                clc
                ret

;***********************************************************************

                ; write one sector to the disk

;***********************************************************************

write_disk:
                ; get the track number

                mov dx,word ptr [write_track]

                ; shift the upper 2 bits of the track number
                ; into the upper 2 bits of the sector number

                mov cl,06
                shl dh,cl

                ; get the sector number

                or dh,byte ptr [write_sector]

                mov cx,dx
                xchg ch,cl

                ; get the drive number

                mov dl,byte ptr [drive_number]

                ; get the head number

                mov dh,byte ptr [write_head]

                ; write one sector to the disk

                mov ax,0301h

                int 13h

                ret

;***********************************************************************

                ; transfer system files

;***********************************************************************

bios_source_path:

        db "d:\"

    bios_source_filename:

        db "IBMBIO.COM",000h,008h
        db "$"

bios_destination_path:

        db "d:\"

    bios_destination_filename:

        db "IBMBIO.COM",000h,008h
        db "$"

dos_source_path:

        db "d:\"

    dos_source_filename:

        db "IBMDOS.COM",000h,008h
        db "$"

dos_destination_path:

        db "d:\"

    dos_destination_filename:

        db "IBMDOS.COM",000h,008h
        db "$"

cmd_source_path:

        db "d:\"

    cmd_source_filename:

        db "COMMAND.COM",000h,008h
        db "$"

cmd_destination_path:

        db "d:\"

    cmd_destination_filename:

        db "COMMAND.COM",000h,008h
        db "$"

load_system:
                mov dx,offset file_copy_message
                mov ah,09h

                int 21h

                ; get default drive

                mov ah,19h

                int 21h

                ; set source drive

                add al,41h
                mov byte ptr [bios_source_path],al
                mov byte ptr [dos_source_path],al
                mov byte ptr [cmd_source_path],al

                ; set destination drive

                mov al,byte ptr [drive_number]
                add al,41h
                mov byte ptr [bios_destination_path],al
                mov byte ptr [dos_destination_path],al
                mov byte ptr [cmd_destination_path],al

                ; copy IBMBIOS.COM

                mov bx,offset file_buffer
                mov cx,word ptr [buffer_size]
                mov si,offset bios_source_path
                mov di,offset bios_destination_path

                call file_copy

                jc load_error

                ; copy IBMDOS.COM

                mov bx,offset file_buffer
                mov cx,word ptr [buffer_size]
                mov si,offset dos_source_path
                mov di,offset dos_destination_path

                call file_copy

                jc load_error

                ; copy COMMAND.COM

                mov bx,offset file_buffer
                mov cx,word ptr [buffer_size]
                mov si,offset cmd_source_path
                mov di,offset cmd_destination_path

                call file_copy

                jc load_error

                clc
                ret
load_error:
                stc
                ret

;***********************************************************************

                ; copy a file
                ;
                ; input:
                ;
                ;  bx = pointer to copy buffer
                ;  cx = copy buffer size in bytes
                ;  si = pointer to source path\filename
                ;  di = pointer to destination path\filename

;***********************************************************************

source:                 dw ?
destination:            dw ?
read_handle:            dw ?
write_handle:           dw ?
file_time:              dw ?
file_date               dw ?
file_attr:              dw ?
bytes_read:             dw ?
copy_buffer_size:       dw ?
copy_buffer:            dw ?

; file copy messages

file_not_found:

        db " - error opening input file",00Dh,00Ah
        db "$"

file_create_error:

        db " - error creating output file",00Dh,00Ah
        db "$"

file_read_error:

        db " - error reading file",00Dh,00Ah
        db "$"

file_write_error:

        db " - error writing file",00Dh,00Ah
        db "$"

file_copy:
                mov word ptr [copy_buffer],bx
                mov word ptr [copy_buffer_size],cx
                mov word ptr [source],si
                mov word ptr [destination],di
                mov dx,word ptr [source]
                mov ah,3dh
                xor al,al

                int 21h                         ; open source file

                mov word ptr [read_handle],ax

                jnc file_open

                ; display file not found message

                mov dx,offset cr_lf

                call write_message

                mov dx,word ptr [source]

                call write_message

                mov dx,offset file_not_found

                call write_message

                jmp load_error
file_open:
                mov ax,5700h
                mov bx,word ptr [read_handle]

                int 21h                         ; get file time and date

                mov word ptr [file_time],cx
                mov word ptr [file_date],dx
                mov ax,4300h
                mov dx,word ptr [source]

                int 21h                         ; get file attributes

                mov word ptr [file_attr],cx
                mov al,byte ptr [drive_number]
                mov cx,0000h
                mov dx,word ptr [destination]
                mov ah,3ch

                int 21h                         ; create destination file

                mov word ptr [write_handle],ax

                jnc copy_cmd

                ; display file not created message

                mov dx,offset cr_lf

                call write_message

                mov dx,word ptr [destination]

                call write_message

                mov dx,offset file_create_error

                call write_message

                jmp copy_error
copy_cmd:
                call read_file

                jnc file_read_ok

                ; display file read error message

                mov dx,offset cr_lf

                call write_message

                mov dx,word ptr [source]

                call write_message

                mov dx,offset file_read_error

                call write_message

                jmp copy_error
file_read_ok:
                call write_file

                jnc file_write_ok

                ; display file write error message

                mov dx,offset cr_lf

                call write_message

                mov dx,word ptr [destination]

                call write_message

                mov dx,offset file_write_error

                call write_message

                jmp copy_error
file_write_ok:
                cmp ax,word ptr [copy_buffer_size]

                je copy_cmd

                mov bx,word ptr [read_handle]
                mov ah,3eh

                int 21h                         ; close read_file

                mov ax,5701h
                mov bx,word ptr [write_handle]
                mov cx,word ptr [file_time]
                mov dx,word ptr [file_date]

                int 21h                         ; set file time and date

                mov bx,word ptr [write_handle]
                mov ah,3eh

                int 21h                         ; close write_file

                mov ax,4301h
                mov cx,word ptr [file_attr]
                mov dx,word ptr [destination]

                int 21h                         ; set file attributes

                clc
                ret
copy_error:
                stc
                ret

;***********************************************************************

                ; issue a file copy error message

;***********************************************************************

write_message:
                mov ah,09h

                int 21h

                ret

;***********************************************************************

                ; read file block into buffer

;***********************************************************************

read_file:
                mov cx,word ptr [copy_buffer_size]
                mov bx,word ptr [read_handle]
                mov dx,word ptr [copy_buffer]
                mov ah,3fh

                int 21h

                mov word ptr [bytes_read],ax
                mov cx,ax
                ret

;***********************************************************************

                ; write file block from buffer

;***********************************************************************

write_file:
                mov bx,word ptr [write_handle]
                mov dx,word ptr [copy_buffer]
                mov ah,40h

                int 21h

                ret

;***********************************************************************

                ; boot sector data (track 0, head 0, sector 1)

;***********************************************************************

boot_record:

;***********************************************************************

.8086           ; enable the 8086 instruction set

;***********************************************************************

                jmp boot_strap          ; skip over data areas

;***********************************************************************

                ; DOS data area

;***********************************************************************

oem_name:               db   "FMT 1.00"
bytes_sector:           dw   512
sectors_cluster:        db   ?          ; filled in before writing boot
reserved_sectors:       dw   1
fats:                   db   2
directory_size:         dw   ?          ; filled in before writing boot
small_total:            dw   ?          ; filled in before writing boot
descriptor:             db   ?          ; filled in before writing boot
sectors_fat:            dw   ?          ; filled in before writing boot
sectors_track:          dw   ?          ; filled in before writing boot
heads:                  dw   2
hidden_sectors:         dd   0
big_total:              dd   0
physical_drive:         db   0
reserved:               db   0
extended_boot:          db   029h
sn_low:                 dw   ?          ; filled in before writing boot
sn_high:                dw   ?          ; filled in before writing boot
volume_label:           db   "NO NAME    "
fat_type:               db   "FAT12   "

;***********************************************************************

                ; bootstrap program data area

;***********************************************************************

sectors_directory:      db ?            ; filled in before writing boot
dir_start:              dd ?            ; filled in before writing boot
data_start:             dd ?            ; filled in before writing boot
track:                  dw ?            ; boot program variable
head:                   db ?            ; boot program variable
sector:                 db ?            ; boot program variable
disk_table:             db 11 dup (?)   ; filled in before writing boot

bios_name:              db "IBMBIO  COM"

dos_name:               db "IBMDOS  COM"

                        ; boot program messages
reset_message:
                        db 00Dh,00Ah,00Ah
                        db "Disk controller error ..."
                        db 000h

system_message:
                        db 00Dh,00Ah,00Ah
                        db "No system on disk or disk read error ..."
                        db 000h

retry_message:
                        db 00Dh,00Ah,00Ah
                        db "Press any key to retry boot."
                        db 000h

;***********************************************************************

boot_strap:
                ; temporarily disable interrupts

                cli

                ; set the stack segment and the extra segment to 0000
                ; DOS loads the boot record at 0000:7c00,
                ; so put the stack just below it

                xor ax,ax
                mov es,ax
                mov ss,ax
                mov sp,7c00h

                assume es:0000h,ss:0000h

                ; set up the parameters used by the BIOS program loader

                lds si,es:[0078h]
                mov bx,0078h

                ; save current disk parameter table

                push ds
                push si
                push ss
                push bx

                ; set the data segment to 0000

                mov ds,ax

                assume ds:0000h

                ; use the disk parameter table in the floppy boot record

                mov word ptr [bx],7c00h+disk_table-boot_record
                mov word ptr [bx+02],ax

                ; enable interrupts

                sti

                ; reset the disk controller

                int 13h

                jnc reset_ok

                ; display reset error message

                mov si,7c00h+offset reset_message-offset boot_record

                call write_string

                jmp not_bootable
reset_ok:
                ; get directory start sector (absolute sector number)

                mov ax,word ptr ds:[7c00h+dir_start-boot_record]
                mov dx,word ptr ds:[7c00h+dir_start-boot_record+2]

                ; get track, head, and sector

                call calculate_ths

                jnc dir_sector_ok

                ; display system error message

                mov si,7c00h+offset system_message-offset boot_record

                call write_string

                jmp not_bootable
dir_sector_ok:
                ; set the offset of the disk read buffer

                mov bx,0500h

                ; read the first directory sector

                call read_disk

                jc not_bootable

                ; check directory for the BIOS file
                ; (it must be the first directory entry)

                mov di,bx
                mov cx,11
                mov si,7c00h+offset bios_name-offset boot_record

                rep cmpsb

                jz dir_read_ok

                ; display system error message

                mov si,7c00h+offset system_message-offset boot_record

                call write_string

                jmp not_bootable
dir_read_ok:
                ; check directory for the DOS file
                ; (it must be the second directory entry)

                lea di,[bx+32]
                mov si,7c00h+offset dos_name-offset boot_record
                mov cx,11

                rep cmpsb

                ; valid directory, attempt to boot from the disk

                jz system_found

                ; display system error message

                mov si,7c00h+offset system_message-offset boot_record

                call write_string

                jmp not_bootable

;***********************************************************************

system_found:
                ; get the data start sector (absolute sector number)

                mov ax,word ptr ds:[7c00h+data_start-boot_record]
                mov dx,word ptr ds:[7c00h+data_start-boot_record+2]

                ; set the offset of the disk read buffer

                mov bx,0700h

                ; read 3 sectors (the length of the BIOS program loader)

                mov cx,3
read_next:
                push ax
                push cx
                push dx

                ; get track, head, and sector

                call calculate_ths

                jnb ok_to_read

                ; clean up the stack

                pop ax
                pop ax
                pop ax

                ; display system error message

                mov si,7c00h+offset system_message-offset boot_record

                call write_string

                jmp not_bootable
ok_to_read:
                call read_disk

                pop dx
                pop cx
                pop ax

                jnc disk_read_ok

                ; display system error message

                mov si,7c00h+offset system_message-offset boot_record

                call write_string

                jmp not_bootable
disk_read_ok:
                ; increment the sector to read

                add ax,1
                adc dx,0

                ; bump the offset of the read buffer by the sector size

                add bx,word ptr ds:[7c00h+bytes_sector-boot_record]

                loop read_next

                ; set up the parameters for the BIOS program loader

                ; get the media descriptor

                mov ch,byte ptr ds:[7c00h+descriptor-boot_record]

                ; get the drive number

                mov dl,byte ptr ds:[7c00h+physical_drive-boot_record]

                ; get the data start sector (absolute sector number)

                mov ax,word ptr ds:[7c00h+data_start-boot_record+2]
                mov bx,word ptr ds:[7c00h+data_start-boot_record]

                ; execute the BIOS program loader

                jmp far ptr program_loader

;***********************************************************************

not_bootable:
                ; display retry message

                mov si,7c00h+offset retry_message-offset boot_record

                call write_string

                ; wait for a key to be pressed

                xor ah,ah

                int 16h

                ; clean up the stack

                pop si
                pop ds
                pop [si]
                pop [si+2]

                ; try to boot the system again

                int 19h

;***********************************************************************

                ; bootstrap subroutines

;***********************************************************************

write_string:
                ; get one character

                lodsb

                ; check for end of ASCIIZ string

                or al,al

                jnz next_char

                ret
next_char:
                ; write one character to the screen

                mov ah,0eh

                ; use video page 0, normal white

                mov bx,0007h

                int 10h

                jmp write_string

;***********************************************************************

read_disk:
                ; get the track number

                mov dx,word ptr ds:[7c00h+track-boot_record]

                ; shift the upper 2 bits of the track number
                ; into the upper 2 bits of the sector number

                mov cl,06
                shl dh,cl

                ; get the sector number

                or dh,byte ptr ds:[7c00h+sector-boot_record]

                mov cx,dx
                xchg ch,cl

                ; get the drive number

                mov dl,byte ptr ds:[7c00h+physical_drive-boot_record]

                ; get the head number

                mov dh,byte ptr ds:[7c00h+head-boot_record]

                ; read one sector from the disk

                mov ax,0201h

                int 13h

                ret

;***********************************************************************

calculate_ths:
                ; check for valid absolute sector number

                cmp dx,word ptr ds:[7c00h+sectors_track-boot_record]

                jb ths_ok

                stc
                ret
ths_ok:
                ; calculate the sector number

                div word ptr ds:[7c00h+sectors_track-boot_record]
                inc dl

                ; store the sector number

                mov byte ptr ds:[7c00h+sector-boot_record],dl

                ; calculate the head number and the track number

                xor dx,dx
                div word ptr ds:[7c00h+heads-boot_record]

                ; store the head number

                mov byte ptr ds:[7c00h+head-boot_record],dl

                ; store the track number

                mov word ptr ds:[7c00h+track-boot_record],ax

                clc
                ret

;***********************************************************************

                ; adjust the boot record size to 512 bytes

;***********************************************************************

fill:           db (512-(offset fill-offset boot_record)-2) dup (0)

;***********************************************************************

                ; boot sector signature

;***********************************************************************

signature:      dw 0AA55h

;***********************************************************************

                ; end of boot sector data

;***********************************************************************

;***********************************************************************

                ; FAT and directory data

;***********************************************************************

                ; the first FAT sector has the media descriptor byte
fat_record:
                db ?                    ; filled in before writing FAT
                db 0ffh,0ffh
                db 509 dup (0)

                ; other FAT sectors and directory sectors are all zeros
fat_dir_record:
                db 512 dup (0)

;***********************************************************************

                ; copyright information

;***********************************************************************

versn:          db "FMT version 1.00 23-JAN-1990",000h

copyright:      db "Copyright (C) 1990  by Clair Alan Hardesty",000h

;***********************************************************************

                ; put file copy buffer after code and other data

;***********************************************************************

file_buffer:
                ; buffer space starts here

cseg            ends

end             entry_point

;***********************************************************************

                ; end of Fmt.asm

;***********************************************************************
