;***********************************************************************
;*                                                                     *
;*  Fmt.asm                                             22-DEC-1990    *
;*                                                                     *
;*  Format Floppy Disk - version 1.30 - 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|R]                                  *
;*                                                                     *
;*    where: d = drive (A or B), size = floppy size in Kb,             *
;*                                                                     *
;*      the optional V enables format verification, the optional S     *
;*                                                                     *
;*      copies the system files, the optional N bypasses the prompt,   *
;*                                                                     *
;*      and the optional R enables recursive formatting with prompting *
;*                                                                     *
;*    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)   *
;*    8 = insufficient stack space for program                         *
;*    9 = program aborted by user (Control-C, Break, or Escape)        *
;*   10 = unknown drive type (CMOS says floppy type is greater than 4) *
;*   11 = memory allocation error (while attempting to reduce memory)  *
;*   12 = both N and R options specified (only one allowed)            *
;*                                                                     *
;***********************************************************************
;*                                                                     *
;*  In order to use the system transfer option, the following files    *
;*                                                                     *
;*    must be present in the root diectory of the default drive:       *
;*                                                                     *
;*      1: IBMBIO.COM or IO.SYS (normally a hidden file)               *
;*                                                                     *
;*      2: IBMDOS.COM or MSDOS.SYS (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.0 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                              *
;*  840 Colusa Street                                                  *
;*  Chico, CA 95928-4117                                               *
;*  TEL : (916) 342-2751                                               *
;*  FAX : (916) 345-7586                                               *
;*  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                                          *
;*                                                                     *
;***********************************************************************
;*                                                                     *
;*  Revision history:                                                  *
;*                                                                     *
;*    Version 1.00 : 23-JAN-1990                                       *
;*                                                                     *
;*      Initial public release                                         *
;*                                                                     *
;*    Version 1.10 : 03-FEB-1990                                       *
;*                                                                     *
;*      Fixed MASM bugs                                                *
;*                                                                     *
;*      Added MASM version 5.0 compatibility                           *
;*                                                                     *
;*      Improved compatibility for drive type determination            *
;*                                                                     *
;*      Added a break handler and `user abort' message                 *
;*                                                                     *
;*      Added some new exit codes                                      *
;*                                                                     *
;*      Improved the boot record code                                  *
;*                                                                     *
;*    Version 1.20 : 10-JUL-1990                                       *
;*                                                                     *
;*      Fixed serial number generation to match IBM format exactly     *
;*                                                                     *
;*      Fixed system file transfer to work with IBM or Microsoft DOS   *
;*                                                                     *
;*      Modified the user abort code to accept the ESCAPE, CONTROL-C,  *
;*                                                                     *
;*        or BREAK keys, and fixed some bugs in the user abort code    *
;*                                                                     *
;*      Added recursive formatting capability                          *
;*                                                                     *
;*    Version 1.30 : 22-DEC-1990                                       *
;*                                                                     *
;*      Comment clarifications                                         *
;*                                                                     *
;*      Minor code improvements                                        *
;*                                                                     *
;***********************************************************************

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

                ; program equates

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

TRUE            equ     01h
FALSE           equ     00h

ESC_KEY         equ     011bh
CTRL_C_KEY      equ     2e03h

NULL            equ     00h
BELL            equ     07h
BACKSPACE       equ     08h
TAB             equ     09h
LF              equ     0ah
CR              equ     0dh
SPACE           equ     20h

BOOT            equ     7c00h           ; boot loader base address

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

                ; set-up for interrupt vector table access

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

seg_zero        segment at 0000h

                assume es:seg_zero

                org 0000h

seg_zero        ends

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

                ; 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 0ffffh

                assume ds:rom_bios

                org 000eh

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

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

BCD_size:       db 4 dup (?)    ; disk size in BCD
binary_size:    dw ?            ; disk size in binary
verify:         db FALSE        ; default is no verification
system:         db FALSE        ; default is no system file transfer
prompt:         db TRUE         ; default is prompt to insert floppy
recurse:        db FALSE        ; default is no recursion
prmt_or_rcrs:   db FALSE        ; used to allow only one of the options
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 1024         ; minimum allowable stack space
minimum_buffer: dw 4096         ; minimum file copy buffer size
buffer_size:    dw ?            ; actual file copy buffer size
system_exists   db ?            ; boolean for system on default drive
is_ibm          db ?            ; boolean for PC-DOS or MS-DOS

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

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

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

stack_pointer:

        dw ?    ; storage for initial stack pointer (eases some exits)

parameter_pointer:

        dd ?    ; storage for the original disk parameter table pointer

control_break:

        dd ?    ; storage for the original control-break handler vector

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

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:

        ; uses some non-standard parameters for improved speed

        ; 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

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

                ; 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

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

        ; file copy data area

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

ibm_bios_source_path:

        db "d:\"

    ibm_bios_source_filename:

        db "IBMBIO.COM",NULL,BACKSPACE
        db "$"

ibm_bios_destination_path:

        db "d:\"

    ibm_bios_destination_filename:

        db "IBMBIO.COM",NULL,BACKSPACE
        db "$"

ibm_dos_source_path:

        db "d:\"

    ibm_dos_source_filename:

        db "IBMDOS.COM",NULL,BACKSPACE
        db "$"

ibm_dos_destination_path:

        db "d:\"

    ibm_dos_destination_filename:

        db "IBMDOS.COM",NULL,BACKSPACE
        db "$"

ms_bios_source_path:

        db "d:\"

    ms_bios_source_filename:

        db "IO.SYS",NULL,BACKSPACE
        db "$"

ms_bios_destination_path:

        db "d:\"

    ms_bios_destination_filename:

        db "IO.SYS",NULL,BACKSPACE
        db "$"

ms_dos_source_path:

        db "d:\"

    ms_dos_source_filename:

        db "MSDOS.SYS",NULL,BACKSPACE
        db "$"

ms_dos_destination_path:

        db "d:\"

    ms_dos_destination_filename:

        db "MSDOS.SYS",NULL,BACKSPACE
        db "$"

cmd_source_path:

        db "d:\"

    cmd_source_filename:

        db "COMMAND.COM",NULL,BACKSPACE
        db "$"

cmd_destination_path:

        db "d:\"

    cmd_destination_filename:

        db "COMMAND.COM",NULL,BACKSPACE
        db "$"

ibm_bios_name:

        db "IBMBIO  COM"

ibm_dos_name:

        db "IBMDOS  COM"

ms_bios_name:

        db "IO      SYS"

ms_dos_name:

        db "MSDOS   SYS"

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

        ; informational messages

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

cr_lf:
        db CR,LF
        db "$"

insert_floppy:

        db "Put desired floppy in drive "

    insert_drive:

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

initializing:

        db "Initializing"
        db "$"

clear_line:

        db 50 dup (BACKSPACE)
        db 50 dup (SPACE)
        db 50 dup (BACKSPACE)
        db "$"

format_display:

        db 26 dup (BACKSPACE)
        db "Formatting track "

    format_track_number:

        db 2 dup (?)
        db " head "

    format_head_number:

        db ?
        db "$"

verify_display:

        db 26 dup (BACKSPACE)
        db "Verifying  track "

    verify_track_number:

        db 2 dup (?)
        db " head "

    verify_head_number:

        db ?
        db "$"

boot_message:

        db 26 dup (BACKSPACE)
        db 26 dup (SPACE)
        db 26 dup (BACKSPACE)
        db "Writing boot record"
        db "$"

fat_message:

        db 19 dup (BACKSPACE)
        db 19 dup (SPACE)
        db 19 dup (BACKSPACE)
        db "Writing FATs"
        db "$"

dir_message:

        db 12 dup (BACKSPACE)
        db 12 dup (SPACE)
        db 12 dup (BACKSPACE)
        db "Writing root directory"
        db "$"

file_copy_message:

        db 22 dup (BACKSPACE)
        db 22 dup (SPACE)
        db 22 dup (BACKSPACE)
        db "Copying system files"
        db "$"

exit_message:

        db 22 dup (BACKSPACE)
        db 22 dup (SPACE)
        db 22 dup (BACKSPACE)
        db "Format complete"
        db CR,LF
        db "$"

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

        ; error messages

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

allocation_error:

        db "Memory allocation error"
        db CR,LF
        db "$"

no_stack_space:

        db "Not enough room for program stack"
        db CR,LF
        db "$"

computer_message:

        db "IBM AT or compatible computer required"
        db CR,LF
        db "$"

no_drive_error:

        db "No such drive in system"
        db CR,LF
        db "$"

reset_error:

        db "Disk controller reset error"
        db CR,LF
        db "$"

set_drive_error:

        db "Disk drive type not supported by BIOS"
        db CR,LF
        db "$"

get_type_error:

        db "Unknown drive type"
        db CR,LF
        db "$"

no_floppy_error:

        db "No floppy in drive"
        db CR,LF
        db "$"

set_disk_error:

        db "Disk / Drive combination not supported"
        db CR,LF
        db "$"

write_boot_error:

        db "Error writing boot record"
        db CR,LF
        db "$"

write_fat_error:

        db "Error writing FAT"
        db CR,LF
        db "$"

write_dir_error:

        db "Error writing directory"
        db CR,LF
        db "$"

write_protect_error:

        db "Disk is write protected"
        db CR,LF
        db "$"

user_abort:

        db "User abort"
        db CR,LF
        db "$"

error_during_format:

        db "Error during format"
        db CR,LF
        db "$"

disk_type_error:

        db "Defective floppy or wrong floppy type"
        db CR,LF
        db "$"

verify_error:

        db "Verification error"
        db CR,LF
        db "$"

buffer_space_error:

        db "No room for file copy buffer"
        db CR,LF
        db "$"

option_error:

        db CR,LF
        db "Both N and R options specified (only one allowed)"
        db CR,LF
        db "$"

help_message:

db CR,LF
db "FMT version 1.30, Copyright (C) 1990 Clair Alan Hardesty"
db CR,LF,LF
db " syntax: FMT d: size [V] [S] [N|R]"
db CR,LF,LF
db "  where: d = drive (A or B), size = floppy size in Kb,"
db CR,LF,LF
db "   the optional V enables format verification, the optional S"
db CR,LF,LF
db "   copies the system files, the optional N bypasses the prompt,"
db CR,LF,LF
db "   and the optional R enables recursive formatting with prompting"
db CR,LF,LF
db "  supported sizes are:"
db CR,LF,LF
db "   360 for a 360 Kb floppy in a 360 Kb or 1.2 Mb drive"
db CR,LF
db "   720 for a 720 Kb floppy in a 720 Kb or 1.44 Mb drive"
db CR,LF
db "   1200 for a 1.2 Mb floppy in a 1.2 Mb drive"
db CR,LF
db "   1440 for a 1.44 Mb floppy in a 1.44 Mb drive"
db CR,LF
db "$"

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

                ; reduce memory allocation and check stack space

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

start:
                ; if more than 64 Kb is available, SP should equal FFFCh

                cmp sp,0fffch

                jne check_stack

                ; reduce memory allocation to 64 Kb

                mov bx,1000h
                mov ah,4ah

                int 21h

                jnc check_stack

                ; set exit code to 11 (memory allocation error)

                mov dx,offset allocation_error
                mov ah,09h

                int 21h

                mov ax,4c0bh

                int 21h
check_stack:
                ; check stack space

                mov ax,sp
                mov bx,offset file_buffer
                sub ax,bx
                cmp ax,word ptr [stack_size]

                ja room_for_stack

                ; set exit code to 9 (not enough room for stack)

                mov dx,offset no_stack_space
                mov ah,09h

                int 21h

                mov ax,4c08h

                int 21h
room_for_stack:
                ; save the stack pointer

                mov word ptr [stack_pointer],sp

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

                ; check cpu and computer type

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

                ; check CPU for 80286 or above

                push ds
                xor     ax,ax
                push    ax
                popf
                pushf
                pop     ax
                and     ax,8000h
                cmp     ax,8000h

                je short not_an_at

                ; check the model byte in ROM

                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 80286 P/S-2 models

                je at_or_better

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

                je at_or_better

                ; additional model identification code goes here
not_an_at:
                ; computer is not AT compatible

                pop ds

                assume ds:cseg

                mov dx,offset computer_message
                mov ah,09h

                int 21h

                ; set exit code to 6 (not an AT or compatible)

                mov sp,word ptr [stack_pointer]
                mov ax,4c06h

                int 21h

at_or_better:

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

cseg            ends

.286            ; enable the 80286 instruction set

cseg            segment

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

                pop ds

                assume ds:cseg

                ; initialize some variables

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

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

                ; parse the command line

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

parse_loop:
                inc bx
                cmp byte ptr [bx],CR

                jne not_finished

                jmp got_command
not_finished:
                cmp byte ptr [bx],SPACE

                je parse_loop

                cmp byte ptr [bx],TAB

                je parse_loop

                cmp cl,0

                jg number_check

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

                ; check the drive letter (must be drive A: or B:)

                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:
                ; check for last digit by looking at next character

                cmp byte ptr [bx+1],CR

                je last_digit

                cmp byte ptr [bx+1],SPACE

                je last_digit

                cmp byte ptr [bx+1],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    ; store BCD digit in BCD_size
                inc di                  ; increment storage pointer
                inc dl                  ; increment digit count
                cmp dl,5                ; check digit count

                jl jump_to_parse        ; less than 5 digits

                jmp help                ; too many digits in disk size
jump_to_parse:
                jmp parse_loop          ; continue parsing parameters
check_option:
                cmp cl,6

                jl parse_option         ; less than 6 parameters

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

                je set_verify

                cmp byte ptr [bx],'S'   ; check for system option

                je set_system

                cmp byte ptr [bx],'N'   ; check for skip prompt option

                je clear_prompt

                cmp byte ptr [bx],'R'   ; check for recursion option

                je set_recursion

                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:
                cmp byte ptr [prmt_or_rcrs],FALSE

                je prompt_ok
exclusive:
                ; mutually exclusive options specified

                mov dx,offset option_error
                mov ah,09h

                int 21h

                ; set exit code to 12 (mutually exclusive options)

                mov sp,word ptr [stack_pointer]
                mov ax,4c0ch

                int 21h
prompt_ok:
                mov byte ptr [prmt_or_rcrs],TRUE
                mov byte ptr [recurse],FALSE
                mov byte ptr [prompt],FALSE
                inc cl

                jmp parse_loop
set_recursion:
                cmp byte ptr [prmt_or_rcrs],FALSE

                je recurse_ok

                jmp exclusive
recurse_ok:
                mov byte ptr [prmt_or_rcrs],TRUE
                mov byte ptr [recurse],TRUE
                mov byte ptr [prompt],TRUE
                inc cl

                jmp parse_loop
got_command:
                cmp cl,2

                jnl two_parameters

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

                jl help                 ; too few digits in disk size

                ; convert the disk 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 sp,word ptr [stack_pointer]
                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

                jb no_buffer

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

                jc no_buffer

                cmp ax,word ptr [minimum_buffer]

                jnb 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 sp,word ptr [stack_pointer]
                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

                ; save the original disk parameter table pointer

                xor ax,ax
                mov es,ax

                assume es:seg_zero

                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

                ; save the original control-break vector

                mov ax,word ptr es:[008ch]
                mov dx,word ptr es:[008eh]
                mov word ptr [control_break],ax
                mov word ptr [control_break+2],dx
recursion_loop:
                xor ax,ax
                mov es,ax

                assume es:seg_zero

                ; 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
                mov bx,offset break_handler
                cli
                mov word ptr es:[0078h],ax
                mov word ptr es:[007ah],cs

                ; set the new control-break vector

                mov word ptr es:[008ch],bx
                mov word ptr es:[008eh],cs
                sti
                push cs
                pop es

                assume es:cseg

                ; check for prompt bypass

                cmp byte ptr [prompt],1

                jne no_prompt

                ; clear the message line

                mov dx,offset clear_line
                mov ah,09h

                int 21h
eat_keystroke:
                ; eat any pending keystrokes

                mov ah,01h

                int 16h

                jz no_keystroke

                xor ah,ah

                int 16h

                jmp eat_keystroke
no_keystroke:
                ; 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

                cmp ax,CTRL_C_KEY       ; control-C

                jne escape

                jmp break_handler
escape:
                cmp ax,ESC_KEY          ; escape

                jne clear

                jmp break_handler
clear:
                ; clear the message line

                mov dx,offset clear_line
                mov ah,09h

                int 21h
no_prompt:
                mov dx,offset initializing
                mov ah,09h

                int 21h

                ; get disk drive information

                ; note: Some machines do not support this function call,
                ;       this makes it difficult to determine if the
                ;       requested drive and the requested disk size are
                ;       compatible prior to attempting the format.  What
                ;       this BIOS function should do is read the floppy
                ;       type from the CMOS RAM, then set the registers
                ;       as appropriate for that type.

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

                int 13h

                jnc got_drive_type

                ; keep going if the bios does not support this function

                cmp ah,01h

                jne no_drive_type

                ; get the drive type the hard way (from the CMOS RAM)

                mov al,10h
                out 70h,al

                ; note: Very fast machines may need additional I/O wait
                ;       time here. This is common on 80486 machines.

                jmp short io_wait
io_wait:
                in al,71h
                cmp dl,0

                jne drive_b

                and al,0f0h
                shr al,4
drive_b:
                and al,0fh
                mov bl,al
                cmp bl,1

                jne not_360

                mov cx,2709h

                jmp short got_drive_type
not_360:
                cmp bl,2

                jne not_1200

                mov cx,4f0fh

                jmp short got_drive_type
not_1200:
                cmp bl,3

                jne not_720

                mov cx,4f09h

                jmp short got_drive_type
not_720:
                cmp bl,4

                jne not_1440

                mov cx,4f12h

                jmp short got_drive_type
not_1440:
                cmp bl,0

                je got_drive_type
no_drive_type:
                mov dx,offset clear_line
                mov ah,09h

                int 21h

                mov dx,offset get_type_error
                mov ah,09h

                int 21h

                ; set exit code to 10 (unknown drive type)

                mov ax,4c0ah
                push ax

                jmp error_exit
got_drive_type:
                cmp bl,0

                jne drive_exists

                mov dx,offset clear_line
                mov ah,09h

                int 21h

                mov dx,offset no_drive_error
                mov ah,09h

                int 21h

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

                mov ax,4c03h
                push ax

                jmp error_exit
drive_exists:
                cmp bl,4

                jg no_drive_type

                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 clear_line
                mov ah,09h

                int 21h

                mov dx,offset set_disk_error
                mov ah,09h

                int 21h

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

                mov ax,4c02h
                push ax

                jmp error_exit
forty_tracks:
                mov byte ptr [max_track],39
nine_sectors:
                mov byte ptr [sectors_track],9
size_ok:
                ; put the parameter table in the boot record

                pop es
                xor ah,ah
                mov al,byte ptr [disk_size]
                dec ax
                imul ax,11
                add ax,offset parameter_table
                mov si,ax
                mov di,offset disk_table
                mov cx,11
                cld

                rep movsb

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

                ; 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]
                add ax,word ptr [reserved_sectors]

                ; store directory start sector (absolute sector number)

                mov word ptr [dir_start],ax

                ; calculate the location of the first data sector

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

                ; store data start sector (absolute sector number)

                mov word ptr [data_start],ax
                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:
                ; keep going if the bios does not support this function

                cmp ah,01h

                je set_disk

                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

                ; keep going if the bios does not support this function

                cmp ah,01h

                je format_disk

                dec byte ptr [retry_count]

                jnz disk_retry

                mov dx,offset set_disk_error

                jmp write_error

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

                ; format the disk

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

format_disk:
                ; start at track zero

                call track_zero

                ; set up the loop counter

                xor ch,ch
                mov cl,byte ptr [max_track]
                inc cx
format_loop:
                cmp byte ptr [prompt],TRUE

                jne no_key_ready

                ; check for pending keystrokes

                mov ah,01h

                int 16h

                jz no_key_ready

                xor ah,ah

                int 16h

                cmp ax,CTRL_C_KEY       ; control-C

                jne not_Ctrl_C

                jmp break_handler
not_Ctrl_C:
                cmp ax,ESC_KEY          ; escape

                jne format_loop

                jmp break_handler
no_key_ready:
                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:
                ; the disk serial number is a four byte number made up
                ; from the system clock as follows :

                ; byte 0 = day + hundredths of a second
                ; byte 1 = month + second
                ; byte 2 = low byte of year + minute
                ; byte 3 = high byte of year + hour

                ; this method of serial number generation makes the
                ; possibility of duplicate serial numbers occuring
                ; extremely low, but not impossible

                ; get the system time

                mov ah,2ch

                int 21h

                push dx
                push cx

                ; get the system date

                mov ah,2ah

                int 21h

                pop ax
                add cx,ax

                pop ax
                add dx,ax

                ; store the serial number in the boot record

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

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

                ; set up the system file names in the boot record

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

                ; get default drive

                mov ah,19h

                int 21h

                ; set source drive

                add al,41h
                mov byte ptr [ibm_bios_source_path],al
                mov byte ptr [ms_bios_source_path],al

                ; attempt to open IO.SYS

                mov dx,offset ms_bios_source_path
                xor al,al
                mov ah,3dh

                int 21h

                jnc found_ms

                ; attempt to open IBMBIO.COM

                mov dx,offset ibm_bios_source_path
                xor al,al
                mov ah,3dh

                int 21h

                jnc found_ibm

                ; no system on default drive

                mov byte ptr [system_exists],FALSE

                jmp short write_boot
found_ms:
                ; close the file

                mov bx,ax
                mov ah,3eh

                int 21h

                mov byte ptr [system_exists],TRUE
                mov byte ptr [is_ibm],FALSE

                ; fix the bios name in the boot record

                mov cx,11
                mov si,offset ms_bios_name
                mov di,offset bios_name
                cld

                rep movsb

                mov cx,11
                mov si,offset ms_dos_name
                mov di,offset dos_name
                cld

                rep movsb

                jmp short write_boot
found_ibm:
                ; close the file

                mov bx,ax
                mov ah,3eh

                int 21h

                mov byte ptr [system_exists],TRUE
                mov byte ptr [is_ibm],TRUE

                ; fix the bios name in the boot record

                mov cx,11
                mov si,offset ibm_bios_name
                mov di,offset bios_name
                cld

                rep movsb

                mov cx,11
                mov si,offset ibm_dos_name
                mov di,offset dos_name
                cld

                rep movsb

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

                ; 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 DOS_boot
                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

                ; 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

                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

                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
                add ax,word ptr [sectors_fat]

                ; 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

                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 write_error
fat2_write_ok:
                pop dx
                pop cx
                pop ax

                ; increment the absolute sector number

                add ax,1

                loop fat2

                jnc write_dir

                mov dx,offset write_fat_error

                jmp short write_error

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

                ; write the root directory

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

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

                int 21h

                ; set the starting sector (absolute)

                mov ax,word ptr [sectors_fat]
                mul byte ptr [fats]
                add ax,word ptr [reserved_sectors]

                ; 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

                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

                loop dir

                jnc transfer

                mov dx,offset write_dir_error

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

                jne exit

                cmp byte ptr [system_exists],TRUE

                jne no_system

                ; transfer the system files

                call load_system

                pushf

                ; restore the original disk parameter table pointer

                ; this must be done because the create file function
                ; used in copying the system files will reset some of
                ; the values in the floppy parameter table to their
                ; default values. When recursive formatting is enabled,
                ; the parameter table will be reset before each floppy.

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

                assume es:seg_zero

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

                assume es:cseg

                popf

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

                mov ax,4c05h
                push ax

                jmp short error_exit
write_error:
                push dx
                mov dx,offset clear_line
                mov ah,09h

                int 21h

                pop dx
                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 or recurse

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

exit:
                cmp byte ptr [recurse],FALSE

                je no_recursion

                ; display format complete message

                mov dx,offset exit_message
                mov ah,09h

                int 21h

                ; beep to prompt for next floppy

                mov dl,BELL
                mov ah,02h

                int 21h

                jmp recursion_loop
no_recursion:
                ; set exit code to 0 (Success)

                mov ax,4c00h
                push ax
error_exit:
                ; restore the original disk parameter table pointer
                ;  and the original control-break handler

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

                assume es:seg_zero

                mov ax,word ptr [control_break]
                mov bx,word ptr [control_break+2]
                cli
                mov word ptr es:[008ch],ax
                mov word ptr es:[008eh],bx
                mov word ptr es:[0078h],cx
                mov word ptr es:[007ah],dx
                sti
                pop es

                assume es:cseg

                ; reset the disk controller

                xor ah,ah
                mov dl,byte ptr [drive_number]

                int 13

                pop ax
                push ax
                cmp al,0

                jne abnormal_exit

                ; display format complete message

                mov dx,offset exit_message
                mov ah,09h

                int 21h
abnormal_exit:
                ; restore the cursor

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

                int 10h

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

                pop ax

                mov sp,word ptr [stack_pointer]
                int 21h

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

                ; format subroutines

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

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

                ; control-break handler

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

break_handler:
                mov sp,word ptr [stack_pointer]

                cmp ax,CTRL_C_KEY       ; control-C

                je control_c

                cmp ax,ESC_KEY          ; escape

                je control_c

                mov bh,0
                mov ah,03h

                int 10h

                dec dh
                mov dl,50
                mov ah,02

                int 10h
control_c:
                mov dx,offset clear_line
                mov ah,09h

                int 21h

                mov dx,offset user_abort
                mov ah,09h

                int 21h

                mov ax,4c09h
                push ax

                jmp error_exit

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

                ; information display during format
                ;
                ; input:
                ;
                ; al = track number
                ; bl = head number

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

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
                ;
                ; input:
                ;
                ; al = track number
                ; bl = head number

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

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

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

                ; set the track number in the sector tables to zero

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

track_zero:
                xor ch,ch
                mov cl,36
                mov bx,offset sector_table_0
zero_counter:
                mov byte ptr [bx],0
                add bx,4

                loop zero_counter

                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 track 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 fail 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 [disk_size],2

                je one_point_two

                cmp byte ptr [sector_table_0],0

                je verify_type

                jmp short format_head_1
one_point_two:
                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:
                ; calculate the sector number

                xor dx,dx
                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

                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

                shl dh,6

                ; 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 the system files

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

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 [ibm_bios_source_path],al
                mov byte ptr [ibm_dos_source_path],al
                mov byte ptr [ms_bios_source_path],al
                mov byte ptr [ms_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 [ibm_bios_destination_path],al
                mov byte ptr [ibm_dos_destination_path],al
                mov byte ptr [ms_bios_destination_path],al
                mov byte ptr [ms_dos_destination_path],al
                mov byte ptr [cmd_destination_path],al

                ; copy the bios system file (IBMBIO.COM or IO.SYS)

                cmp byte ptr [is_ibm],TRUE

                jne ms_bios

                mov bx,offset file_buffer
                mov cx,word ptr [buffer_size]
                mov si,offset ibm_bios_source_path
                mov di,offset ibm_bios_destination_path

                call file_copy

                jnc copy_dos

                jmp short load_error
ms_bios:
                mov bx,offset file_buffer
                mov cx,word ptr [buffer_size]
                mov si,offset ms_bios_source_path
                mov di,offset ms_bios_destination_path

                call file_copy

                jc load_error
copy_dos:
                ; copy the dos system file (IBMDOS.COM or MSDOS.SYS)

                cmp byte ptr [is_ibm],TRUE

                jne ms_dos

                mov bx,offset file_buffer
                mov cx,word ptr [buffer_size]
                mov si,offset ibm_dos_source_path
                mov di,offset ibm_dos_destination_path

                call file_copy

                jnc copy_cmd

                jmp short load_error
ms_dos:
                mov bx,offset file_buffer
                mov cx,word ptr [buffer_size]
                mov si,offset ms_dos_source_path
                mov di,offset ms_dos_destination_path

                call file_copy

                jc load_error
copy_cmd:
                ; 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
load_error:
                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

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

                ; file copy variables

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

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 error messages

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

file_not_found:

        db " - error opening input file"
        db CR,LF
        db "$"

file_create_error:

        db " - error creating output file"
        db CR,LF
        db "$"

file_read_error:

        db " - error reading file"
        db CR,LF
        db "$"

file_write_error:

        db " - error writing file"
        db CR,LF
        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

                mov word ptr [write_handle],ax

                jnc copy_loop

                ; 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 short copy_error
copy_loop:
                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 short 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 short copy_error
file_write_ok:
                cmp ax,word ptr [copy_buffer_size]

                je copy_loop

                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

                ret

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

                ; write file block from buffer

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

write_file:
                mov cx,word ptr [bytes_read]
                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)

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

DOS_boot:

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

cseg            ends

.8086           ; enable the 8086 instruction set

cseg            segment

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

                jmp boot_strap          ; skip over data areas

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

                ; DOS disk data area

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

oem_name:               db   "FMT 1.30"

bytes_sector:           dw   512        ; bytes per sector
sectors_cluster:        db   ?          ; filled in before writing boot
reserved_sectors:       dw   1          ; number of reserved sectors
fats:                   db   2          ; number of FATs
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          ; number of heads
hidden_sectors:         dd   0          ; number of hidden sectors
big_total:              dd   0          ; used for large partitions
physical_drive:         db   0          ; part of why only A is bootable
reserved:               db   0          ; not currently used by DOS
extended_boot:          db   029h       ; signature for DOS 4.0 format
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:              dw ?            ; filled in before writing boot
data_start:             dw ?            ; filled in before writing boot
disk_table:             db 11 dup (?)   ; filled in before writing boot
bios_name:              db 11 dup (?)   ; filled in before writing boot
dos_name:               db 11 dup (?)   ; filled in before writing boot
track:                  dw ?            ; bootstrap program variable
head:                   db ?            ; bootstrap program variable
sector:                 db ?            ; bootstrap program variable

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

                ; bootstrap program error messages

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

reset_message:
                        db CR,LF,LF
                        db "Disk controller error,"
                        db NULL
read_message:
                        db CR,LF,LF
                        db "Disk read error,"
                        db NULL
system_message:
                        db CR,LF,LF
                        db "No system on disk,"
                        db NULL
retry_message:
                        db CR,LF
                        db "Press any key to reboot."
                        db NULL

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

                ; bootstrap program code

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

boot_strap:
                ; disable interrupts while modifying segment registers

                cli

                ; set the stack segment and the extra segment to 0000

                xor ax,ax
                mov es,ax
                mov ss,ax

                assume es:seg_zero,ss:seg_zero

                ; DOS loads the boot record at 0000:7c00,
                ; so put the stack just below it

                mov sp,BOOT

                ; set up the parameters used by the BIOS program loader

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

                ; push the BIOS program loader variables onto the stack

                push ds
                push si
                push ss
                push bx

                ; set the data segment to 0000

                mov ds,ax

                assume ds:seg_zero

                ; use the disk parameter table in the floppy boot record

                mov word ptr [bx],BOOT+offset disk_table-offset DOS_boot
                mov word ptr [bx+02],ax

                ; enable interrupts

                sti

                ; reset the disk controller

                int 13h

                jnc reset_ok

                ; display reset error message

                mov si,BOOT+offset reset_message-offset DOS_boot

                call write_string

                jmp short not_bootable
reset_ok:
                ; check for system name in boot record

                cmp byte ptr ds:[BOOT+bios_name-DOS_boot],0

                je system_error

                ; get directory start sector (absolute sector number)

                mov ax,word ptr ds:[BOOT+dir_start-DOS_boot]

                ; get track, head, and sector

                call calculate_ths

                ; set the offset of the disk read buffer

                mov bx,0500h

                ; read the first directory sector

                call read_disk

                jc read_error

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

                mov di,bx
                mov cx,11
                mov si,BOOT+offset bios_name-offset DOS_boot

                rep cmpsb

                jnz system_error

                ; check directory for the DOS file
                ; (it must be the second directory entry)

                lea di,[bx+32]
                mov si,BOOT+offset dos_name-offset DOS_boot
                mov cx,11

                rep cmpsb

                jnz system_error

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

system_found:
                ; valid directory, attempt to boot from the disk

                ; get the data start sector (absolute sector number)

                mov ax,word ptr ds:[BOOT+data_start-DOS_boot]

                ; 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

                ; read one disk sector

                call read_disk

                pop dx
                pop cx
                pop ax

                jc read_error
disk_read_ok:
                ; increment the sector to read

                inc ax

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

                add bx,word ptr ds:[BOOT+bytes_sector-DOS_boot]

                loop read_next

                ; set up the parameters for the BIOS program loader

                ; get the media descriptor

                mov ch,byte ptr ds:[BOOT+descriptor-DOS_boot]

                ; get the drive number

                mov dl,byte ptr ds:[BOOT+physical_drive-DOS_boot]

                ; get the data start sector (absolute sector number)

                xor ax,ax
                mov bx,word ptr ds:[BOOT+data_start-DOS_boot]

                ; execute the BIOS program loader

                jmp far ptr program_loader

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

not_bootable:
                ; display retry message

                mov si,BOOT+offset retry_message-offset DOS_boot

                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

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

system_error:
                ; display the no system error message

                mov si,BOOT+offset system_message-offset DOS_boot

                call write_string

                jmp not_bootable

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

read_error:
                ; display the disk read error message

                mov si,BOOT+offset read_message-offset DOS_boot

                call write_string

                jmp not_bootable

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

                ; bootstrap subroutines

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

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

                ; write a message to the screen

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

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 a sector from the disk

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

read_disk:
                ; get the track number

                mov dx,word ptr ds:[BOOT+track-DOS_boot]

                ; 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:[BOOT+sector-DOS_boot]

                mov cx,dx
                xchg ch,cl

                ; get the drive number

                mov dl,byte ptr ds:[BOOT+physical_drive-DOS_boot]

                ; get the head number

                mov dh,byte ptr ds:[BOOT+head-DOS_boot]

                ; read one sector from the disk

                mov ax,0201h

                int 13h

                ret

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

                ; calculate the track, head, and sector numbers

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

calculate_ths:
                ; calculate the sector number

                xor dx,dx
                div word ptr ds:[BOOT+sectors_track-DOS_boot]
                inc dl

                ; store the sector number

                mov byte ptr ds:[BOOT+sector-DOS_boot],dl

                ; calculate the head number and the track number

                xor dx,dx
                div word ptr ds:[BOOT+heads-DOS_boot]

                ; store the head number

                mov byte ptr ds:[BOOT+head-DOS_boot],dl

                ; store the track number

                mov word ptr ds:[BOOT+track-DOS_boot],ax

                ret

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

                ; put copyright information in boot record (and FMT.COM)

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

versionx:       db "FMT version 1.30 22-DEC-1990",NULL

copyright:      db "Copyright 1990 Clair Alan Hardesty",NULL

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

                ; adjust the boot record size to 512 bytes

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

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

                ; note: if the boot record is too long, the assembler
                ;       will issue an error message stating that a
                ;       positive count is required for the dup command.

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

                ; boot sector signature

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

signature:      dw 0AA55h

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

                ; end of boot sector data

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

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

                ; FAT and directory data

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

                ; the first FAT sector begins with the media descriptor
fat_record:
                db ?                    ; filled in before writing FAT
                db 0ffh,0ffh

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

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

                ; file copy buffer

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

file_buffer:
                ; the system file copy buffer space starts here and
                ; extends to the bottom of the stack.

cseg            ends

end             entry_point

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

                ; end of Fmt.asm

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