Metropoli BBS
VIEWER: senddisk.asm MODE: TEXT (ASCII)
;********* 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

[ RETURN TO DIRECTORY ]