title Prepare VHARD floppies subttl Prologue page 60,132 comment { ****************************************************************************** File VHPREP.ASM Author: Aaron L. Brenner BIX mail address albrenner GEnie address A.BRENNER This program is hereby released to the public domain. Purpose: Prepare floppy diskettes for use with VHARD. This consists of the following: 1) Format the floppies to 10 sectors per track, 40 tracks (resulting in a capacity of 800K per diskette). 2) Write the VHARD boot sector to each disk containing, along with boot code, a disk ID and number. 3) Write a DOS boot sector, 2 FATs, and root directory to the first disk. All disk operations are performed through VHARDCTL, the character device driver installed with VHARD. Errorlevel returned: 0 All is well 1 VHARD.SYS is not installed Revision history: 1.00 07/11/90 ALB Created. ****************************************************************************** endcomment { subttl Included files page include dd_defs.inc include vhard.inc subttl Program data and stack page vhprep_data segment para ID_to_use db 8 dup(' ') ; ID to use for the diskettes disk_num db 0ffh ; Number for each diskette track_buf db 512 * 10 dup(0) ; Track buffer for reading and writing drive_num db 0 ; The drive assigned to VHARD vhard_vnum dw 0 ; Version of VHARD VHARD_BPB DOS_BPB <> ; BPB gotten from VHARD dw 81h ; Pointer to our command tail our_PSP dw 0 ; Our PSP segment ; ; This block is used to pass commands to VHPREP ; command_blk VH_CMD <> vhctl_name db 'VHARDCTL',0 ; Name of control driver for VHARD vhctl_handle dw 0 ; Handle for VHARDCTL not_installed db 13,10,'The VHARD driver is not installed.',13,10,'$' insert_disk db 13,10,'Put a blank diskette in drive A:, then press' db ' a key $' label_disk db 'Label this diskette $' disk_bad db 7,13,10,'This diskette is unusable!',13,10,'$' crlf_str db 13,10,'$' err_prefix db 7,'Error: $' err0 db 'spurious error message$' err1 db 'drive/adapter failed or no disk in drive$' err2 db 'seek operation failed$' err3 db 'disk adapter failed$' err4 db 'CRC error$' err5 db 'attempt to DMA across segment boundary$' err6 db 'DMA overrun$' err7 db 'sector not found$' err8 db 'diskette is write-protected$' err9 db 'address mark not found$' err10 db 'invalid BIOS command$' err11 db 'internal command error$' err0ff db 'unknown BIOS error code$' error_msgs dw err0, err1, err2, err3, err4, err5, err6, err7, err8 dw err9, err10, err11, err0, err0, err0ff vhprep_data ends vhprep_stack segment word stack dw 256 dup(0) vhprep_stack ends ;***************************************************************************** ; ; This gets written to the actual boot sector of each diskette. As you can ; see, it just spits out a message, waits for a key, then tries to IPL again. ; ;***************************************************************************** boot_code_seg segment para assume cs:boot_code_seg, ds:boot_code_seg org 0 boot_code proc near jmp short boot_msg_disp ; Jump to message display code db 17 dup(?) ; Place keeper for disk ID & number boot_msg_disp: cld ; Make sure of direction call do_display ; Do the display db 7,'This is NOT a bootable diskette!',13,10 db 'Put a different disk in the drive, and press a key to' db ' try again',13,10,0 do_display: pop si ; Point to the string to display ddsp_l1: lods byte ptr cs:[si] ; Get a byte or al,al ; End of string yet? jz ddsp_l2 ; Yep - just hang up now mov ah,14 ; BIOS "Write TTY" function int 10h ; jmp short ddsp_l1 ; Loop back for the rest ddsp_l2: sub ah,ah ; Get a key int 16h ; int 19h ; Try to IPL again jmp $ ; Hang up now boot_code_end label byte boot_code endp boot_code_seg ends subttl Start of program code page vhprep_code segment assume cs:vhprep_code, ds:vhprep_data, es:vhprep_data, ss:vhprep_stack start proc call initialize ; Set things up call do_prep ; Do the preparation call terminate ; Do program cleanup mov ax,4c00h ; int 21h ; start endp ;***************************************************************************** ; ; Program initialization. ; ; Make sure the VHARDCTL driver is present, and get driver info from it. ; ;***************************************************************************** initialize proc near mov ax,vhprep_data ; Get our data segment mov ds,ax ; mov our_PSP,es ; Save our PSP segment mov es,ax ; mov dx,offset vhctl_name ; Try to open VHARDCTL, read/write mov ax,3d02h ; int 21h ; jnc init_l1 ; If it opened, make sure it's a dev init_noway: mov dx,offset not_installed ; Complain that it isn't there mov ah,9 ; int 21h ; mov ax,4c01h ; Exit with errorlevel of 1 int 21h ; init_l1: mov vhctl_handle,ax ; Save the handle for other ops mov bx,ax ; Get the handle for IOCTL call mov ax,4400h ; Get info on this guy int 21h ; test dl,80h ; Is this a device? jz init_noway ; Nope - no way we're doing anything mov command_blk.VC_cmd_code,CMD_GETDATA mov word ptr command_blk.VC_buffer[0],offset drive_num mov word ptr command_blk.VC_buffer[2],ds mov ax,4403h ; Get data from VHARDCTL mov dx,offset command_blk ; mov cx,size VH_CMD ; int 21h ; call get_cmdline_parms ; Get a parameters from the cmd line cmp byte ptr ID_to_use[0],' '; Did they supply an ID? jne init_exit ; Yep - don't change it mov ah,0 ; Get timer count from BIOS int 1ah ; Returns CX:DX mov ax,'HV' ; Set up disk ID mov word ptr ID_to_use[0],ax; mov ax,cx ; Use the time as part of the ID mov di,offset ID_to_use[2] ; call store2digits ; mov al,dh ; call store2digits ; mov al,dl ; call store2digits ; init_exit: ret ; Return to Main initialize endp ;***************************************************************************** ; ; Get any command line parameters ; ; The only parameters allowed are a disk ID and a disk number. ; ;***************************************************************************** get_cmdline_parms proc near push es ; Save it for this les si,dword ptr our_PSP[-2]; Point to the command tail call skip_spaces ; Skip any whitespace mov cx,8 ; Max to copy mov di,offset ID_to_use ; gcmp_l1: lods byte ptr es:[si] ; Get a byte cmp al,' ' ; Whitespace? je gcmp_l3 ; Yep - stop copying cmp al,9 ; je gcmp_l3 ; cmp al,0dh ; End of it? je gcmp_exit ; Yep - exit cmp al,'a' ; Lower case? jb gcmp_l2 ; No - never mind cmp al,'z' ; ja gcmp_l2 ; xor al,20h ; Make it upper case gcmp_l2: jcxz gcmp_l1 ; Don't store if ID is full mov [di],al ; Save the byte inc di ; Point to next spot dec cx ; Knock off the count jmp gcmp_l1 ; Loop back gcmp_l3: call skip_spaces ; Skip any other whitespace sub bl,bl ; Clear accumulator gcmp_l4: lods byte ptr es:[si] ; Pick up a byte cmp al,'0' ; Valid digit? jb gcmp_l5 ; Nope - stop now cmp al,'9' ; ja gcmp_l5 ; sub al,'0' ; Make digit binary mov bh,bl ; Multiply current value by 10 shl bh,1 ; by (BL * 2) + (BL * 8) shl bl,1 ; shl bl,1 ; shl bl,1 ; add bl,al ; Add in new digit jmp short gcmp_l4 ; Loop back gcmp_l5: cmp bl,12 ; Make sure it's legal ja gcmp_exit ; mov disk_num,bl ; Save the number gcmp_exit: pop es ; ret ; Return to initialization get_cmdline_parms endp ;***************************************************************************** ; ; Skip whitespace characters at ES:SI ; ;***************************************************************************** skip_spaces proc near sksp_l1: mov al,es:[si] ; Get a byte cmp al,' ' ; Whitespace? je sksp_l2 ; Yep - skip it cmp al,9 ; jne sksp_exit ; No - exit now sksp_l2: inc si ; Skip this character jmp short sksp_l1 ; Loop back sksp_exit: ret ; Return to caller skip_spaces endp ;***************************************************************************** ; ; Convert AL to 2 hex digits, storing them at DI. ; ;***************************************************************************** store2digits proc near push ax ; Save it for a second mov cl,4 ; Move high nybble down shr al,cl ; call store_nybble ; Store it as ASCII pop ax ; Get low nybble back store_nybble: and al,0fh ; Just keep low nybble add al,90h ; Convert to ASCII hex daa ; adc al,40h ; daa ; stosb ; Store digit ret ; Return to caller store2digits endp ;***************************************************************************** ; ; Take care of any cleanup prior to returning to DOS. ; All we do is close the handle for VHARDCTL. ; ;***************************************************************************** terminate proc near mov bx,vhctl_handle ; Get the handle mov ah,3eh ; Close it int 21h ; ret ; Return to Main terminate endp ;***************************************************************************** ; ; Do the actual preparation ; ; Format disk 0, writing a DOS boot sector, 2 copies of the FAT, and an ; empty root directory (empty except for a volume label that is the disk ID). ; ; Once disk 0 is taken care of, just format the rest of the disks. ; ;***************************************************************************** do_prep proc near cmp disk_num,0ffh ; Did they specify a disk? je dopr_l2 ; No - do all disks cmp disk_num,0 ; Is it the first disk? jne dopr_l1 ; No - do a normal disk call do_first_disk ; Do just the first disk jmp short dopr_exit ; Exit dopr_l1: call do_one_disk ; Just do this disk jmp short dopr_exit ; dopr_l2: mov disk_num,0 ; call do_first_disk ; Set up the first disk mov cx,12 ; 12 more to do mov disk_num,1 ; Starting with disk 1 dopr_l3: call do_one_disk ; Do this disk inc disk_num ; Set for next one loop dopr_l3 ; Loop for all 12 disks dopr_exit: ret ; Return to Main do_prep endp ;***************************************************************************** ; ; Prepare the first diskette. This requires special treatment because it also ; contains the DOS boot sector, 2 copies of a FAT, and a root directory. ; ;***************************************************************************** do_first_disk proc near dfdk_l1: call format_disk ; Format the first disk call copy_boot_sect ; Copy the boot sector to track buf mov di,offset track_buf[512].DBS_BPB mov si,offset VHARD_BPB mov cx,size DOS_BPB rep movsb mov word ptr track_buf[512 * 2],0fffch ; Set up 2 empty FATs mov track_buf[(512 * 2) + 2],0ffh ; mov word ptr track_buf[6 * 512],0fffch ; mov track_buf[(6 * 512) + 2],0ffh ; sub ax,ax ; call write_track ; Write it jc dfdk_err ; If unable, complain call clear_track_buf ; Wipe it clean mov di,offset track_buf ; Point to first root dir entry mov si,offset ID_to_use ; Use the disk ID as the vol label mov cx,4 ; rep movsw ; mov al,' ' ; Fill it out with spaces stosb ; stosb ; stosb ; mov al,8 ; Attribute byte for volume label stosb ; mov cx,5 ; Fill 10 reserved bytes w/0 sub al,al ; rep stosw ; mov ah,2ch ; Get time from DOS int 21h ; mov ah,ch ; Get the hour shl ah,1 ; Put it in the high 5 bits shl ah,1 ; shl ah,1 ; mov ch,0 ; Make the minutes a word mov bx,cx ; Into a temp register mov cl,5 ; Move the minutes into middle 6 bits shl bx,cl ; or ax,bx ; Add to time shr dh,1 ; Make seconds 2-sec intervals or al,dh ; Finish with that stosw ; Put the timestamp into the dir entry mov ah,2ah ; Get the date from DOS int 21h ; sub cx,1980 ; Make the year the way DOS wants mov ax,cx ; mov cl,7 ; Move to high 7 bits ror ax,cl ; mov bl,dh ; Get the month sub bh,bh ; as a word mov cl,5 ; Put it in the middle shl bx,cl ; or ax,bx ; Add to date or al,dl ; Add the days stosw ; Put it in the dir entry mov ax,100h ; Write track 0, head 1 call write_track ; jc dfdk_err call show_label ; Tell 'em how to label it dfdk_exit: ret ; Return to caller ; dfdk_err: call report_error ; Complain about the error mov dx,offset disk_bad ; Let 'em know the disk is bad mov ah,9 ; int 21h ; jmp dfdk_l1 do_first_disk endp ;***************************************************************************** ; ; Prepare a disk other than the first one. Just format it and write a boot ; sector. ; ;***************************************************************************** do_one_disk proc near dodk_l1: call format_disk ; Format this disk call copy_boot_sect ; Copy the boot sector to track_buf mov al,0 ; Write this track call write_track ; jnc dodk_exit ; call report_error ; Complain about the error mov dx,offset disk_bad ; Let 'em know the disk is bad mov ah,9 ; int 21h ; jmp short dodk_l1 dodk_exit: call show_label ; Tell 'em what the label is ret ; Return to caller do_one_disk endp ;***************************************************************************** ; ; Copy the boot sector into the first sector of the track buffer ; ;***************************************************************************** copy_boot_sect proc near save cx, si, di call clear_track_buf ; Wipe out our track buffer push ds ; Save Our Segment mov cx,boot_code_seg ; Point to our boot code mov ds,cx ; assume ds:boot_code_seg mov si,offset boot_code ; Point to the boot code mov cx,offset boot_code_end ; Number of bytes to copy mov di,offset track_buf ; Where to put it rep movsb ; Copy boot code to track buffer pop ds ; Get our segment back assume ds:vhprep_data mov si,offset ID_to_use ; Point to disk ID mov di,offset track_buf.BS_disk_id mov cx,4 ; rep movsw ; Copy disk ID to boot sector mov cl,disk_num ; Copy disk # to boot sector mov track_buf.BS_disk_num,cl; sub ax,ax ; Write track 0/head 0 call write_track ; restore ret ; Return to caller copy_boot_sect endp ;***************************************************************************** ; ; Fill the track buffer with 0s. ; ;***************************************************************************** clear_track_buf proc near save ax, cx, di mov cx,512 * 5 ; Number of words to clear mov di,offset track_buf ; What to clear sub ax,ax ; Fill with 0 cld ; Make sure of direction rep stosw ; Fill the buffer restore ret ; Return to caller clear_track_buf endp ;***************************************************************************** ; ; Prompt for a disk, then format all tracks. ; ;***************************************************************************** format_disk proc near save ax, cx, dx fmtd_l1: mov dx,offset insert_disk ; Display the prompt mov ah,9 ; int 21h ; mov ax,0c08h ; Get a key int 21h ; or al,al ; Fn key? jnz fmtd_l2 ; Nope - continue mov ah,8 ; Read the second character int 21h ; fmtd_l2: mov dx,offset crlf_str ; Newline mov ah,9 ; int 21h ; sub ax,ax ; Start with track 0 ; inc ah ; mov cx,40 ; 40 tracks to do fmtd_l3: call format_track ; Format head 0 of the track jc fmtd_err ; Complain on error xor ah,1 ; Other head call format_track ; jc fmtd_err ; Complain on error xor ah,1 ; Head 0 again inc al ; Next cylinder loop fmtd_l3 ; Loop for the whole disk restore ret ; Return to caller fmtd_err: ; ; An error occurred during the format. Report the error, let them know the ; disk is unusable, then prompt for another. ; call report_error ; Report the error mov dx,offset disk_bad ; Let 'em know the disk is bad mov ah,9 ; int 21h ; jmp fmtd_l1 ; Try with another disk format_disk endp ;***************************************************************************** ; ; Issue a format command for the track in AL/head in AH ; ;***************************************************************************** format_track proc near save ax, bx, cx, dx, di ; ; Before we can do the format, we need to set up the track buffer with the ; array of format parameters. Each entry in the array is 4 bytes, structured: ; byte 0 track ; byte 1 head ; byte 2 sector ; byte 3 sector size (2 in this case) ; mov di,offset track_buf ; Point to the buffer mov cx,10 ; 10 entries mov bx,201h ; Starting with sector 1 fmtt_l1: stosb ; Store the track mov [di],ah ; Store the head inc di ; mov [di],bl ; Now the sector number inc bl ; inc di ; mov byte ptr[di],2 ; Sector size inc di ; loop fmtt_l1 ; Loop for all entries mov command_blk.VC_cmd_code,CMD_FORMAT ; Command code ror ah,1 ; Put the head in the high bit or al,ah ; Add to track number mov command_blk.VC_track,al ; Pass it in the command block mov word ptr command_blk.VC_buffer[0],offset track_buf mov word ptr command_blk.VC_buffer[2],ds mov dx,offset command_blk ; Point to the block mov ax,4403h ; IOCTL Write to the VHARDCTL device mov bx,vhctl_handle mov cx,size VH_CMD int 21h cmp command_blk.VC_status,STS_OK + 1 ; Get the status cmc ; Set CF to 1 if error restore ret ; Return to caller format_track endp ;***************************************************************************** ; ; Write track_buf to the track in AL/head in AH ; ;***************************************************************************** write_track proc near save ax, bx, cx, dx mov command_blk.VC_cmd_code,CMD_WRITE ror ah,1 or al,ah mov command_blk.VC_track,al mov word ptr command_blk.VC_buffer[0],offset track_buf mov word ptr command_blk.VC_buffer[2],ds mov dx,offset command_blk mov ax,4403h mov bx,vhctl_handle mov cx,size VH_CMD int 21h cmp command_blk.VC_status,STS_OK + 1 cmc restore ret write_track endp ;***************************************************************************** ; ; Report an error based on the status byte in the command block. ; ;***************************************************************************** report_error proc near save ax, bx, dx mov dx,offset err_prefix ; Display the error message prefix mov ah,9 ; int 21h ; mov bl,command_blk.VC_status; Get returned status sub bh,bh ; Make it a word shl bx,1 ; Make it a word offset mov dx,error_msgs[bx] ; Get the appropriate error message mov ah,9 ; int 21h ; mov dx,offset crlf_str ; Newline mov ah,9 ; int 21h ; restore ret ; Return to caller report_error endp ;***************************************************************************** ; ; Tell the user what label to put on this diskette ; ;***************************************************************************** show_label proc near save ax, bx, cx, dx mov dx,offset label_disk ; Tell them to label the disk mov ah,9 ; int 21h ; mov dx,offset ID_to_use ; Tell them what to label it with mov cx,8 ; mov bx,1 ; mov ah,40h ; Use IOCTL Write to do it int 21h ; mov ah,2 ; mov dl,' ' ; int 21h ; mov ah,2 ; mov dl,'#' ; int 21h ; mov al,disk_num ; Display the disk number aam ; or ax,3030h ; push ax ; mov dl,ah ; mov ah,2 ; int 21h ; pop dx ; mov ah,2 ; int 21h ; mov dx,offset crlf_str ; Finish off with a newline mov ah,9 ; int 21h ; restore ret ; Return to caller show_label endp vhprep_code ends end start