;*********************************************************************** ;* * ;* Fmt.asm 23-JAN-1990 * ;* * ;* Format Floppy Disk - version 1.00 - IBM(R) DOS 4.0 style format * ;* * ;* Designed for use with IBM AT compatible sytems (including PS/2s) * ;* * ;* Syntax: FMT d: size [v] [s] [n] * ;* * ;* where: d = drive (A or B), size = floppy size in Kb, * ;* * ;* the optional v enables format verification, * ;* * ;* the optional s copies the system files, * ;* * ;* and the optional n bypasses the `insert floppy' prompt * ;* * ;* supported sizes are: * ;* * ;* 360 for a 360 Kb floppy in a 360 Kb or 1.2 Mb drive * ;* 720 for a 720 Kb floppy in a 720 Kb or 1.44 Mb drive * ;* 1200 for a 1.2 Mb floppy in a 1.2 Mb drive * ;* 1440 for a 1.44 Mb floppy in a 1.44 Mb drive * ;* * ;* Exit codes: * ;* * ;* 0 = successful format * ;* 1 = failure during format, verify, or write * ;* 2 = disk size / drive type combination is not supported * ;* 3 = requested drive does not exist * ;* 4 = invalid or null command line * ;* 5 = system files not found or error writing system files * ;* 6 = computer is not AT compatible * ;* 7 = insufficient memory for file copy buffer (system transfer) * ;* * ;* In order to use the system transfer option, the following files * ;* * ;* must be in the root diectory of the default drive: * ;* * ;* 1: IBMBIO.COM (normally a hidden file) * ;* * ;* 2: IBMDOS.COM (normally a hidden file) * ;* * ;* 3: COMMAND.COM * ;* * ;* Compile with Borland Turbo Assembler(R) version 1.0 or greater * ;* * ;* tasm /t fmt tasm /t /zi fmt * ;* or * ;* tlink /t /x fmt tlink /v /x fmt * ;* * ;* tdstrip -c -s fmt * ;* * ;* for use with Turbo Debugger(R) * ;* or * ;* * ;* Compile with Microsoft(R) Macro Assembler version 5.1 or greater * ;* * ;* masm /t fmt; * ;* * ;* link fmt; * ;* * ;* exe2bin fmt fmt.com * ;* * ;* del fmt.exe * ;* * ;* Copyright (C) 1990 by: * ;* * ;* Clair Alan Hardesty * ;* IBM-PC Hardware/Software Consultant * ;* 10301 Johnson Av * ;* Cupertino, CA 95014 * ;* (408) 446-0550 * ;* CompuServe 75350,16 * ;* * ;* Microsoft(R) is a registered trademark of Microsoft Corporation * ;* * ;* IBM(R) and PS/2 are registered trademarks of International * ;* * ;* Business Machines Corporation * ;* * ;* Turbo Assembler(R) and Turbo Debugger(R) are registered trademarks * ;* * ;* of Borland International * ;* * ;*********************************************************************** ;*********************************************************************** ; set-up for the far jump used in the boot record code ;*********************************************************************** ibmbio segment at 0070h assume cs:ibmbio org 0000h program_loader label far ibmbio ends ;*********************************************************************** ; set-up for system identification ;*********************************************************************** rom_bios segment at 0f000h assume ds:rom_bios org 0fffeh system_id label byte rom_bios ends ;*********************************************************************** ; main program code ;*********************************************************************** cseg segment assume cs:cseg,ds:cseg,es:cseg,ss:cseg org 0100h entry_point: jmp start ; skip over data area ;*********************************************************************** ; format data area ;*********************************************************************** ; format sector tables sector_table_0: db 000h,000h,001h,002h db 000h,000h,002h,002h db 000h,000h,003h,002h db 000h,000h,004h,002h db 000h,000h,005h,002h db 000h,000h,006h,002h db 000h,000h,007h,002h db 000h,000h,008h,002h db 000h,000h,009h,002h db 000h,000h,00Ah,002h db 000h,000h,00Bh,002h db 000h,000h,00Ch,002h db 000h,000h,00Dh,002h db 000h,000h,00Eh,002h db 000h,000h,00Fh,002h db 000h,000h,010h,002h db 000h,000h,011h,002h db 000h,000h,012h,002h sector_table_1: db 000h,001h,001h,002h db 000h,001h,002h,002h db 000h,001h,003h,002h db 000h,001h,004h,002h db 000h,001h,005h,002h db 000h,001h,006h,002h db 000h,001h,007h,002h db 000h,001h,008h,002h db 000h,001h,009h,002h db 000h,001h,00Ah,002h db 000h,001h,00Bh,002h db 000h,001h,00Ch,002h db 000h,001h,00Dh,002h db 000h,001h,00Eh,002h db 000h,001h,00Fh,002h db 000h,001h,010h,002h db 000h,001h,011h,002h db 000h,001h,012h,002h ;*********************************************************************** temp_size: db 4 dup (?) binary_size: dw ? verify: db 0 ; default is no verification system: db 0 ; default is no system file transfer prompt: db 1 ; default is prompt for floppy drive_number: db ? disk_drive: dw ? max_track: db ? retry_count: db ? write_track: dw ? write_head: db ? write_sector: db ? cursor: dw ? stack_size: dw 2048 ; used in buffer size calculation minimum_buffer: dw 4096 ; minimum file copy buffer size buffer_size: dw ? ; actual file copy buffer size ;*********************************************************************** disk_size: db ? ; 1 = 360kb ; 2 = 1.2Mb ; 3 = 720kb ; 4 = 1.44Mb drive_type: db ? ; 0 = no drive ; 1 = 360kb ; 2 = 1.2Mb ; 3 = 720kb ; 4 = 1.44Mb ;*********************************************************************** parameter_pointer: dd ? ; storage for the original disk parameter table pointer ;*********************************************************************** floppy_table: ; 360 Kb db 0FDh ; media descriptor db 40 ; number of tracks dw 9 ; sectors per track dw 112 ; entries per root directory db 7 ; sectors per root directory db 2 ; sectors per cluster dw 2 ; sectors per FAT ; 1.2 Mb db 0F9h ; media descriptor db 80 ; number of tracks dw 15 ; sectors per track dw 224 ; entries per root directory db 14 ; sectors per root directory db 1 ; sectors per cluster dw 7 ; sectors per FAT ; 720 Kb db 0F9h ; media descriptor db 80 ; number of tracks dw 9 ; sectors per track dw 112 ; entries per root directory db 7 ; sectors per root directory db 2 ; sectors per cluster dw 3 ; sectors per FAT ; 1.44 Mb db 0F0h ; media descriptor db 80 ; number of tracks dw 18 ; sectors per track dw 224 ; entries per root directory db 14 ; sectors per root directory db 1 ; sectors per cluster dw 9 ; sectors per FAT ;*********************************************************************** parameter_table: ; 360 Kb db 0D2h,002h,009h,002h,009h,02Ah,0FFh,050h,0F6h,001h,001h ; 1.2 Mb db 082h,002h,009h,002h,00Fh,01Bh,0FFh,054h,0F6h,001h,001h ; 720 Kb db 012h,002h,009h,002h,009h,02Ah,0FFh,050h,0F6h,001h,001h ; 1.44 Mb db 012h,002h,009h,002h,012h,01Bh,0FFh,06Ch,0F6h,001h,001h ;*********************************************************************** ; informational messages ;*********************************************************************** cr_lf: db 00Dh,00Ah db "$" insert_floppy: db "Put desired floppy in drive " insert_drive: db ? db ": and press any key" db "$" clear_line: db 50 dup (008h) db 50 dup (020h) db 50 dup (008h) db "$" format_display: db 26 dup (008h) db "Formatting track " format_track_number: db 2 dup (?) db " head " format_head_number: db ? db "$" verify_display: db 26 dup (008h) db "Verifying track " verify_track_number: db 2 dup (?) db " head " verify_head_number: db ? db "$" boot_message: db 26 dup (008h) db 26 dup (020h) db 26 dup (008h) db "Writing boot record" db "$" fat_message: db 19 dup (008h) db 19 dup (020h) db 19 dup (008h) db "Writing FATs" db "$" dir_message: db 12 dup (008h) db 12 dup (020h) db 12 dup (008h) db "Writing root directory" db "$" file_copy_message: db 22 dup (008h) db 22 dup (020h) db 22 dup (008h) db "Copying system files" db "$" exit_message: db 22 dup (008h) db 22 dup (020h) db 22 dup (008h) db "Format complete" db 00Dh,00Ah db "$" ;*********************************************************************** ; error messages ;*********************************************************************** computer_message: db 00Dh,00Ah,"IBM AT or compatible computer required",00Dh,00Ah db "$" no_drive_error: db 00Dh,00Ah,"No such drive in system",00Dh,00Ah db "$" reset_error: db 00Dh,00Ah,"Disk controller reset error",00Dh,00Ah db "$" set_drive_error: db 00Dh,00Ah,"Disk drive type not supported by BIOS",00Dh,00Ah db "$" no_floppy_error: db 00Dh,00Ah,"No floppy in drive",00Dh,00Ah db "$" set_disk_error: db 00Dh,00Ah,"Disk / Drive combination not supported",00Dh,00Ah db "$" write_boot_error: db 00Dh,00Ah,"Error writing boot record",00Dh,00Ah db "$" write_fat_error: db 00Dh,00Ah,"Error writing FAT",00Dh,00Ah db "$" write_dir_error: db 00Dh,00Ah,"Error writing directory",00Dh,00Ah db "$" write_protect_error: db 00Dh,00Ah,"Disk is write protected",00Dh,00Ah db "$" error_during_format: db 00Dh,00Ah,"Error during format",00Dh,00Ah db "$" disk_type_error: db 00Dh,00Ah,"Probably wrong floppy type",00Dh,00Ah db "$" verify_error: db 00Dh,00Ah,"Verification error",00Dh,00Ah db "$" buffer_space_error: db 00Dh,00Ah,"No room for file copy buffer",00Dh,00Ah db "$" help_message: db 00Dh,00Ah,"FMT version 1.00, Copyright (C) 1990 Clair Alan Hardesty" db 00Dh,00Ah db 00Dh,00Ah," syntax: fmt d: size [v] [s] [n]" db 00Dh,00Ah db 00Dh,00Ah," where: d = drive (A or B), size = floppy size in Kb," db 00Dh,00Ah db 00Dh,00Ah," the optional v enables format verification," db 00Dh,00Ah db 00Dh,00Ah," the optional s copies the system files," db 00Dh,00Ah db 00Dh,00Ah," and the optional n bypasses the `insert floppy' prompt" db 00Dh,00Ah db 00Dh,00Ah," supported sizes are:" db 00Dh,00Ah db 00Dh,00Ah," 360 for a 360 Kb floppy in a 360 Kb or 1.2 Mb drive" db 00Dh,00Ah," 720 for a 720 Kb floppy in a 720 Kb or 1.44 Mb drive" db 00Dh,00Ah," 1200 for a 1.2 Mb floppy in a 1.2 Mb drive" db 00Dh,00Ah," 1440 for a 1.44 Mb floppy in a 1.44 Mb drive" db 00Dh,00Ah db "$" ;*********************************************************************** ; check cpu and computer type ;*********************************************************************** start: ; check CPU for 80286 or above xor ax,ax push ax popf pushf pop ax and ax,08000h cmp ax,08000h jnz two_eighty_six mov dx,offset computer_message mov ah,09h int 21h ; exit jmp short not_an_at two_eighty_six: ; check the model byte in ROM push ds mov ax,rom_bios mov ds,ax assume ds:rom_bios cmp system_id,0fch ; AT or compatible je at_or_better cmp system_id,0fah ; most P/S-2 (80286) models je at_or_better cmp system_id,0f8h ; P/S-2 model 80 je at_or_better ; computer is not AT compatible pop ds assume ds:cseg mov dx,offset computer_message mov ah,09h int 21h not_an_at: ; set exit code to 6 (not an AT or compatible) mov ax,4c06h int 21h at_or_better: ;*********************************************************************** .286 ; enable the 80286 instruction set ;*********************************************************************** pop ds assume ds:cseg ; initialize some variables mov cl,0 ; parameter counter mov dl,0 ; disk size digit counter mov di,offset temp_size ; temporary disk size storage mov bx,0080h ; pointer to command line ;*********************************************************************** ; parse the command line ;*********************************************************************** parse_loop: inc bx cmp byte ptr [bx],0dh ; carriage return jne not_finished jmp got_command not_finished: cmp byte ptr [bx],' ' ; space je parse_loop cmp byte ptr [bx],09h ; tab je parse_loop cmp cl,0 jg number_check and byte ptr [bx],5fh ; capitalize drive letter cmp byte ptr [bx],'A' jnl check_for_b jmp help ; invalid drive check_for_b: cmp byte ptr [bx],'B' jng store_drive jmp help ; invalid drive store_drive: mov al,byte ptr [bx] sub al,'A' mov byte ptr [drive_number],al colon_check: cmp byte ptr [bx+1],':' je found_colon jmp help ; no colon after drive letter found_colon: inc bx ; skip over the colon inc cl ; increment parameter count jmp parse_loop number_check: cmp cl,1 jg check_option cmp byte ptr [bx],'0' jnl maybe_a_number jmp help ; not a number maybe_a_number: cmp byte ptr [bx],'9' jng is_a_number jmp help ; not a number is_a_number: cmp byte ptr [bx+1],0Dh ; next character is a return je last_digit cmp byte ptr [bx+1],' ' ; next character is a space je last_digit cmp byte ptr [bx+1],09h ; next character is a tab jne store_digit last_digit: inc cl ; increment parameter count store_digit: mov al,byte ptr [bx] sub al,'0' ; convert ASCII to BCD mov byte ptr [di],al inc di inc dl cmp dl,4 jle jump_to_parse jmp help ; too many digits in disk size jump_to_parse: jmp parse_loop check_option: cmp cl,5 jng parse_option jmp help ; too many parameters parse_option: and byte ptr [bx],5fh cmp byte ptr [bx],'V' je set_verify cmp byte ptr [bx],'S' je set_system cmp byte ptr [bx],'N' je clear_prompt jmp help ; invalid option set_verify: mov byte ptr [verify],1 ; set format verify true inc cl jmp parse_loop set_system: mov byte ptr [system],1 ; set transfer system true inc cl jmp parse_loop clear_prompt: mov byte ptr [prompt],0 ; set transfer system true inc cl jmp parse_loop got_command: cmp cl,2 jnl two_parameters jmp help ; too few parameters two_parameters: cmp dl,3 jl help ; too few digits in disk size ; convert the size parameter from BCD to binary mov cl,dl dec di xor ah,ah mov al,byte ptr [di] mov word ptr [binary_size],ax dec di dec cl xor ah,ah mov al,byte ptr [di] imul ax,10 add word ptr [binary_size],ax dec di dec cl xor ah,ah mov al,byte ptr [di] imul ax,100 add word ptr [binary_size],ax dec di dec cl jz test_size_360 xor ah,ah mov al,byte ptr [di] imul ax,1000 add word ptr [binary_size],ax test_size_360: cmp word ptr [binary_size],360 jne test_size_1200 ; not 360 Kb mov byte ptr [disk_size],1 jmp short set_up ; command line is valid test_size_1200: cmp word ptr [binary_size],1200 jne test_size_720 ; not 1.2 Mb mov byte ptr [disk_size],2 jmp short set_up ; command line is valid test_size_720: cmp word ptr [binary_size],720 jne test_size_1440 ; not 720 Kb mov byte ptr [disk_size],3 jmp short set_up ; command line is valid test_size_1440: cmp word ptr [binary_size],1440 jne help ; not 1.44 Mb mov byte ptr [disk_size],4 jmp short set_up ; command line is valid help: ; command line is invalid or null mov dx,offset help_message mov ah,09h int 21h ; set exit code to 4 (null or invalid command line) mov ax,4c04h int 21h ;*********************************************************************** ; set up the drive parameters and the parameter table ;*********************************************************************** set_up: ; if transfering system, check file copy buffer space cmp byte ptr [system],1 jne cursor_off ; calculate copy buffer size mov ax,sp cmp ax,offset file_buffer jc no_buffer sub ax,offset file_buffer sbb ax,word ptr [stack_size] jc no_buffer cmp ax,word ptr [minimum_buffer] jnc buffer_ok no_buffer: mov dx,offset buffer_space_error mov ah,09h int 21h ; set exit code to 7 (no room for file copy buffer) mov ax,4c07h int 21h buffer_ok: mov word ptr [buffer_size],ax cursor_off: ; save the current cursor and then turn the cursor off mov ah,03h int 10h mov word ptr [cursor],cx or cx,2000h mov ah,01h int 10h ; issue a carrriage-return / line-feed to stdout mov dx,offset cr_lf mov ah,09h int 21h ; check for prompt bypass cmp byte ptr [prompt],1 jne no_prompt ; prompt for a floppy mov dx,offset insert_floppy mov ah,09h mov al,byte ptr [drive_number] add al,41h mov byte ptr [insert_drive],al int 21h ; wait for a key press xor ah,ah int 16h ; clear the message line mov dx,offset clear_line mov ah,09h int 21h no_prompt: ; get disk drive information push es push di push si mov ah,08h mov dl,byte ptr [drive_number] int 13h cmp bl,0 jne drive_exists mov dx,offset no_drive_error mov ah,09h int 21h ; set exit code to 3 (drive does not exist) pop ax pop ax pop ax mov ax,4c03h int 21h drive_exists: mov byte ptr [max_track],ch mov byte ptr [sectors_track],cl mov byte ptr [drive_type],bl mov ah,byte ptr [disk_size] mov al,bl mov word ptr [disk_drive],ax ; check for floppy size less than drive maximum capacity ; check for a 360 Kb floppy in a 1.2 Mb drive cmp word ptr [disk_drive],0102h je forty_tracks ; check for a 720 Kb floppy in a 1.44 Mb drive cmp word ptr [disk_drive],0304h je nine_sectors ; check for improper disk / drive cominations cmp word ptr [disk_drive],0201h je bad_size ; 1.2 Mb in 360 Kb cmp word ptr [disk_drive],0301h je bad_size ; 720 Kb in 360 Kb cmp word ptr [disk_drive],0401h je bad_size ; 1.44 Mb in 360 Kb cmp word ptr [disk_drive],0302h je bad_size ; 720 Kb in 1.2 Mb cmp word ptr [disk_drive],0402h je bad_size ; 1.44 Mb in 1.2 Mb cmp word ptr [disk_drive],0103h je bad_size ; 360 Kb in 720 Kb cmp word ptr [disk_drive],0203h je bad_size ; 1.2 Mb in 720 Kb cmp word ptr [disk_drive],0403h je bad_size ; 1.44 Mb in 720 Kb cmp word ptr [disk_drive],0104h je bad_size ; 360 Kb in 1.44 Mb cmp word ptr [disk_drive],0204h je bad_size ; 1.2 Mb in 1.44 Mb jmp short size_ok bad_size: mov dx,offset set_disk_error mov ah,09h int 21h ; set exit code to 2 (disk / drive comination error) pop ax pop ax pop ax mov ax,4c02h int 21h forty_tracks: mov byte ptr [max_track],39 nine_sectors: mov byte ptr [sectors_track],9 size_ok: ; save the original disk parameter table pointer xor ax,ax mov es,ax mov ax,word ptr es:[0078h] mov dx,word ptr es:[007ah] mov word ptr [parameter_pointer],ax mov word ptr [parameter_pointer+2],dx ; set the disk parameter table pointer for formatting xor ah,ah mov al,byte ptr [disk_size] dec ax imul ax,11 add ax,offset parameter_table cli mov word ptr es:[0078h],ax mov word ptr es:[007ah],cs sti ; put the parameter table in the boot record push cs pop es mov si,ax mov di,offset disk_table mov cx,11 cld rep movsb pop si pop di pop es ;*********************************************************************** ; store the disk parameters in the boot record and FAT ;*********************************************************************** push si ; calculate the offset to the parameters in floppy_table xor ah,ah mov al,byte ptr [disk_size] dec ax imul ax,10 mov bx,ax mov si,offset floppy_table ; set the media descriptor byte mov al,byte ptr [si+bx] mov byte ptr [descriptor],al mov byte ptr [fat_record],al ; first byte of FAT ; set the number of root directory entries mov ax,word ptr [si+bx+4] mov word ptr [directory_size],ax ; set the number of sectors per directory mov al,byte ptr [si+bx+6] mov byte ptr [sectors_directory],al ; set the number of sectors per cluster mov al,byte ptr [si+bx+7] mov byte ptr [sectors_cluster],al ; set the number of sectors per FAT mov ax,word ptr [si+bx+8] mov word ptr [sectors_fat],ax ; set the total number of sectors per disk xor dx,dx xor ah,ah mov al,byte ptr [max_track] inc ax mul word ptr [sectors_track] xor bh,bh mov bl,byte ptr [heads] mul bx mov word ptr [small_total],ax ; calculate the location of the first directory sector xor dx,dx xor ah,ah mov al,byte ptr [fats] mul word ptr [sectors_fat] add ax,word ptr [hidden_sectors] adc dx,word ptr [hidden_sectors+2] add ax,word ptr [reserved_sectors] adc dx,0 ; store directory start sector (absolute sector number) mov word ptr [dir_start],ax mov word ptr [dir_start+2],dx ; calculate the location of the first data sector xor bh,bh mov bl,byte ptr [sectors_directory] add ax,bx adc dx,0 ; store data start sector (absolute sector number) mov word ptr [data_start],ax mov word ptr [data_start+2],dx pop si ;*********************************************************************** ; reset the disk controller ;*********************************************************************** reset: mov byte ptr [retry_count],3 reset_retry: xor ah,ah mov dl,byte ptr [drive_number] int 13h jnc short set_drive dec byte ptr [retry_count] jnz reset_retry mov dx,offset reset_error jmp write_error ;*********************************************************************** ; set the drive type for format ;*********************************************************************** set_drive: mov byte ptr [retry_count],3 drive_retry: mov al,byte ptr [drive_type] cmp al,3 jl five_inch mov al,4 jmp short set_type five_inch: cmp al,2 jne low_density cmp byte ptr [disk_size],1 jg high_density mov al,2 jmp short set_type high_density: mov al,3 jmp short set_type low_density: mov al,1 set_type: mov ah,17h int 13h jnc short set_disk cmp ah,80h jne floppy_exists mov dx,offset no_floppy_error jmp write_error floppy_exists: dec byte ptr [retry_count] jnz drive_retry mov dx,offset set_drive_error jmp write_error ;*********************************************************************** ; set the disk type for format ;*********************************************************************** set_disk: mov byte ptr [retry_count],3 disk_retry: push es push di mov ah,18h mov ch,byte ptr [max_track] mov cl,byte ptr [sectors_track] mov dl,byte ptr [drive_number] int 13h pop di pop es jnc format_disk dec byte ptr [retry_count] jnz disk_retry mov dx,offset set_disk_error jmp write_error ;*********************************************************************** ; format the disk ;*********************************************************************** format_disk: ; set up the loop counter xor ch,ch mov cl,byte ptr [max_track] inc cx format_loop: push cx call format_track jnc continue pop cx jmp write_error continue: call next_track pop cx loop format_loop ;*********************************************************************** ; set up the volume serial number in the boot record ;*********************************************************************** serial_number: ; get the time mov ah,2ch int 21h ; fiddle the serial number bytes (similar to DOS 4.0) ; ; I don't know why IBM does this. It may be done to ; hide the fact that the serial number is derived ; from the system clock. ; ; I am not sure that the numbers that I have chosen are ; the same as IBM's, but they are close. ; ; a more reasonable approach might be to simply add one ; to each byte to avoid zero bytes in the serial number add cx,07c7h add dx,0707h ; store the serial number in the boot record mov word ptr [sn_low],cx mov word ptr [sn_high],dx ;*********************************************************************** ; write the boot record and FAT1 ;*********************************************************************** write_boot: mov dx,offset boot_message mov ah,09h int 21h mov byte ptr [retry_count],3 boot_retry: mov ax,0302h mov bx,offset boot_record mov cx,0001h xor dh,dh mov dl,byte ptr [drive_number] int 13h jnc short finish_fat1 dec byte ptr [retry_count] jnz boot_retry mov dx,offset write_boot_error jmp write_error finish_fat1: mov dx,offset fat_message mov ah,09h int 21h ; set the starting sector (absolute) mov ax,2 xor dx,dx ; set the number of sectors to write mov cx,word ptr [sectors_fat] dec cx fat1: push ax push cx push dx call write_ths jnc fat1_ths_ok pop ax pop ax pop ax mov dx,offset write_fat_error jmp write_error fat1_ths_ok: mov bx,offset fat_dir_record mov byte ptr [retry_count],3 fat1_retry: call write_disk jnc fat1_write_ok dec byte ptr [retry_count] jnz fat1_retry pop ax pop ax pop ax mov dx,offset write_fat_error jmp write_error fat1_write_ok: pop dx pop cx pop ax ; increment the absolute sector number add ax,1 adc dx,0 loop fat1 jnc write_fat mov dx,offset write_fat_error jmp write_error ;*********************************************************************** ; write FAT2 ;*********************************************************************** write_fat: mov byte ptr [retry_count],3 fat_retry: mov ax,0301h mov bx,offset fat_record xor ch,ch mov cl,byte ptr [sectors_fat] add cl,2 xor dh,dh mov dl,byte ptr [drive_number] int 13h jnc finish_fat2 dec byte ptr [retry_count] jnz fat_retry mov dx,offset write_fat_error jmp write_error finish_fat2: ; set the starting sector (absolute) mov ax,2 xor dx,dx add ax,word ptr [sectors_fat] adc dx,0 ; set the number of sectors to write mov cx,word ptr [sectors_fat] dec cx fat2: push ax push cx push dx call write_ths jnc fat2_ths_ok pop ax pop ax pop ax mov dx,offset write_fat_error jmp write_error fat2_ths_ok: mov bx,offset fat_dir_record mov byte ptr [retry_count],3 fat2_retry: call write_disk jnc fat2_write_ok dec byte ptr [retry_count] jnz fat2_retry pop ax pop ax pop ax mov dx,offset write_fat_error jmp short write_error fat2_write_ok: pop dx pop cx pop ax ; increment the absolute sector number add ax,1 adc dx,0 loop fat2 jnc write_dir mov dx,offset write_fat_error jmp short write_error ;*********************************************************************** ; write the directory ;*********************************************************************** write_dir: mov dx,offset dir_message mov ah,09h int 21h ; set the starting sector (absolute) xor dx,dx mov ax,word ptr [sectors_fat] mul byte ptr [fats] add ax,word ptr [reserved_sectors] adc dx,0 ; set the number of sectors to write xor ch,ch mov cl,byte ptr [sectors_directory] dir: push ax push cx push dx call write_ths jnc dir_ths_ok pop ax pop ax pop ax mov dx,offset write_dir_error jmp short write_error dir_ths_ok: mov bx,offset fat_dir_record mov byte ptr [retry_count],3 dir_retry: call write_disk jnc dir_write_ok dec byte ptr [retry_count] jnz dir_retry pop ax pop ax pop ax mov dx,offset write_dir_error jmp short write_error dir_write_ok: pop dx pop cx pop ax ; increment the absolute sector number add ax,1 adc dx,0 loop dir jnc transfer mov dx,offset write_dir_error jmp short write_error transfer: cmp byte ptr [system],1 jne exit ; transfer the system files call load_system jnc exit ; set exit code to 5 (error copying system files) mov ax,4c05h push ax jmp short error_exit write_error: mov ah,09h int 21h ; set exit code to 1 (format, verify, or write error) mov ax,4c01h push ax jmp short error_exit ;*********************************************************************** ; clean up and exit ;*********************************************************************** exit: ; set exit code to 0 (Success) mov ax,4c00h push ax error_exit: ; restore the original disk parameter table pointer mov cx,word ptr [parameter_pointer] mov dx,word ptr [parameter_pointer+2] push ds xor ax,ax mov ds,ax assume ds:0000h cli mov word ptr ds:[0078h],cx mov word ptr ds:[007ah],dx sti pop ds assume ds:cseg ; park the heads on track 0 mov ah,0ch xor cl,cl xor dh,dh mov dl,byte ptr [drive_number] int 13h pop ax push ax cmp al,0 jne error_out ; display format complete message mov dx,offset exit_message mov ah,09h int 21h error_out: ; restore the cursor mov cx,word ptr [cursor] mov ah,01h int 10h ; terminate with exit code (0 = Success, else = Failure) pop ax int 21h ;*********************************************************************** ; format subroutines ;*********************************************************************** ;*********************************************************************** ; information display during format ;*********************************************************************** display_format: xor ah,ah aam or ax,3030h mov byte ptr [format_track_number+1],al cmp ah,30h jne format_digit mov ah,20h format_digit: mov byte ptr [format_track_number],ah mov al,bl xor ah,ah aam or ax,3030h mov byte ptr [format_head_number],al mov dx,offset format_display mov ah,09h int 21h ret ;*********************************************************************** ; information display during verification ;*********************************************************************** display_verify: xor ah,ah aam or ax,3030h mov byte ptr [verify_track_number+1],al cmp ah,30h jne verify_digit mov ah,20h verify_digit: mov byte ptr [verify_track_number],ah mov al,bl xor ah,ah aam or ax,3030h mov byte ptr [verify_head_number],al mov dx,offset verify_display mov ah,09h int 21h ret ;*********************************************************************** ; increment the track number in the sector tables ;*********************************************************************** next_track: xor ch,ch mov cl,36 mov bx,offset sector_table_0 track_counter: inc byte ptr [bx] add bx,4 loop track_counter ret ;*********************************************************************** ; format (and possibly verify) both heads on one track ;*********************************************************************** format_track: mov al,byte ptr [sector_table_0] mov bl,0 ; display formatting message call display_format mov byte ptr [retry_count],3 head_0_retry: mov ah,5 mov al,byte ptr [sectors_track] mov bx,offset sector_table_0 mov ch,byte ptr [bx] mov cl,byte ptr [bx+2] mov dh,byte ptr [bx+1] mov dl,byte ptr [drive_number] int 13h jnc short head_0_ok cmp ah,80h jne no_timeout mov dx,offset no_floppy_error stc ret no_timeout: cmp ah,03h jne not_protected mov dx,offset write_protect_error stc ret not_protected: dec byte ptr [retry_count] jnz head_0_retry mov dx,offset error_during_format stc ret head_0_ok: cmp byte ptr [verify],1 je verify_head_0 ; check for proper floppy type ; ; this is done via a verification of tracks 0, ; 59, and 79, for head 0 only, for all 1.2 Mb disks ; not being verified, and on track 0 head 0 only, for ; 360 Kb, 720 Kb and 1.44 Mb disks not being verified. ; ; the following combinations will fail at track 0: ; ; 1: trying to format a 1.2 Mb floppy to 360 Kb ; 2: trying to format a 720 Kb floppy to 1.44 Mb * ; 3: trying to format a 1.44 Mb floppy to 720 Kb * ! ; ; the following combination will fail at track 79: ; ; 1: trying to format a 360 Kb floppy to 1.2 Mb (it will ; probably fail sooner, but it is sure to here) ; ; notes: ; ; * - these combinations may not fail if the drive- ; controller combination does not properly detect ; the the floppy type. ; ; ! - this combination may not fail in 720 Kb drives. ; ; While certain non-standard combinations may not fail, ; either the data on the disk may be at risk, or your ; pocketbook may suffer, if you insist on using them. cmp byte ptr [sector_table_0],0 je verify_type cmp byte ptr [disk_size],2 je one_point_two jmp short format_head_1 one_point_two: cmp byte ptr [sector_table_0],59 je verify_type cmp byte ptr [sector_table_0],79 je verify_type jmp short format_head_1 verify_type: mov ah,04h int 13h jnc format_head_1 mov dx,offset disk_type_error stc ret verify_head_0: pushf pusha mov al,byte ptr [sector_table_0] mov bl,0 ; display verifying message call display_verify popa popf mov ah,04h int 13h jnc format_head_1 mov dx,offset verify_error stc ret format_head_1: pushf pusha mov al,byte ptr [sector_table_0] mov bl,1 ; display formatting message call display_format popa popf mov byte ptr [retry_count],3 head_1_retry: mov ah,5 mov al,byte ptr [sectors_track] mov bx,offset sector_table_1 mov ch,byte ptr [bx] mov cl,byte ptr [bx+2] mov dh,byte ptr [bx+1] mov dl,byte ptr [drive_number] int 13h jnc short head_1_ok dec byte ptr [retry_count] jnz head_1_retry mov dx,offset error_during_format stc ret head_1_ok: cmp byte ptr [verify],1 je verify_head_1 format_return: clc ret verify_head_1: pushf pusha mov al,byte ptr [sector_table_0] mov bl,1 ; display verifying message call display_verify popa popf mov ah,04h int 13h jnc format_return mov dx,offset verify_error stc ret ;*********************************************************************** ; calculate the track, head, and sector for a disk write ;*********************************************************************** write_ths: ; check for a valid absolute sector number cmp dx,word ptr [sectors_track] jb write_ths_ok stc ret write_ths_ok: ; calculate the sector number div word ptr [sectors_track] inc dl ; store the sector number mov byte ptr [write_sector],dl ; calculate the head number and the track number xor dx,dx div word ptr [heads] ; store the head number mov byte ptr [write_head],dl ; store the track number mov word ptr [write_track],ax clc ret ;*********************************************************************** ; write one sector to the disk ;*********************************************************************** write_disk: ; get the track number mov dx,word ptr [write_track] ; shift the upper 2 bits of the track number ; into the upper 2 bits of the sector number mov cl,06 shl dh,cl ; get the sector number or dh,byte ptr [write_sector] mov cx,dx xchg ch,cl ; get the drive number mov dl,byte ptr [drive_number] ; get the head number mov dh,byte ptr [write_head] ; write one sector to the disk mov ax,0301h int 13h ret ;*********************************************************************** ; transfer system files ;*********************************************************************** bios_source_path: db "d:\" bios_source_filename: db "IBMBIO.COM",000h,008h db "$" bios_destination_path: db "d:\" bios_destination_filename: db "IBMBIO.COM",000h,008h db "$" dos_source_path: db "d:\" dos_source_filename: db "IBMDOS.COM",000h,008h db "$" dos_destination_path: db "d:\" dos_destination_filename: db "IBMDOS.COM",000h,008h db "$" cmd_source_path: db "d:\" cmd_source_filename: db "COMMAND.COM",000h,008h db "$" cmd_destination_path: db "d:\" cmd_destination_filename: db "COMMAND.COM",000h,008h db "$" load_system: mov dx,offset file_copy_message mov ah,09h int 21h ; get default drive mov ah,19h int 21h ; set source drive add al,41h mov byte ptr [bios_source_path],al mov byte ptr [dos_source_path],al mov byte ptr [cmd_source_path],al ; set destination drive mov al,byte ptr [drive_number] add al,41h mov byte ptr [bios_destination_path],al mov byte ptr [dos_destination_path],al mov byte ptr [cmd_destination_path],al ; copy IBMBIOS.COM mov bx,offset file_buffer mov cx,word ptr [buffer_size] mov si,offset bios_source_path mov di,offset bios_destination_path call file_copy jc load_error ; copy IBMDOS.COM mov bx,offset file_buffer mov cx,word ptr [buffer_size] mov si,offset dos_source_path mov di,offset dos_destination_path call file_copy jc load_error ; copy COMMAND.COM mov bx,offset file_buffer mov cx,word ptr [buffer_size] mov si,offset cmd_source_path mov di,offset cmd_destination_path call file_copy jc load_error clc ret load_error: stc ret ;*********************************************************************** ; copy a file ; ; input: ; ; bx = pointer to copy buffer ; cx = copy buffer size in bytes ; si = pointer to source path\filename ; di = pointer to destination path\filename ;*********************************************************************** source: dw ? destination: dw ? read_handle: dw ? write_handle: dw ? file_time: dw ? file_date dw ? file_attr: dw ? bytes_read: dw ? copy_buffer_size: dw ? copy_buffer: dw ? ; file copy messages file_not_found: db " - error opening input file",00Dh,00Ah db "$" file_create_error: db " - error creating output file",00Dh,00Ah db "$" file_read_error: db " - error reading file",00Dh,00Ah db "$" file_write_error: db " - error writing file",00Dh,00Ah db "$" file_copy: mov word ptr [copy_buffer],bx mov word ptr [copy_buffer_size],cx mov word ptr [source],si mov word ptr [destination],di mov dx,word ptr [source] mov ah,3dh xor al,al int 21h ; open source file mov word ptr [read_handle],ax jnc file_open ; display file not found message mov dx,offset cr_lf call write_message mov dx,word ptr [source] call write_message mov dx,offset file_not_found call write_message jmp load_error file_open: mov ax,5700h mov bx,word ptr [read_handle] int 21h ; get file time and date mov word ptr [file_time],cx mov word ptr [file_date],dx mov ax,4300h mov dx,word ptr [source] int 21h ; get file attributes mov word ptr [file_attr],cx mov al,byte ptr [drive_number] mov cx,0000h mov dx,word ptr [destination] mov ah,3ch int 21h ; create destination file mov word ptr [write_handle],ax jnc copy_cmd ; display file not created message mov dx,offset cr_lf call write_message mov dx,word ptr [destination] call write_message mov dx,offset file_create_error call write_message jmp copy_error copy_cmd: call read_file jnc file_read_ok ; display file read error message mov dx,offset cr_lf call write_message mov dx,word ptr [source] call write_message mov dx,offset file_read_error call write_message jmp copy_error file_read_ok: call write_file jnc file_write_ok ; display file write error message mov dx,offset cr_lf call write_message mov dx,word ptr [destination] call write_message mov dx,offset file_write_error call write_message jmp copy_error file_write_ok: cmp ax,word ptr [copy_buffer_size] je copy_cmd mov bx,word ptr [read_handle] mov ah,3eh int 21h ; close read_file mov ax,5701h mov bx,word ptr [write_handle] mov cx,word ptr [file_time] mov dx,word ptr [file_date] int 21h ; set file time and date mov bx,word ptr [write_handle] mov ah,3eh int 21h ; close write_file mov ax,4301h mov cx,word ptr [file_attr] mov dx,word ptr [destination] int 21h ; set file attributes clc ret copy_error: stc ret ;*********************************************************************** ; issue a file copy error message ;*********************************************************************** write_message: mov ah,09h int 21h ret ;*********************************************************************** ; read file block into buffer ;*********************************************************************** read_file: mov cx,word ptr [copy_buffer_size] mov bx,word ptr [read_handle] mov dx,word ptr [copy_buffer] mov ah,3fh int 21h mov word ptr [bytes_read],ax mov cx,ax ret ;*********************************************************************** ; write file block from buffer ;*********************************************************************** write_file: mov bx,word ptr [write_handle] mov dx,word ptr [copy_buffer] mov ah,40h int 21h ret ;*********************************************************************** ; boot sector data (track 0, head 0, sector 1) ;*********************************************************************** boot_record: ;*********************************************************************** .8086 ; enable the 8086 instruction set ;*********************************************************************** jmp boot_strap ; skip over data areas ;*********************************************************************** ; DOS data area ;*********************************************************************** oem_name: db "FMT 1.00" bytes_sector: dw 512 sectors_cluster: db ? ; filled in before writing boot reserved_sectors: dw 1 fats: db 2 directory_size: dw ? ; filled in before writing boot small_total: dw ? ; filled in before writing boot descriptor: db ? ; filled in before writing boot sectors_fat: dw ? ; filled in before writing boot sectors_track: dw ? ; filled in before writing boot heads: dw 2 hidden_sectors: dd 0 big_total: dd 0 physical_drive: db 0 reserved: db 0 extended_boot: db 029h sn_low: dw ? ; filled in before writing boot sn_high: dw ? ; filled in before writing boot volume_label: db "NO NAME " fat_type: db "FAT12 " ;*********************************************************************** ; bootstrap program data area ;*********************************************************************** sectors_directory: db ? ; filled in before writing boot dir_start: dd ? ; filled in before writing boot data_start: dd ? ; filled in before writing boot track: dw ? ; boot program variable head: db ? ; boot program variable sector: db ? ; boot program variable disk_table: db 11 dup (?) ; filled in before writing boot bios_name: db "IBMBIO COM" dos_name: db "IBMDOS COM" ; boot program messages reset_message: db 00Dh,00Ah,00Ah db "Disk controller error ..." db 000h system_message: db 00Dh,00Ah,00Ah db "No system on disk or disk read error ..." db 000h retry_message: db 00Dh,00Ah,00Ah db "Press any key to retry boot." db 000h ;*********************************************************************** boot_strap: ; temporarily disable interrupts cli ; set the stack segment and the extra segment to 0000 ; DOS loads the boot record at 0000:7c00, ; so put the stack just below it xor ax,ax mov es,ax mov ss,ax mov sp,7c00h assume es:0000h,ss:0000h ; set up the parameters used by the BIOS program loader lds si,es:[0078h] mov bx,0078h ; save current disk parameter table push ds push si push ss push bx ; set the data segment to 0000 mov ds,ax assume ds:0000h ; use the disk parameter table in the floppy boot record mov word ptr [bx],7c00h+disk_table-boot_record mov word ptr [bx+02],ax ; enable interrupts sti ; reset the disk controller int 13h jnc reset_ok ; display reset error message mov si,7c00h+offset reset_message-offset boot_record call write_string jmp not_bootable reset_ok: ; get directory start sector (absolute sector number) mov ax,word ptr ds:[7c00h+dir_start-boot_record] mov dx,word ptr ds:[7c00h+dir_start-boot_record+2] ; get track, head, and sector call calculate_ths jnc dir_sector_ok ; display system error message mov si,7c00h+offset system_message-offset boot_record call write_string jmp not_bootable dir_sector_ok: ; set the offset of the disk read buffer mov bx,0500h ; read the first directory sector call read_disk jc not_bootable ; check directory for the BIOS file ; (it must be the first directory entry) mov di,bx mov cx,11 mov si,7c00h+offset bios_name-offset boot_record rep cmpsb jz dir_read_ok ; display system error message mov si,7c00h+offset system_message-offset boot_record call write_string jmp not_bootable dir_read_ok: ; check directory for the DOS file ; (it must be the second directory entry) lea di,[bx+32] mov si,7c00h+offset dos_name-offset boot_record mov cx,11 rep cmpsb ; valid directory, attempt to boot from the disk jz system_found ; display system error message mov si,7c00h+offset system_message-offset boot_record call write_string jmp not_bootable ;*********************************************************************** system_found: ; get the data start sector (absolute sector number) mov ax,word ptr ds:[7c00h+data_start-boot_record] mov dx,word ptr ds:[7c00h+data_start-boot_record+2] ; set the offset of the disk read buffer mov bx,0700h ; read 3 sectors (the length of the BIOS program loader) mov cx,3 read_next: push ax push cx push dx ; get track, head, and sector call calculate_ths jnb ok_to_read ; clean up the stack pop ax pop ax pop ax ; display system error message mov si,7c00h+offset system_message-offset boot_record call write_string jmp not_bootable ok_to_read: call read_disk pop dx pop cx pop ax jnc disk_read_ok ; display system error message mov si,7c00h+offset system_message-offset boot_record call write_string jmp not_bootable disk_read_ok: ; increment the sector to read add ax,1 adc dx,0 ; bump the offset of the read buffer by the sector size add bx,word ptr ds:[7c00h+bytes_sector-boot_record] loop read_next ; set up the parameters for the BIOS program loader ; get the media descriptor mov ch,byte ptr ds:[7c00h+descriptor-boot_record] ; get the drive number mov dl,byte ptr ds:[7c00h+physical_drive-boot_record] ; get the data start sector (absolute sector number) mov ax,word ptr ds:[7c00h+data_start-boot_record+2] mov bx,word ptr ds:[7c00h+data_start-boot_record] ; execute the BIOS program loader jmp far ptr program_loader ;*********************************************************************** not_bootable: ; display retry message mov si,7c00h+offset retry_message-offset boot_record call write_string ; wait for a key to be pressed xor ah,ah int 16h ; clean up the stack pop si pop ds pop [si] pop [si+2] ; try to boot the system again int 19h ;*********************************************************************** ; bootstrap subroutines ;*********************************************************************** write_string: ; get one character lodsb ; check for end of ASCIIZ string or al,al jnz next_char ret next_char: ; write one character to the screen mov ah,0eh ; use video page 0, normal white mov bx,0007h int 10h jmp write_string ;*********************************************************************** read_disk: ; get the track number mov dx,word ptr ds:[7c00h+track-boot_record] ; shift the upper 2 bits of the track number ; into the upper 2 bits of the sector number mov cl,06 shl dh,cl ; get the sector number or dh,byte ptr ds:[7c00h+sector-boot_record] mov cx,dx xchg ch,cl ; get the drive number mov dl,byte ptr ds:[7c00h+physical_drive-boot_record] ; get the head number mov dh,byte ptr ds:[7c00h+head-boot_record] ; read one sector from the disk mov ax,0201h int 13h ret ;*********************************************************************** calculate_ths: ; check for valid absolute sector number cmp dx,word ptr ds:[7c00h+sectors_track-boot_record] jb ths_ok stc ret ths_ok: ; calculate the sector number div word ptr ds:[7c00h+sectors_track-boot_record] inc dl ; store the sector number mov byte ptr ds:[7c00h+sector-boot_record],dl ; calculate the head number and the track number xor dx,dx div word ptr ds:[7c00h+heads-boot_record] ; store the head number mov byte ptr ds:[7c00h+head-boot_record],dl ; store the track number mov word ptr ds:[7c00h+track-boot_record],ax clc ret ;*********************************************************************** ; adjust the boot record size to 512 bytes ;*********************************************************************** fill: db (512-(offset fill-offset boot_record)-2) dup (0) ;*********************************************************************** ; boot sector signature ;*********************************************************************** signature: dw 0AA55h ;*********************************************************************** ; end of boot sector data ;*********************************************************************** ;*********************************************************************** ; FAT and directory data ;*********************************************************************** ; the first FAT sector has the media descriptor byte fat_record: db ? ; filled in before writing FAT db 0ffh,0ffh db 509 dup (0) ; other FAT sectors and directory sectors are all zeros fat_dir_record: db 512 dup (0) ;*********************************************************************** ; copyright information ;*********************************************************************** versn: db "FMT version 1.00 23-JAN-1990",000h copyright: db "Copyright (C) 1990 by Clair Alan Hardesty",000h ;*********************************************************************** ; put file copy buffer after code and other data ;*********************************************************************** file_buffer: ; buffer space starts here cseg ends end entry_point ;*********************************************************************** ; end of Fmt.asm ;***********************************************************************