;********* S E N D D I S K . A S M *******************************************
; A disk duplication utility to allow all sectors (including boot)
; of a floppy disk to be transmitted via a temporary file over a
; modem and replaced on another floppy disk.  With this utility,
; you should be able to distribute the IBM PC-DOS 4.0 fix disks
; with the SBBS system.  The temporary file is 368,640 bytes long
; for a 360k diskette and twice that for a 720k diskette.  This is long,
; but length should compress nicely using pkarc or equivalent.
; The .exe version of this file needs to be included in the .ARC
; file distributed to enable recreation of the disk.
;
; This program will allow a BOOTABLE disk to be transmitted via a
; modem and recreated at another location.
;
; created 2 November 1988
;  Jim Offenbecher, Software Librarian, Huntsville PC Users Group
;   13920 Creely Drive SW
;    Huntsville, AL  35803
;     Tel: [H] (205) 882-3205
;          [W] (205) 881-5741
;
; modified to: 1.  Allow 720k disk usage with message showing type.
;              2.  Check file length prior to disk write.
;              3.  Comment the source code.
;      4 November 1988 - jko  (per conversation with IBM SBBS)
;
;  This program including source code is hereby placed in the
;  Public Domain for all to use.  This program including source
;  code may be freely distributed provided that no fee is charged
;  for it or it's distribution.  Feel free to modify it providing
;  original credits are retained - it is quick and dirty to allow
;  early distribution of the DOS 4.0 fixes...
;    yes I know the error exits don't reset the stack on exit...
; assemble with MASM 5.0 or above - no macros or libraries needed.

.model small

.data
cr      equ     0dh
lf      equ     0ah

secsiz  equ     512
sectrk  equ     9
;track count for 3.5" 720k disks
trks7   equ     80
;track count for 5.25" 360k disks
trks    equ     40
sides   equ     2
trklen  equ     secsiz * sectrk

; number of clusters to check for when IDing the floppy
; if it's more than this, and the FAT ID is 0F9h, then it's a
; 5.25" 1.2 Meg and not a 720k 3.5" diskette...

clustchk        equ     750

signon  db      '            SENDDISK.EXE',cr,lf
        db      cr,lf
        db      'A Utility program to allow the IBM SBBS to',cr,lf
        db      'distribute the patches for IBM Dos 4.0 via',cr,lf
        db      'file mode.  This Program captures the entire',cr,lf
        db      'disk into a single file and/or recreates the',cr,lf
        db      'disk from the file.  It requires a disk which',cr,lf
        db      'has been formatted to 360k (9 secs per track)',cr,lf
        db      'or to 720k (80 tracks-9 secs per track- 3.5")',cr,lf
        db      'in the A: or B: drive. It is quick and dirty,',cr,lf
        db      'written in assembly, and is hereby placed in',cr,lf
        db      'the public domain for all to use.',cr,lf
        db      cr,lf
        db      '           by Jim Offenbecher',cr,lf
        db      '         SSS Computer Center Inc.',cr,lf
        db      '           Huntsville, Alabama',cr,lf
        db      'Software Librarian- Huntsville PC Users Group (P1239 AL)',cr,lf
        db      cr,lf
dirask  db      cr,lf,'Do you want to:',cr,lf
        db      '      1.  Read a floppy disk into a file on hard disk',cr,lf
        db      '      2.  Write a hard disk file onto a floppy disk',cr,lf
        db      '  Enter your choice (1-2)->','$'

askerr  db      cr,lf,'Must be a 1 or 2 only',cr,lf,'$'
as2err  db      cr,lf,'Must be an A or B only',cr,lf,'$'

askdsk  db      cr,lf,'Enter Floppy Disk Drive Designation (A-B)->','$'

typ360  db      cr,lf,'Diskette is 360k 5.25 inch',cr,lf,'$'
typ720  db      cr,lf,'Diskette is 720k 3.5 inch',cr,lf,'$'

askfil  db      cr,lf,'Enter Hard Disk File Name ->','$'

rerr001 db      cr,lf,'Error Reading Floppy Disk',cr,lf,'$'

werr001 db      cr,lf,'Error Writing Floppy Disk',cr,lf,'$'

serr001 db      cr,lf,'File Size does NOT match disk type',cr,lf,'$'

newlin  db      cr,lf,'$'

nofile  db      cr,lf,'File Not Found',cr,lf,'$'
badpath db      cr,lf,'Invalid Path',cr,lf,'$'
nohand  db      cr,lf,'No Handle Available',cr,lf,'$'
accden  db      cr,lf,'Access to File Denied',cr,lf,'$'
gopnerr db      cr,lf,'Error Opening File',cr,lf,'$'
gmaker  db      cr,lf,'Error Creating File',cr,lf,'$'
rfiler  db      cr,lf,'Error Reading File',cr,lf,'$'
wfiler  db      cr,lf,'Error Writing File',cr,lf,'$'
closerr db      cr,lf,'Error Closing File',cr,lf,'$'

fwtok   db      cr,lf,'Disk Capture Successful',cr,lf,'$'
dwtok   db      cr,lf,'Disk Re-Creation Successful',cr,lf,'$'

dotmsg  db      '.$'

; storage for temporary variables

;file name input buffer
bufinp  db      40      ;max length of buffer
chrcnt  db      0
;                1234567890123456789012345678901234567890
filsto  db      '                                         '

; ascii storage for the floppy drive involved
drivasc db      'A'

; binary storage for the floppy drive involved
dskbin  db      1

; disk type (number of tracks)
dsktyp  dw      80

;current sector storage for reading and writing floppy disk
cursec  dw      0

; storage for the file handle assigned to the temp file
handle  dw      0

; buffer for a single track of data...
bugbuff db      sectrk+1 dup (secsiz dup (?))

; executable code begins here...

.code
        mov     ax,@data        ;MASM 5.0+ find data segment
        mov     ds,ax           ;set up data segment
        mov     es,ax           ;set up extended segment too
restart:
        mov     dx,offset signon
        mov     ah,9
        int     21h             ;sign on the system
        mov     ah,1
        int     21h             ;get a single character
        cmp     al,'1'          ;check for make file condition

        jz      makfil          ;go make a file if so
        cmp     al,'2'          ;check for make disk condition
        jz      makdsk          ;go make a disk if a 2
        mov     dx,offset askerr
        mov     ah,9            ;print error if not 1 or 2
        int     21h
        jmp     restart         ;and ask again

;this module creates a file on the hard disk from all sectors on the
;specified floppy drive.  Drive type is checked to determine correct
;number of sectors to read.

makfil:
        call    getdisk         ;find out which floppy to use
        call    getfile         ;and the file name to use too
        call    makefile        ; make the file
        jc      exit            ;exit if error
        mov     cx,0            ;start at sector zero
        mov     cursec,cx
        mov     cx,dsktyp       ;get # tracks this type

; copy a single track from the floppy to a file, looping till all tracks done
fillop:
        push    cx              ;save track counter
        mov     dx,offset dotmsg;tell user another track
        mov     ah,9
        int     21h
        call    readdisk        ;go read track into buffer
        call    writfile        ;write buffer out to file
        mov     cx,cursec       ;get starting sector read last time
        add     cx,9
        mov     cursec,cx       ;bump the starting sector for next loop
        pop     cx              ;get track counter back
        dec     cx              ;one less track to do
        jnz     fillop          ;loop till all tracks done...

; all done writing the file, so bye-bye
        call    closfile        ;close the new file
        mov     dx,offset fwtok
        jmp     errexit         ;tell user OK, and leave the program


; this module writes all sectors on a floppy disk from data in a
; file on the hard disk.  The floppy must be formatted, and the file
; length must match the format of the floppy disk.

makdsk:
        call    getdisk         ;find out which floppy to use
        call    getfile         ;and which file to write to it
        call    openfile        ;open the file using handles
        jc      exit            ;abort if can't open
        call    lenchk          ;check length and abort if wrong
        mov     cx,0            ;initialize the sector to write to
        mov     cursec,cx
        mov     cx,dsktyp       ;and get the track count from storage

; this module reads one track's worth of data from the file and
; writes it out to the floppy, looping until 'dsktyp' tracks are written

fil2lop:
        push    cx              ;save the track counter on stack
        mov     dx,offset dotmsg        ;tell him we're working
        mov     ah,9
        int     21h
        call    readfile        ;get one tracks data from file
        call    writdisk        ;put it on the floppy
        mov     cx,cursec       ;get the old starting sector written
        add     cx,9            ;bump it one track's worth
        mov     cursec,cx       ;and save it for next loop
        pop     cx              ;get track counter back
        dec     cx              ;one less track to do
        jnz     fil2lop         ;loop till all tracks done...
;all done writing the floppy drive now...
        call    closfile        ;close the file
        mov     dx,offset dwtok
        jmp     errexit         ;tell user OK, and exit the program

;common exit point for errors... prints string at DS:DX and then exits...
errexit:
        mov     ah,9
        int     21h
exit:
        mov     ah,4ch
        int     21h

; this module prompts the operator for the floppy drive to use, only A and
; B are acceptable answers.  It then checks the type of media involved using
; the FAT Descriptor for 360k drives, and a combination of the FAT Descriptor
; and the cluster count for 720k drives.  The correct number of 9 sectored
; tracks is stored for later loop usage.

getdisk:
        mov     dx,offset askdsk
        mov     ah,9
        int     21h             ;ask the question
        mov     ah,1
        int     21h             ;get the answer
        and     al,05fh         ;make lower case
        cmp     al,3
        jz      exit            ;exit on control-c
        cmp     al,27
        jz      exit            ;or on escape char
        cmp     al,'A'          ;was it an 'A'???
        jnz     gd002
;process drive A request
        mov     drivasc,al      ;save for later use
        sub     al,41h          ;convert to binary
        mov     dskbin,al       ;save it too
        jmp     gdret           ;jump around the B check and error for
                                ;common return
;it wasn't an A so check for a B
gd002:
        cmp     al,'B'
        jnz     gd003           ;not B either - then must be wrong
        mov     drivasc,al      ;save the B for later use
        sub     al,41h          ;convert to binary
        mov     dskbin,al       ;save it too
        jmp     gdret

;here if not A or B - must be error...
gd003:                  ;here if in error
        mov     dx,offset as2err
        mov     ah,9
        int     21h             ;tell him A or B only
        jmp     getdisk         ;ask again...

;here with al containing binary drive value...0=A, 1=B
gdret:
        inc     al              ;convert to base 1 for absolute call
        mov     dl,al           ;put in DL for absolute call
        mov     ah,1ch          ;put the GET FAT function in AH
        push    ds              ;save from int 21 function
        int     21h
        jnc     gdret1          ;got a good fat description
        jmp     readerr         ;couldn't get a good FAT description

;here if got good fat function call - note:  DS has changed.....
gdret1:
        mov     al,Byte Ptr[bx] ;get the Media Descriptor Byte...
        pop     ds              ;restore Data Segment to ours...
        cmp     al,0fdh         ;check for 9 sector 5.25 inch disk
        jnz     chk720          ;no, check for the 720k...
        mov     ax,80           ;360k disk has 40 trks, 2 sides, for tot of 80
        mov     dsktyp,ax       ;save in storage for later loop
        mov     dx,offset typ360
        mov     ah,9
        int     21h             ;tell user it's a 360
        jmp     dskgood         ;go to common exit point...

;here if wasn't a 360k drive in the selected drive...
chk720:
        cmp     al,0f9h         ;check for 3.5 inch disk...(maybe 1.2 Meg)??
        jnz     readerr         ;if not 0fdh or 0f9h then must be wrong type

; both the 3.5" 720k and the 1.2m 5.25 have an f9 Fat descriptor -
; use the tot clusters to insure it's a 720k...
        cmp     dx,clustchk     ;check for 720k
        jnc     readerr         ;error if it's too many
        mov     ax,160          ;track count on 3.5 inch disk
        mov     dsktyp,ax       ;save the total tracks for later looping use

; now have good drive type entered, and know correct number of tracks to use
dskgood:
        mov     dx,offset newlin
        mov     ah,9
        int     21h             ;just start a new line for clarity...
        ret


; this module prompts for and inputs a file name to use via the buffered
; input routine.  It also terminates the input with a zero byte for the
; file handle routines to come later...

getfile:
        mov     dx,offset askfil
        mov     ah,9
        int     21h             ;show user what we want...
        mov     ah,0ah          ;buffered input for file name...
        mov     dx,offset bufinp
        int     21h             ;get an input from him..
        mov     al,chrcnt       ;get character count he entered
        cmp     al,0            ;nothing means exit...
        jnz     gfl01           ;he did something...
        jmp     exit
;operator entered a response other than enter key...
gfl01:
        mov     ah,0            ;clear hi byte of AX
        mov     di,offset filsto;set offset to 1st character entered
        add     di,ax           ;add the number of chars entered
        mov     byte ptr[di],0  ;put a zero terminator there
        mov     dx,offset newlin;clear the line for clarity...
        mov     ah,9
        int     21h
        ret

; this module opens the file using the handle function...
openfile:
        mov     ah,3dh          ;function to open file handle
        mov     al,0            ;read only mode - careful...
        mov     dx,offset filsto;point to ASCIIZ name...
        int     21h             ;go try to open...
        jnc     openok          ;if carry not set, then ok...

;here if error opening file... check sequentially for errors...
        mov     dx,offset nofile
        cmp     al,2            ;error 2 is no file found
        jz      openerr
        mov     dx,offset badpath
        cmp     al,3            ;error 3 is can't find path
        jz      openerr
        mov     dx,offset nohand
        cmp     al,4            ;error 4 is no handle available
        jz      openerr
        mov     dx,offset accden
        cmp     al,5            ;error 5 is Access Denied
        jz      openerr
        mov     dx,offset gopnerr
                                ;use general error if not above...
openerr:
        jmp     errexit         ;go print the error and quit...

;here if file opened ok...
openok:
        mov     handle,ax       ;save handle for later use.
        ret

;tell operator we had an error reading file and then exit..
readerr:
        mov     dx,offset rerr001
        jmp     errexit

; this module creates a new file when reading in a floppy disk
makefile:
        mov     ah,3ch          ;function in AH
        mov     cx,0            ;attributes set to none
        mov     dx,offset filsto;point to ASCIIZ string inputted
        int     21h             ;go try to make it
        jnc     makeok          ;carry not set - then OK - it's made
        mov     dx,offset badpath
        cmp     al,3            ;error 3 is can't find path
        jz      makerr
        mov     dx,offset nohand
        cmp     al,4            ;error 4 is no handle available
        jz      makerr
        mov     dx,offset accden
        cmp     al,5            ;error 5 is access denied
        jz      makerr
        mov     dx,offset gmaker
                                ;use general error if not above...
makerr:
        jmp     errexit         ;print the error for user and exit

;here if file was created OK
makeok:
        mov     handle,ax       ;save handle for later writes...
        ret

;this module closes the file and updates the directory
closfile:
        mov     ah,3eh          ;function to close in AH
        mov     bx,handle       ;get the saved handle
        int     21h             ;go close it
        jnc     closok          ;branch if no error ocurred
;here if error closing file
        mov     dx,offset closerr
        jmp     errexit         ;print the error and exit
;here if closed OK
closok:
        ret

; this module reads one tracks worth of data (9 sectors) into the buffer.
; the file pointer is left for DOS to handle, we only do a sequential read.

readfile:
        mov     ah,3fh          ;function in AH
        mov     bx,handle       ;handle in BX
        mov     dx,offset bugbuff;point to the buffer to use
        mov     cx,trklen       ;number of bytes to read in CX
        int     21h             ;go read the file
        jnc     readok          ;skip error if none ocurred
rfiler2:
        mov     dx,offset rfiler
        jmp     errexit         ;tell him an error and exit

;here if no error ocurred, however, make sure we didn't run out of bytes...
readok:
        cmp     ax,cx           ;see if we read all we wanted...
        jnz     rfiler2         ;error if short read...
        ret

; this routine writes one track's worth of data from the buffer to the file
; the file pointer is left for DOS to handle, we only do a sequential write.
writfile:
        mov     ah,40h          ;function in AH
        mov     bx,handle       ;handle in BX
        mov     dx,offset bugbuff;point to the buffer to use
        mov     cx,trklen       ;number of bytes to write in CX
        int     21h             ;go write it
        jnc     writok          ;skip error if none ocurred

;here if DOS reported an error
wfiler2:
        call    closfile                ;don't leave lost clusters
        mov     dx,offset wfiler
        jmp     errexit                 ;tell user we can't write and exit

;here if no error ocurred, however, make sure we wrote the whole buffer...
writok:
        cmp     ax,cx           ;check to see complete write
        jnz     wfiler2         ;error if not
        ret                     ;else all is ok

; this module reads one track's worth of data from the floppy disk
readdisk:
        mov     al,dskbin       ;get the disk to use
        mov     cx,9            ;nine sectors per track
        mov     dx,cursec       ;where to start
        mov     bx,offset bugbuff;where to put data
        int     25h             ;go do it
        pop     cx              ;clean up stack from int 25h (the flags)
        jnc     dredok          ;skip the error if ok
        jmp     readerr         ;else tell user and exit
;here if read the track ok
dredok:
        ret

; this module writes one track's worth of data to the floppy disk
writdisk:
        mov     al,dskbin       ;get the disk to use
        mov     cx,9            ;write the whole nine sectors
        mov     dx,cursec       ;where to start
        mov     bx,offset bugbuff;where to get data from
        int     26h             ;go do it
        pop     cx              ;clean up stack from int 25h (the flags)
        jnc     dwitok          ;skip the error if ok
        jmp     writerr         ;else tell user and exit
;here if wrote the whole track
dwitok:
        ret

;here if an error writing a track to floppy disk
writerr:
        mov     dx,offset werr001
        jmp     errexit

; check the length of the file to see if it came from the same type
; disk drive - 360k disks return DX=0005 and AX=A000 while
; 720k drives return DX=000B and AX=4000... error exit if no match..

lenchk:
        mov     ax,4202h        ;LSEEK ref EOF
        mov     bx,handle       ;file handle to use
        mov     cx,0            ;cx and dx set the offset
        mov     dx,cx           ;offset 0 from end
        int     21h             ;go move the DOS pointer
        push    dx              ;save the pointer (DX:AX) on stack for rewind
        push    ax
        mov     ax,4200h        ;LSEEK ref beginning
        mov     bx,handle       ;same file
        mov     cx,0            ;same offset
        mov     dx,cx           ;offset 0 from beginning
        int     21h             ;reset pointer to beginning
        pop     ax              ;get the length back from the stack
        pop     dx
; now have file length in DX:AX and pointer back at beginning of file...
        mov     cx,dsktyp       ;get # tracks in CX
        mov     si,5            ;set to 360k first
        mov     di,0a000h
        cmp     cx,80           ;check for 360k track count
        jz      chkit           ;ok, then skip the set to 720k value
        mov     si,0bh          ;set comparators to 720k standard
        mov     di,04000h
chkit:
; now compare DX with SI and AX with DI - if they dont both match, file
; is the wrong size...
        cmp     dx,si           ;check hi words first
        jnz     sizerr          ;error if not equal
        cmp     ax,di           ;check lo words too
        jnz     sizerr          ;error if not equal
        ret                     ;ok, file is right length

; here if wrong size file - tell user and abort program.
sizerr:
        mov     dx,offset serr001
        jmp     errexit

;thats all folks......
end
