[alt.sources] SCSI Version 1.1

briana@tau-ceti.isc-br.com (Brian Who?) (09/09/90)

     Here is version 1.1 of my SCSI Device Driver for the Seagate ST-01
interface card.  It add support for a single tape device and cleans up
the code a little.

#
# This is a Shell Archive.
# Remove everything above and including the cut line.
# Then run the rest of the file through #! /bin/sh.
# -----cut here-----cut here-----cut here-----cut here-----
#! /bin/sh
# Execute the file with #! /bin/sh (not csh) to create the files:
#	binmode.c
#	dump.asm
#	equ.inc
#	ioctl.asm
#	kludge.asm
#	makefile
#	options.inc
#	readme.10
#	readme.11
#	scsi.asm
#	serase.c
#	sformat.c
#	struct.inc
#	subs.asm
#	units.asm
# This Archive created: Sat Sep  8 12:13:37 1990
# By: Brian Who? at I Saute Cats - Barbecue Rats, Spokane, WA
#
export PATH; PATH=/bin:$PATH
echo shar: extracting "'binmode.c'" '(497 characters)'
if test -f 'binmode.c'
then
echo shar: will not over-write existing file "'binmode.c'"
else
sed 's/^XX//' > 'binmode.c' << \SHAR_EOF
XX#ifdef MSDOS
XX#include <dos.h>
XX
XXbinmode(fd)
XXint fd;
XX{
XX	union REGS inregs, outregs;
XX
XX	/*
XX	** get the current mode
XX	*/
XX	inregs.h.ah = 0x44;			/* ioctl */
XX	inregs.h.al = 0x00;			/* get */
XX	inregs.x.bx = fd;			/* unit */
XX	intdos(&inregs, &outregs);
XX
XX	/*
XX	** set to BINARY mode (this works for char devices)
XX	*/
XX	inregs.h.ah = 0x44;			/* ioctl */
XX	inregs.h.al = 0x01;			/* set */
XX	inregs.x.bx = fd;			/* unit */
XX	inregs.h.dh = 0;
XX	inregs.h.dl = outregs.h.dl | 0x20;
XX	intdos(&inregs, &outregs);
XX}
XX#endif
SHAR_EOF
if test 497 -ne "`wc -c < 'binmode.c'`"
then
echo shar: error transmitting "'binmode.c'" '(should have been 497 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'dump.asm'" '(1296 characters)'
if test -f 'dump.asm'
then
echo shar: will not over-write existing file "'dump.asm'"
else
sed 's/^XX//' > 'dump.asm' << \SHAR_EOF
XX;
XX; Convert bin (ax) to ascii (bx => buffer)
XX;
XXbin_ascii	proc	near
XX		pusha
XX		push	ax
XX		mov	cx,6
XXfill_buff:	mov	byte ptr [bx],' '
XX		inc	bx
XX		loop	fill_buff
XX		mov	si,10
XX		or	ax,ax
XX		jns	clr_dvd
XX		neg	ax
XXclr_dvd:	sub	dx,dx
XX		div	si
XX		add	dx,'0'
XX		dec	bx
XX		mov	[bx],dl
XX		inc	cx
XX		or	ax,ax
XX		jnz	clr_dvd
XX		pop	ax
XX		or	ax,ax
XX		jns	no_more
XX		dec	bx
XX		mov	byte ptr [bx],'-'
XXno_more:	popa
XX		ret
XXbin_ascii	endp
XX
XX;
XX; Convert Hex (dx) to Ascii (bx => buffer)
XX;
XXhex2asc4	proc	near
XX		push	cx
XX		push	ax
XX		mov	cx,4		;Do Four Digits
XXh241:		rol	dx,4
XX		mov	al,dl		;Get the Current Digit
XX		and	al,0Fh
XX		cmp	al,0Ah		;Is It Hex?
XX		jge	h242
XX		add	al,30h		;Normal Digit
XX		jmp	h243
XXh242:		add	al,37h		;Hex Digit
XXh243:		mov	[bx],al		;Insert in Buffer
XX		inc	bx
XX		loop	h241
XX		pop	ax
XX		pop	cx
XX		ret
XXhex2asc4	endp
XX
XX;
XX; Convert Hex (dl) to Ascii (bx => buffer)
XX;
XXhex2asc2	proc	near
XX		push	cx
XX		push	ax
XX		mov	cx,2		;Do Two Digits
XXh221:		rol	dl,4
XX		mov	al,dl		;Get the Current Digit
XX		and	al,0Fh
XX		cmp	al,0Ah		;Is It Hex?
XX		jge	h222
XX		add	al,30h		;Normal Digit
XX		jmp	h223
XXh222:		add	al,37h		;Hex Digit
XXh223:		mov	[bx],al		;Insert in Buffer
XX		inc	bx
XX		loop	h221
XX		pop	ax
XX		pop	cx
XX		ret
XXhex2asc2	endp
XX
XX;
XX; Print a string
XX;
XX; ds:dx => string
XX;
XXputs		proc	near
XX		pusha
XX		mov	ah,9		;DOS print string
XX		int	21h
XX		popa
XX		ret
XXputs		endp
SHAR_EOF
if test 1296 -ne "`wc -c < 'dump.asm'`"
then
echo shar: error transmitting "'dump.asm'" '(should have been 1296 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'equ.inc'" '(3726 characters)'
if test -f 'equ.inc'
then
echo shar: will not over-write existing file "'equ.inc'"
else
sed 's/^XX//' > 'equ.inc' << \SHAR_EOF
XX;
XX; Equates
XX;
XXTRUE			EQU	001h
XXFALSE			EQU	000h
XX
XXDISK_REQUEST	EQU	000h		;Disk I/O Request
XXTAPE_REQUEST	EQU	001h		;Tape I/O Request
XX
XX;
XX; ST-01 Card Registers
XX;
XXSCSI_CARD_SEG	EQU	0DE00h		;Base Segment of Card
XXSCSI_CMD_PORT	EQU	01A00h		;Offset to Command Port
XXSCSI_DATA_PORT	EQU	01C00h		;Offset to Data Port
XX
XXSTACK_SIZE	EQU	512		;Our Local Stack
XX
XX;
XX; How the 16 bit sector number is broken down
XX; into a sector and cylinder number.
XX;
XX; x = Cylinder, y = track, always have only 1 head
XX;
XX; xxxx xxxx xxxx yyyy
XX;
XX; SECT_2_FS is computed as: ((unit_bpb.bpb_ts / CLUSTSIZE) * 2) / P_SECT)
XX;
XXSECT_TRACK	EQU	16		;Logical Sectors in a Track
XXSECT_MASK	EQU	0000Fh		;Mask for Sector
XXCYL_MASK	EQU	0FFF0h		;Mask for Cylinder
XXSECT_2_CYL	EQU	4		;How far to shift to convert
XX					;Sector to Cylinder Number
XXSECT_2_FS	EQU	10		;How far to shift for
XX					;Total Sectors to Fat Sectors
XX
XX;
XX; The max target to check for (0-X).
XX; If using 'reserve_addr', this should not execede 6!
XX;
XXMAXUNIT		EQU	6		;Maximum Unit Number
XX
XXP_SECT		EQU	512		;Physical Sector Size
XXCLUSTSIZE	EQU	4		;Number of Sectors to a Cluster
XX
XX;
XX; For multi_sector reads and writes,
XX; set the following to '1'
XX;
XX; CHUNK_MASK is the maximum number of sectors to access in one
XX; SCSI request.  It MUST be a power of two, and CHUNK_MASK * P_SECT
XX; MUST be <= 64K to prevent segment wrap problems.
XX;
XXCHUNK_MAX	EQU	32
XX
XXCMDENABLE	EQU	080h		;Enable the output drivers
XXCMDENINTR	EQU	040h		;Enable Interrupt
XXCMDPARITY	EQU	020h		;Enable Parity
XXCMDSTARB	EQU	010h		;Start Bus Arbitration
XXCMDATTN		EQU	008h		;Assert ATTN
XXCMDBSY		EQU	004h		;Assert BSY
XXCMDSEL		EQU	002h		;Assert SEL
XXCMDRST		EQU	001h		;Assert RST
XX
XX		if scsi_parity
XXCMDBASE		EQU	CMDPARITY	;Base value of all commands
XX		else
XXCMDBASE		EQU	000h		;Base value of all commands
XX		endif
XX
XXSTARBCOMPL	EQU	080h		;Arbitration Complete
XXSTPARERR	EQU	040h		;Parity Error
XXSTSEL		EQU	020h		;SEL Asserted
XXSTREQ		EQU	010h		;REQ Asserted
XXSTCD		EQU	008h		;C/D Asserted
XXSTIO		EQU	004h		;I/O Asserted
XXSTMSG		EQU	002h		;MSG Asserted
XXSTBSY		EQU	001h		;BSY Asserted
XX
XXFREE_MASK	EQU	03Fh
XX
XXREQ_MASK	EQU	STMSG or STCD or STIO
XXREQ_DATAOUT	EQU	000h		;Data Out Phase
XXREQ_DATAIN	EQU	STIO		;Data In Phase
XXREQ_CMDOUT	EQU	STCD		;Command Out Phase
XXREQ_STATIN	EQU	STCD or STIO	;Status In Phase
XXREQ_MSGOUT	EQU	STMSG or STCD	;Msg Out Phase
XXREQ_MSGIN	EQU	STMSG or STCD or STIO	;Msg In Phase
XX
XXCOK		EQU	0		;Command Completed OK
XXCNOCONNECT	EQU	1		;Unable to Connect to Target
XXCBUSBUSY	EQU	2		;Bus Busy
XXCTIMEOUT	EQU	3		;Timeout waiting for Response
XXCERROR		EQU	4		;Target Return Error
XXCBUSY		EQU	5		;Target was Busy
XXCDISCONNECT	EQU	6		;Target Disconnected
XX
XXSCSI_TESTREADY	EQU	000h		;Test Unit Ready (6 byte)
XXSCSI_REQSENSE	EQU	003h		;Request Sense (6 byte)
XXSCSI_FORMATUNIT	EQU	004h		;Format Disk (6 byte)
XXSCSI_WRITEFM	EQU	010h		;Write File Marks (6 byte)
XXSCSI_INQUIRY	EQU	012h		;Inquire (6 byte)
XXSCSI_MODE_SET	EQU	015h		;Mode Select (6 byte)
XXSCSI_ERASE	EQU	019h		;Erase Tape (6 byte)
XXSCSI_MODE_GET	EQU	01Ah		;Mode Sense (6 byte)
XXSCSI_LOAD	EQU	01Bh		;Load / Unload Tape (6 byte)
XXSCSI_READSIZE	EQU	025h		;Read Drive Capacity (10 byte)
XX		if extended_io
XXSCSI_READBLK	EQU	028h		;Read Sectors (10 byte)
XXSCSI_WRITEBLK	EQU	02Ah		;Write Sectors (10 byte)
XX		else
XXSCSI_READBLK	EQU	008h		;Read Sectors (6 byte)
XXSCSI_WRITEBLK	EQU	00Ah		;Write Sectors (6 byte)
XX		endif
XXSCSI_VERIFYBLK	EQU	02Fh		;Verify Blocks (10 byte)
XX
XXMSG_COMPLETE	EQU	000h		;Command is Complete
XXMSG_SAVE	EQU	002h		;Save Data Pointers
XXMSG_RESTORE	EQU	003h		;Restore Data Pointers
XXMSG_ERROR	EQU	005h		;Error Detected
XXMSG_ABORT	EQU	006h		;Abort the Command
XXMSG_REJECT	EQU	007h		;Reject the Command
XXMSG_NOP		EQU	008h		;No Operation
XXMSG_IDENTIFY	EQU	080h		;Identify Yourself
XX
XXLOAD_TAPE	EQU	001h		;Load
XXUNLOAD_TAPE	EQU	000h		;Unload
SHAR_EOF
echo shar: a missing newline was added to "'equ.inc'"
if test 3726 -ne "`wc -c < 'equ.inc'`"
then
echo shar: error transmitting "'equ.inc'" '(should have been 3726 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'ioctl.asm'" '(2148 characters)'
if test -f 'ioctl.asm'
then
echo shar: will not over-write existing file "'ioctl.asm'"
else
sed 's/^XX//' > 'ioctl.asm' << \SHAR_EOF
XX;
XX; Process an ioctl request for the current unit
XX;
XX; return 'C' on error
XX;
XXscsi_ioctl	proc
XX		mov	al,es:[bx].rh19_minor		;Get the minor number
XX
XX		cmp	al,40h				;Set Device Params?
XX		jnz	scsi_i_42h
XX		clc
XX		jmp	short scsi_i_exit
XX
XXscsi_i_42h:	cmp	al,42h				;Format and Verify?
XX		jnz	scsi_i_60h
XX		call	scsi_verify
XX		jmp	short scsi_i_exit
XX
XXscsi_i_60h:	cmp	al,60h				;Get Device Params?
XX		jnz	scsi_i_62h
XX		mov	si,cur_bpb			;Get the Current BPB
XX		mov	di,es:[bx].rh19_buf_ofs		;Get the Param Buffer
XX		mov	ax,es:[bx].rh19_buf_seg
XX		mov	es,ax
XX		mov	es:[di].dpb_special,05h		;Sect Same/Use Cur BPB
XX		mov	es:[di].dpb_type,05h		;Fixed Disk
XX		mov	es:[di].dpb_attr,0001h		;Not Removable
XX		mov	ax,[si].bpb_ts
XX		shr	ax,SECT_2_CYL			;Convert Sect to Cyl
XX		mov	es:[di].dpb_cyl,ax
XX		mov	es:[di].dpb_media,0		;????
XX		mov	es:[di].dpb_sectors,SECT_TRACK	;Sectors per Track
XX
XX		push	di
XX		lea	di,es:[di].dpb_bpb		;Copy the bpb into
XX		mov	cx,size bpb			;the requestors buffer
XX		cld
XX	rep	movsb
XX		pop	di
XX
XX		lea	di,es:[di].dpb_track		;Build the Track List
XX		mov	cx,SECT_TRACK
XX		mov	ax,0				;Start with Sector 0
XXscsi_i_t_loop:	mov	es:[di],ax			;Sector Number
XX		inc	ax
XX		inc	di
XX		inc	di
XX		mov	word ptr es:[di],P_SECT		;Sector Size
XX		inc	di
XX		inc	di
XX		loop	scsi_i_t_loop
XX		clc
XX		jmp	short scsi_i_exit
XX
XXscsi_i_62h:	cmp	al,62h				;Verify?
XX		jnz	scsi_i_error
XX		call	scsi_verify
XX		jmp	short scsi_i_exit
XX
XXscsi_i_error:	stc
XXscsi_i_exit:	ret
XXscsi_ioctl	endp
XX
XX;
XX; Process an ioctl_write request
XX;
XXscsi_ioctl_write proc
XX		mov	di,es:[bx].rh12_buf_ofs		;Get The Command
XX		mov	ax,es:[bx].rh12_buf_seg		;Buffer
XX		mov	es,ax
XX		mov	ax,es:[di].ioc_command		;What Command
XX
XX;
XX; Format Disk Unit
XX;
XX		cmp	al,'F'				;Format?
XX		jnz	try_erase
XX		mov	ax,es:[di].ioc_param		;Get Interleave
XX		lea	di,cmd_format			;Insert into Command
XX		mov	[di].fmt_cmd_il_b0,al
XX		mov	[di].fmt_cmd_il_b1,ah
XX		call	docmd
XX		jnc	scsi_i_w_exit
XX		call	scsi_sense
XX		jmp	scsi_i_w_exit
XX
XX;
XX; Erase Tape Unit
XX;
XXtry_erase:	cmp	al,'E'				;Erase?
XX		jnz	scsi_i_w_error
XX		lea	di,cmd_erase			;Now Erase Tape
XX		call	docmd
XX		jnc	scsi_i_w_exit
XX		call	scsi_sense
XX		jmp	scsi_i_w_error
XX
XXscsi_i_w_error:	stc
XXscsi_i_w_exit:	ret
XXscsi_ioctl_write endp
SHAR_EOF
if test 2148 -ne "`wc -c < 'ioctl.asm'`"
then
echo shar: error transmitting "'ioctl.asm'" '(should have been 2148 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'kludge.asm'" '(2417 characters)'
if test -f 'kludge.asm'
then
echo shar: will not over-write existing file "'kludge.asm'"
else
sed 's/^XX//' > 'kludge.asm' << \SHAR_EOF
XX;
XX; This code is needed because DOS insists on opening a char device
XX; in cooked mode.  The problem is that without adding code to every
XX; application that would ever use us, we have no way to alter this
XX; because the use of O_BINARY or setmode() do not affect char devices.
XX;
XX; The solution (kludge) is to watch open requests issued thru the
XX; INT 21 vector.  If we see a open request followed by a OPEN_DEV
XX; call to us, it must have been an open for us.  So during the return,
XX; force a call to the ioctl facility that will switch to raw mode.
XX;
XX
XX;
XX; The Original INT 21 Vector
XX;
XXvect_int_21	equ	word ptr 4 * 21h
XXorig_int_21	dd	?			;Original INT 21 Vector
XX
XX;
XX; OPEN_DEV flag is TRUE when we are opened
XX;
XXopened_flag	db	FALSE
XX
XXpatch_us_in	proc	near
XX		push	es
XX		push	ax
XX		mov	ax,0			;Patch Ourselves into
XX		mov	es,ax			;the INT 21 Vector
XX		mov	ax,es:[vect_int_21]	;Offset
XX		mov	word ptr orig_int_21,ax
XX		lea	ax,our_int_21
XX		mov	es:[vect_int_21],ax
XX		mov	ax,es:[vect_int_21+2]	;Segment
XX		mov	word ptr orig_int_21+2,ax
XX		mov	ax,cs
XX		mov	es:[vect_int_21+2],ax
XX		pop	ax
XX		pop	es
XX		ret
XXpatch_us_in	endp
XX
XXour_int_21	proc	far
XX		pushf				;Save entry flags
XX		cmp	ah,3Dh			;Is it an open request?
XX		jnz	not_open_req
XX		popf				;Restore entry flags
XX;
XX; We need to set things up so the 'iret' done by the INT 21
XX; code will have some the right stuff on the stack.
XX; #1 Flags with interrupts enabled
XX; #2 Return Address
XX;
XX		sti				;Allow interrupts
XX		pushf				;After the iret
XX		cli				;Shut interrupts off
XX		call	cs:orig_int_21		;While we Pass the request on
XX;
XX; Upon return, interrupts are enabled, so shut them off while we work
XX;
XX		pushf
XX		cli
XX		cmp	cs:opened_flag,FALSE	;Was it an open for us?
XX		jz	not_our_open
XX		mov	cs:opened_flag,FALSE	;Clear for next time
XX;
XX; We need to forge a call to the ioctl interface
XX; to switch DOS to raw mode when it talks to us
XX;
XX		pusha
XX		mov	bx,ax			;Save the Handle
XX		mov	ax,4400h		;Get Device Information
XX		pushf
XX		call	cs:orig_int_21
XX		mov	dh,0			;Setup
XX		or	dl,20h			;for RAW Mode
XX		mov	ax,4401h		;Set Device Information
XX		pushf
XX		call	cs:orig_int_21
XX		popa
XX
XXnot_our_open:	popf				;The Original Flags to return
XX;
XX; When we return, we need to pop the flags that the original INT 21
XX; call left on the stack, and return the flags we got back
XX;
XX		ret	2			;Return and discard flags
XX
XXnot_open_req:	popf				;Pop the saved flags
XX		jmp	cs:orig_int_21		;Continue with original code
XXour_int_21	endp
SHAR_EOF
if test 2417 -ne "`wc -c < 'kludge.asm'`"
then
echo shar: error transmitting "'kludge.asm'" '(should have been 2417 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'makefile'" '(526 characters)'
if test -f 'makefile'
then
echo shar: will not over-write existing file "'makefile'"
else
sed 's/^XX//' > 'makefile' << \SHAR_EOF
XX#
XX# SCSI Stuff
XX#
XXSRCS = scsi.asm subs.asm ioctl.asm dump.asm units.asm kludge.asm \
XX       options.inc equ.inc struct.inc
XX
XXall: scsi.sys sformat.exe serase.exe
XX
XX#
XX# SCSI Disk Device Driver
XX#
XXscsi.sys: scsi.obj
XX	link +scsi.obj, scsi ;
XX	exe2bin scsi.exe scsi.sys
XX
XXscsi.obj: $(SRCS)
XX	masm scsi.asm scsi.obj scsi.lst ;
XX
XX#
XX# SCSI Disk Formatter
XX#
XXsformat.exe: sformat.c
XX	cl -G2 -Ox -o sformat.exe sformat.c
XX
XX#
XX# SCSI Tape Erase
XX#
XXserase.exe: serase.c
XX	cl -G2 -Ox -o serase.exe serase.c
XX
XX#
XX# clean
XX#
XXclean:
XX	rm -f *.exe *.obj *.lst
SHAR_EOF
if test 526 -ne "`wc -c < 'makefile'`"
then
echo shar: error transmitting "'makefile'" '(should have been 526 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'options.inc'" '(1165 characters)'
if test -f 'options.inc'
then
echo shar: will not over-write existing file "'options.inc'"
else
sed 's/^XX//' > 'options.inc' << \SHAR_EOF
XX;
XX; Allow multi_sector reads and writes.
XX;
XX; This means that a read request of less then CHUNK_MAX
XX; sectors will be done in one request to the drive rather
XX; then multiple single sector requests.
XX;
XX; The disadvantage here is that the code in receive_data()
XX; and send_data() must keep the transfer from exceding the
XX; 512 byte buffer on the ST-01 card.
XX;
XXmulti_sector	=	1	;Enable multi_sector support
XX
XX;
XX; Use the extended SCSI commands for reads and writes.
XX;
XX; This means that we can access drives larger then 1Gb
XX; and read/write more then 256 sectors per command.
XX;
XX; The disadvantage here is that not all devices support
XX; the extended command set.
XX;
XXextended_io	=	0	;Disable use of extended io commands
XX
XX;
XX; Use parity on the SCSI bus
XX;
XXscsi_parity	=	1	;Enable use of parity on the SCSI bus
XX
XX;
XX; Reserve SCSI Address 7 for card
XX;
XXreserve_addr	=	1	;Reserve Address for Card Use
XX
XX;
XX; Dump Sense information to the screen
XX;
XXdump_sense	=	1	;Dump Sense Message to the Screen
XX
XX;
XX; Extended Sense or Normal Sense
XX;
XXextended_sense	=	1	;Request Extended Sense
XX
XX;
XX; Include the code to kludge the RAW ioctl call after an open
XX;
XXuse_kludge	=	1	;Enable the ioctl kludge
SHAR_EOF
if test 1165 -ne "`wc -c < 'options.inc'`"
then
echo shar: error transmitting "'options.inc'" '(should have been 1165 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'readme.10'" '(2706 characters)'
if test -f 'readme.10'
then
echo shar: will not over-write existing file "'readme.10'"
else
sed 's/^XX//' > 'readme.10' << \SHAR_EOF
XXScsi Version 1.0
XX
XX     The code you received with this file is a very simple SCSI device
XXdriver that uses the Seagate ST-01 interface card.  As this driver is
XXmy first PC assembler project other then "hello world", please don't
XXsnicker to loudly.
XX
XX     The package includes the source for a device driver that will scan
XXthe SCSI bus for disk drives and partition any drives found into chunks
XXof 32Meg plus any leftovers.  As soon as I discover a way to get DOS to
XXlet me use > 512 byte sectors, It will just allocate the entire disk to
XXa single logical drive.  It also includes a utility to access the low
XXlevel SCSI format function for any disk found.  You may specify the
XXinterleave when the low level format is done and the version of the
XXdriver here seems to work fine at 1:1 with my 8Mhz NEC.
XX
XX     Some of the things to look out for are:
XX
XX#1 The receive_data and send_data functions in subs.asm use polled I/O
XX   to transfer data to/from the card.  I would have loved to just use
XX   the I/O Channel Ready line that the card is suppose to support, but
XX   my NEC does not seem to use that line.  Hence the polling of the REQ
XX   bit in the status port.
XX
XX#2 I did not know how to do clock speed independent timing loops, so there
XX   is a wait100us function in subs.asm that is very processor speed
XX   dependent :-(
XX
XX#3 In ioctl.asm there is a commented out call to scsi_verify.  This is
XX   used by the DOS format utility to scan for errors.  You may want to
XX   enable the call after you get everything setup.  I shut it off while
XX   I was testing as I didn't want to wait for the verify everytime I
XX   changed the interleave.
XX
XX     To bring up the driver.  Assemble and link scsi.sys and add it to
XXyour config.sys file.  After rebooting, you may optionaly do a low level
XXformat of each disk found using sformat.exe.  You then use the DOS format
XXutility on each logical drive.  I did figure out just what format needed
XXin the way of ioctl support to get it to do the high level format for me.
XXAt this point you should have fully functional DOS drives.  In testing
XXI found that with multi_sector support (subs.asm) enabled, the SCSI drives
XXwere just about as fast as the ST-412 C: that came with the machine.  I
XXam sure that the speed problem is basic to the inner loop of the data
XXtransfer routines, but I'm at a loss to figure out how to speed it up.
XX
XX     Anyway, maybe someone can find some use for the code.  I got the
XXcard for free, so I can't really complain about the speed or cost too
XXmuch :-)
XX
XX     Also thanks to the people that sent me samples of other device drivers
XXfor the PC.  I lost the names to a disk crash, but here is the result of
XXyour help and my thanks.
XX
XXBrian Antoine
XXAug 29, 1990
SHAR_EOF
if test 2706 -ne "`wc -c < 'readme.10'`"
then
echo shar: error transmitting "'readme.10'" '(should have been 2706 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'readme.11'" '(2983 characters)'
if test -f 'readme.11'
then
echo shar: will not over-write existing file "'readme.11'"
else
sed 's/^XX//' > 'readme.11' << \SHAR_EOF
XXScsi Version 1.1
XX
XX     This version of the driver add support for a single tape drive, and
XXcleans up some of the code a little.  It also adds a file that has equates
XXto customize some of the major areas of the driver.
XX
XX     When the driver is initialized it does a scan of the SCSI bus for
XXdevices.  The FIRST tape device found is assigned to a char device with the
XXname "SCSITAPE".  If no tape drive is found, the char device is still valid
XXbut will generate a "bad unit" error when accessed.
XX
XX     The SCSITAPE device expects reads and writes to be done in some variation
XXof 512 bytes.  I/O done where (size mod 512) is non-zero will generate an
XXerror.  Tape access must be done in RAW mode, and this is where I had to
XXsome code in the driver that I'm not sure is very pretty.  The problem is
XXthat DOS insists on opening char devices in cooked mode.  It does give you
XXthe ability to switch to RAW mode, but only by using 'intdos()' to access
XXthe ioctl interface.  The MSC 'setmode()' call for binary will not effect
XXchar devices, nor will opening the file with O_BINARY.  So I had two choices.
XXI could modify every program that expected to talk to the tape so that it
XXalso issued the ioctl stuff (see binmode.c), or I could kludge the driver
XXto issue the required calls.
XX
XX     In the end, I punted and did both.  The code in binmode.c is the function
XXthat along with 'setmode()', will make sure that ANYTHING you open is accessed
XXin RAW mode.  Simply check for any call to 'setmode()' that is switching to
XXO_BINARY, and add another call to 'binmode()'.  This assumes that you have
XXthe source to the utility in question.  For those who are don't mind a little
XXkludge amount friends.  The file options.inc has an equate called 'use_kludge'
XXthat when enabled turns on some code in kludge.asm.  This code links into the
XXINT 21h vector and waits for a DOS open request to go by.  When it sees an
XXopen request, it 'calls' rather then 'jmps' to the normal vector.  This allows
XXthe driver to get the 'handle' returned by DOS.  Because the SCSITAPE device
XXenables the driver_open function, I can tell from the fact that an open is
XXin progress and wether the driver just got an open request, wether any
XXparticular open was for me.  When the open was in fact for the SCSITAPE device,
XXI take the 'handle' returned by DOS and forge the required ioctl calls to
XXswitch to RAW mode.
XX
XX     With the addition of tape support.  I now use GNU Tar to swap tapes with
XXmy normal UN*X system at work.  The driver works well enough that I have yet
XXto have a format problem with the different systems.  It is also a lot faster
XXwhen doing backups on the PC.  Using 'fastback' I would get about 1 Meg a
XXminute thruput on my 8 Mhz AT.  Using GNU Tar and the SCSITAPE device I get
XX> 2.5 Meg a minute to the tape and don't have to swap disks quite so often :-)
XX
XX     Anyway, I hope that someone out there actually gets some use out of this
XXcode.  It has been a real adventure for me.
XX
XXBrian Antoine
XXSept 8, 1990
SHAR_EOF
if test 2983 -ne "`wc -c < 'readme.11'`"
then
echo shar: error transmitting "'readme.11'" '(should have been 2983 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'scsi.asm'" '(11664 characters)'
if test -f 'scsi.asm'
then
echo shar: will not over-write existing file "'scsi.asm'"
else
sed 's/^XX//' > 'scsi.asm' << \SHAR_EOF
XX;
XX; Simple SCSI Device Driver
XX;
XX		.286
XX		PAGE	76,132
XX
XX		INCLUDE	options.inc
XX		INCLUDE	equ.inc
XX		INCLUDE	struct.inc
XX;
XX; Start of Code and Data
XX;
XX_TEXT		segment	word public 'CODE'
XX		assume	cs:_TEXT, ds:_TEXT, es:_TEXT
XX
XX		org	0
XX
XX;
XX; Device Header Required By DOS
XX;
XXscsi:
XXtape_link_ofs	dw	disk_link_ofs	;Forward Link
XXtape_link_seg	dw	-1
XX		dw	0C800h		;Char Device
XX		dw	tape_strategy	;Address of 1st DOS Call
XX		dw	dev_interrupt	;Address of 2nd DOS Call
XX		db	'SCSITAPE'	;Device Name
XX
XXdisk_link_ofs	dw	-1		;Forward Link
XXdisk_link_seg	dw	-1
XX		dw	06040h		;Ioctl R/W, Block Device, Non-IBM, Get/Set
XX		dw	disk_strategy	;Address of 1st DOS Call
XX		dw	dev_interrupt	;Address of 2nd DOS Call
XXdisk_count	db	0		;Number of Disks Present
XX		db	7 dup(?)
XX
XX;
XX; Work Space For Our Device Driver
XX;
XX		even
XXrh_off		dw	?		;Request Header Offset
XXrh_seg		dw	?		;Request Header Segment
XXrh_type		db	?		;Request Type
XX
XXwrite_flag	db	FALSE		;TRUE When Tape Write Seen
XXcur_drive	db	-1
XXvol_id		db	'NO NAME    ',0
XX
XX;
XX; Define our own personal Stack
XX;
XX		even
XXnew_stack	db	STACK_SIZE-2 dup (?)	;Our Local Stack
XXnew_stack_top	dw	?
XX
XXstack_ptr	dw	?			;Old Stack Pointer
XXstack_seg	dw	?			;Old Stack Segment
XX
XX;
XX; Command Table
XX;
XXcmdtab		label	byte		;* = Char Only Devices
XX		dw	INITIALIZATION	;Initialization
XX		dw	MEDIA_CHECK	;Media Check (Block Only)
XX		dw	GET_BPB		;Build BPB (Block Only)
XX		dw	unknown		;IOCTL Read
XX		dw	READ		;Read Data
XX		dw	done		;*Non Destructive Read
XX		dw	done		;*Read Status
XX		dw	done		;*Flush Read Buffer
XX		dw	WRITE		;Write Data
XX		dw	WRITE_VERIFY	;Write With Verify
XX		dw	done		;*Write Status
XX		dw	done		;*Flush Write Buffer
XX		dw	WRITE_IOCTL	;IOCTL Write
XX		dw	OPEN_DEV	;Device Open
XX		dw	CLOSE_DEV	;Device Close
XX		dw	done		;Removable Check
XX		dw	unknown		;*Write Until Busy
XX		dw	unknown		;Unknown Call
XX		dw	unknown		;Unknown Call
XX		dw	IOCTL		;Generic Ioctl
XX		dw	unknown		;Unknown Call
XX		dw	unknown		;Unknown Call
XX		dw	unknown		;Unknown Call
XX		dw	GET_DEV		;Get Device
XX		dw	SET_DEV		;Set Device
XX
XXreq_msg		db	0dh,'Request: '
XXreq_msg_type	db	'xx, Count: '
XXreq_msg_count	db	'xxxx, Start: '
XXreq_msg_start	db	'xxxx',0dh,0ah,'$'
XX
XX;
XX; Strategy Procedure
XX;
XXdisk_strategy	proc	far
XX		mov	cs:rh_seg,es		;Save Request Header Ptr Segment
XX		mov	cs:rh_off,bx		;Save Request Header Ptr Offset
XX		mov	cs:rh_type,DISK_REQUEST
XX		ret
XXdisk_strategy	endp
XX
XXtape_strategy	proc	far
XX		mov	cs:rh_seg,es		;Save Request Header Ptr Segment
XX		mov	cs:rh_off,bx		;Save Request Header Ptr Offset
XX		mov	cs:rh_type,TAPE_REQUEST
XX		ret
XXtape_strategy	endp
XX
XX;
XX; Interrupt Procedure
XX;
XXdev_interrupt	proc	far
XX		cli				;Save Machine State On Entry
XX		push	ds
XX		push	es
XX		push	ax
XX		push	bx
XX		push	cx
XX		push	dx
XX		push	si
XX		push	di
XX		push	bp
XX		pushf
XX
XX		mov	cs:stack_seg,ss		;Save Old Stack
XX		mov	cs:stack_ptr,sp
XX
XX		mov	ax,cs			;Save us the Segment Override Crap
XX		mov	ds,ax
XX		mov	es,ax
XX
XX		mov	ss,ax			;Setup Our Local Stack
XX		lea	ax,new_stack_top
XX		mov	sp,ax
XX		sti				;We're Safe Now
XX
XX;
XX; Perform branch based on the command passed in the Request Header
XX;
XX		mov	es,rh_seg		;Point us at the Request Header
XX		mov	bx,rh_off
XX
XX;		pusha
XX;		mov	di,bx
XX;		mov	dl,es:[di].rh_cmd	;Get Command Code
XX;		lea	bx,req_msg_type
XX;		call	hex2asc2
XX;		mov	dx,es:[di].rh4_count
XX;		lea	bx,req_msg_count
XX;		call	hex2asc4
XX;		mov	dx,es:[di].rh4_start
XX;		lea	bx,req_msg_start
XX;		call	hex2asc4
XX;		lea	dx,req_msg
XX;		call	puts
XX;		popa
XX
XX		mov	al,es:[bx].rh_cmd	;Get Command Code
XX		rol	al,1			;Get offset into table
XX		lea	di,cmdtab		;Get address of command table
XX		mov	ah,0			;Clear hi order byte
XX		add	di,ax			;Add offset
XX		jmp	word ptr [di]		;Jump Indirect
XX
XX;
XX; Command Procedures
XX;
XXINITIALIZATION:	cmp	rh_type,TAPE_REQUEST	;Is this SCSITAPE: Init?
XX		jz	init_skip
XX		mov	al,es:[bx].rh0_drv_ltr	;Save the starting Drive
XX		add	al,041h
XX		mov	cur_drive,al
XX		call	initial			;Setup
XX		if use_kludge
XX		call	patch_us_in
XX		endif
XX		mov	bx,rh_off
XX		mov	es,rh_seg
XXinit_skip:	lea	ax,initial		;Set The Break Address
XX		mov	es:[bx].rh0_brk_ofs,ax
XX		mov	es:[bx].rh0_brk_seg,cs
XX		mov	al,disk_count		;Number of Disk Devices Supported
XX		mov	es:[bx].rh0_nunits,al
XX		lea	dx,bpb_array		;BPB Array
XX		mov	es:[bx].rh0_bpb_tbo,dx
XX		mov	es:[bx].rh0_bpb_tbs,cs
XX		jmp	done
XX
XX;
XX; Has the Media Changed
XX;
XXMEDIA_CHECK:	call	find_unit
XX		jc	mc_jmp_err
XX		mov	di,cur_unit
XX		mov	al,[di].unit_mcheck	;Get Initial Status
XX		mov	[di].unit_mcheck,1	;Always OK from then on
XX		mov	es:[bx].rh1_md_stat,al
XX		lea	dx,vol_id		;Address of Volume ID
XX		mov	es:[bx].rh1_volid_ofs,dx
XX		mov	es:[bx].rh1_volid_seg,cs
XX		jmp	done
XXmc_jmp_err:	jmp	bad_unit
XX
XX;
XX; Get Disk Parameter Block
XX;
XXGET_BPB:	call	find_unit
XX		jc	get_jmp_err
XX		mov	dx,cur_bpb		;Address of BPB
XX		mov	es:[bx].rh2_pbpbo,dx
XX		mov	es:[bx].rh2_pbpbs,cs
XX		jmp	done
XXget_jmp_err:	jmp	bad_unit
XX
XX;
XX; Read some data from the disk/tape
XX;
XXREAD:		cmp	rh_type,DISK_REQUEST
XX		jz	read_a_disk
XX		mov	ax,tape_unit		;Do We Have a Tape?
XX		cmp	ax,-1
XX		jz	read_jmp_err1
XX		mov	cur_unit,ax
XX		call	tape_read
XX		jc	read_jmp_err2
XX		jmp	done
XXread_a_disk:	call	find_unit
XX		jc	read_jmp_err1
XX		call	disk_read
XX		jc	read_jmp_err2
XX		jmp	done
XXread_jmp_err1:	jmp	bad_unit
XXread_jmp_err2:	jmp	bad_read
XX
XX;
XX; Write some data to the disk/tape
XX;
XXWRITE		equ	$
XXWRITE_VERIFY:	cmp	rh_type,DISK_REQUEST
XX		jz	write_a_disk
XX		mov	ax,tape_unit		;Do We Have a Tape?
XX		cmp	ax,-1
XX		jz	write_jmp_err1
XX		mov	cur_unit,ax
XX		call	tape_write
XX		jc	write_jmp_err2
XX		jmp	done
XXwrite_a_disk:	call	find_unit
XX		jc	write_jmp_err1
XX		call	disk_write
XX		jc	write_jmp_err2
XX		jmp	done
XXwrite_jmp_err1:	jmp	bad_unit
XXwrite_jmp_err2:	jmp	bad_write
XXwrite_jmp_err3:	jmp	unknown
XX
XX;
XX; Write Ioctl Packet
XX;
XXWRITE_IOCTL:	cmp	rh_type,DISK_REQUEST
XX		jz	ioctl_a_disk
XX		mov	ax,tape_unit			;Do we have a SCSITAPE?
XX		cmp	ax,-1
XX		jz	write_jmp_err1
XX		mov	cur_unit,ax
XX		jmp	short ioctl_do
XXioctl_a_disk:	call	find_unit
XX		jc	write_jmp_err1
XXioctl_do:	call	scsi_ioctl_write
XX		jc	write_jmp_err3
XX		jmp	done
XX
XX;
XX; Special Control Functions
XX;
XXIOCTL:		call	find_unit
XX		jc	ioctl_jmp_err1
XX		call	scsi_ioctl
XX		jc	ioctl_jmp_err2
XX		jmp	done
XXioctl_jmp_err1:	jmp	bad_unit
XXioctl_jmp_err2:	jmp	unknown
XX
XX;
XX; Open Tape Device
XX;
XXOPEN_DEV:	mov	di,tape_unit
XX		cmp	di,-1		;Do We have a SCSITAPE: Unit?
XX		jz	open_err1
XX		mov	cur_unit,di			;New Current Unit
XX		lea	bx,[di].unit_sense_buf		;Buffer Offset
XX		push	ds				;Buffer Segment
XX		pop	es
XX		mov	cx,size sense			;Buffer Size
XX		lea	di,cmd_sense			;Command
XX		call	docmd
XX		jc	open_err2
XX		lea	di,cmd_load			;Now Load Tape
XX		mov	[di].load_cmd_type,LOAD_TAPE
XX		call	docmd
XX		jnc	open_ok
XX		call	scsi_sense
XXopen_err2:	jmp	general
XXopen_err1:	jmp	bad_unit
XXopen_ok:	mov	opened_flag,TRUE		;We are open
XX		mov	write_flag,FALSE		;No Writes Seen
XX		jmp	done
XX
XX;
XX; Close Tape Device
XX;
XXCLOSE_DEV:	mov	di,tape_unit
XX		cmp	di,-1		;Do We have a SCSITAPE: Unit?
XX		jz	close_err1
XX		mov	cur_unit,di			;New Current Unit
XX		cmp	write_flag,TRUE			;Were We Writing?
XX		jnz	tape_no_write
XX		lea	di,cmd_twritefm			;End Tape with FM
XX		call	docmd
XX		jnc	tape_no_write
XX		call	scsi_sense
XXtape_no_write:	lea	di,cmd_load			;Now Unload Tape
XX		mov	[di].load_cmd_type,UNLOAD_TAPE
XX		call	docmd
XX		jnc	close_jmp
XX		call	scsi_sense
XXclose_err2:	jmp	general
XXclose_err1:	jmp	bad_unit
XXclose_jmp:	jmp	done
XX
XX;
XX; Get Device Assignment
XX;
XXGET_DEV:	mov	es:[bx].rh_unit,0
XX		jmp	done
XX
XX;
XX; Set Device Assignment
XX;
XXSET_DEV:	jmp	done
XX
XXbad_unit:	mov	es,rh_seg		;Point us at the Request Header
XX		mov	bx,rh_off
XX		or	es:[bx].rh_status,8001h
XX		jmp	short done
XX
XXunknown:	mov	es,rh_seg		;Point us at the Request Header
XX		mov	bx,rh_off
XX		or	es:[bx].rh_status,8003h
XX		jmp	short done
XX
XXbad_write:	mov	es,rh_seg		;Point us at the Request Header
XX		mov	bx,rh_off
XX		or	es:[bx].rh_status,800Ah
XX		jmp	short done
XX
XXbad_read:	mov	es,rh_seg		;Point us at the Request Header
XX		mov	bx,rh_off
XX		or	es:[bx].rh_status,800Bh
XX		jmp	short done
XX
XXgeneral:	mov	es,rh_seg		;Point us at the Request Header
XX		mov	bx,rh_off
XX		or	es:[bx].rh_status,800Ch
XX		jmp	short done
XX
XXbusy:		mov	es,rh_seg		;Point us at the Request Header
XX		mov	bx,rh_off
XX		or	es:[bx].rh_status,0200h
XX
XXdone:		mov	es,rh_seg		;Point us at the Request Header
XX		mov	bx,rh_off
XX		or	es:[bx].rh_status,0100h
XX
XX		cli				;Make sure we're left alone
XX		mov	ax,cs:stack_seg		;Restore DOS Stack
XX		mov	ss,ax
XX		mov	ax,cs:stack_ptr
XX		mov	sp,ax
XX
XX		popf				;Restore All Registers
XX		pop	bp
XX		pop	di
XX		pop	si
XX		pop	dx
XX		pop	cx
XX		pop	bx
XX		pop	ax
XX		pop	es
XX		pop	ds
XX		sti				;We're Safe Now
XX		ret
XX
XX		INCLUDE	units.asm
XX		INCLUDE	subs.asm
XX		INCLUDE	ioctl.asm
XX		INCLUDE	dump.asm
XX		if use_kludge
XX		INCLUDE	kludge.asm
XX		endif
XX
XX;
XX; End of Program
XX; Stuff Placed Here Gets Handed Back To DOS For Re-use
XX;
XXinitial		proc	near
XX		lea	dx,hello_msg		;Tell them the driver version
XX		call	puts
XX		push	cs
XX		pop	dx
XX		lea	bx,seg_msg_value
XX		call	hex2asc4
XX		lea	dx,seg_msg		;And Were We Loaded
XX		call	puts
XX
XX		call	scsi_reset		;Reset the bus
XX
XX		mov	cx,0			;Scan for devices
XXscan:		mov	ax,cx
XX		add	al,030h
XX		mov	scan_dev,al
XX		mov	ax,1			;Create Select Bit
XX		shl	ax,cl
XX		if reserve_addr
XX		or	al,80h			;Add Card Select Bit
XX		endif
XX		mov	di,cur_unit
XX		mov	[di].unit_select,al
XX		mov	[di].unit_num_drv,0	;No Drives to start with
XX		mov	al,disk_count		;We will start with
XX		mov	[di].unit_1st_drv,al	;Drive Number if any
XX
XX		lea	dx,scan_dev		;Print the device number
XX		call	puts
XX		call	scsi_inquire		;Inquire as to its type
XX		jnc	scan_inq_ok
XX		jmp	scan_err
XX
XXscan_inq_ok:	mov	di,cur_unit
XX		lea	dx,[di].unit_inq_buf.inq_manufact
XX		mov	[di].unit_inq_term,'$'
XX		mov	al,[di].unit_inq_buf.inq_dev_type
XX		or	al,al			;Look at device type
XX		jz	scan_is_drv
XX		cmp	tape_unit,-1		;Do We Already Have A Tape?
XX		jnz	tape_jmp
XX		call	puts			;Make this our SCSITAPE: Unit
XX		mov	tape_unit,di
XX		lea	dx,tape_msg
XXtape_jmp:	jmp	scan_puts
XX
XXscan_is_drv:	call	puts			;Output the Device String
XX		call	scsi_capacity		;Inquire as to its size
XX		lea	dx,err_size
XX		jc	scan_puts		;Do not use unknown drives
XX		lea	dx,crlf
XX		call	puts
XX
XXscan_next_drv:	mov	di,cur_unit
XX		mov	al,disk_count		;Number Of Drives Found
XX		inc	al
XX		mov	disk_count,al
XX		mov	al,[di].unit_num_drv	;We have a valid Drive
XX		inc	al
XX		mov	[di].unit_num_drv,al
XX		mov	al,cur_drive		;Get Current Drive Letter
XX		mov	drv_msg_let,al		;Insert it in message
XX		inc	al			;Bump Drive Letter
XX		mov	cur_drive,al
XX		call	make_bpb		;Setup the BPB for this drive
XX		mov	di,cur_bpb		;Current Working BPB
XX		mov	ax,[di].bpb_ts
XX		shr	ax,11
XX		inc	ax
XX		lea	bx,drv_msg_size
XX		call	bin_ascii
XX		mov	bx,bpb_hw_mark		;Get the BPB High Water Mark
XX		inc	bx			;Bump HW Mark for next time
XX		inc	bx
XX		mov	ax,[bx]			;Get the BPB
XX		mov	cur_bpb,ax		;Make it the current BPB
XX		mov	bpb_hw_mark,bx
XX		lea	dx,drv_msg
XX		call	puts
XX		mov	bx,cur_unit
XX		mov	ah,0
XX		mov	al,[bx].unit_num_drv	;Insert Drive Offset
XX		dec	al			;Into BPB for this Drive
XX		mov	[di].bpb_hs_msw,ax
XX		mov	al,[bx].unit_cap_buf.cap_sectors_b3
XX		or	al,[bx].unit_cap_buf.cap_sectors_b2
XX		or	al,[bx].unit_cap_buf.cap_sectors_b1
XX		or	al,[bx].unit_cap_buf.cap_sectors_b0
XX		jnz	scan_next_drv		;Room left for another Drive
XX		jmp	short scan_next
XX
XXscan_err:	lea	dx,no_dev
XX		cmp	al,CNOCONNECT
XX		jz	scan_puts
XX		lea	dx,err_dev
XXscan_puts:	call	puts
XX		lea	dx,crlf
XX		call	puts
XX
XXscan_next:	inc	cx
XX		cmp	cx,MAXUNIT	;End of devices?
XX		jg	scan_exit
XX		mov	bx,cx		;Bump to next unit
XX		shl	bx,1
XX		mov	ax,word ptr unit_array[bx]
XX		mov	cur_unit,ax
XX		jmp	scan
XX
XXscan_exit:	ret
XXinitial		endp
XX
XX;
XX; Data Area Used Only During Initialization
XX;
XXhello_msg	db	0dh,0ah,'SCSI Device Driver Version 1.1',0Dh,0Ah,'$'
XXseg_msg		db	'Driver Loaded At Segment '
XXseg_msg_value	db	'0000',0dh,0ah,'$'
XXscan_dev	db	'X - ','$'
XXno_dev		db	'(No Installed Device)$'
XXerr_dev		db	'(Error On Device)$'
XXerr_size	db	'unknown size$'
XXdrv_msg		db	'  - Drive '
XXdrv_msg_let	db	'X: '
XXdrv_msg_size	db	'XXXXXX Meg',0dh,0ah,'$'
XXtape_msg	db	0dh,0ah,'  - Is The SCSITAPE: Device$'
XXcrlf		db	0dh,0ah,'$'
XX
XXdev_interrupt	endp
XX_TEXT		ends
XX		end
SHAR_EOF
if test 11664 -ne "`wc -c < 'scsi.asm'`"
then
echo shar: error transmitting "'scsi.asm'" '(should have been 11664 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'serase.c'" '(1658 characters)'
if test -f 'serase.c'
then
echo shar: will not over-write existing file "'serase.c'"
else
sed 's/^XX//' > 'serase.c' << \SHAR_EOF
XX/*
XX** SCSI Tape Erase (Low Level)
XX**
XX** usage: serase
XX**
XX** Revision History:
XX**
XX** Version 1.0  09/03/90 Initial Release
XX**
XX*/
XX#include <stdio.h>
XX#include <fcntl.h>
XX#include <dos.h>
XX
XX#define TRUE (1)
XX#define FALSE (0)
XX#define VERSION "serase Version 1.0 BWA"
XX
XXextern int _doserrno;
XX
XXstruct cmd {
XX	short command;		/* command type */
XX	short args;			/* command args */
XX	short unit;			/* command unit */
XX	short buf_ofs;		/* buffer offset */
XX	short buf_seg;		/* buffer segment */
XX} ioctl_data;
XXunion REGS inregs, outregs;
XXstruct SREGS segregs;
XXint fd;
XXchar *device = "SCSITAPE";
XXchar far *cp;
XX
XXmain(argc, argv)
XXint argc;
XXchar *argv[];
XX{
XX	/*
XX	** say hello
XX	*/
XX	puts(VERSION);
XX	if (argc != 1) usage();
XX
XX	/*
XX	** verify that this is what the user really wants to do
XX	*/
XX	printf("Do you really wish to erase the tape\n");
XX	printf("contained in SCSITAPE unit (y,n)? ");
XX	fflush(stdout);
XX	if ( getchar() != 'y' )
XX	{
XX		puts("Aborting erase ....");
XX		exit(1);
XX	}
XX
XX	/*
XX	** put together the command
XX	*/
XX	fd = open(device, O_WRONLY);
XX	if ( fd < 0 )
XX	{
XX		perror(device);
XX		exit(1);
XX	}
XX	inregs.h.ah = 0x44;			/* ioctl */
XX	inregs.h.al = 0x03;			/* write */
XX	inregs.x.bx = fd;			/* unit */
XX	inregs.x.cx = sizeof(struct cmd);
XX	cp = (char *) &ioctl_data;
XX	inregs.x.dx = FP_OFF(cp);
XX	segregs.ds = FP_SEG(cp);
XX	ioctl_data.command = 'E';
XX
XX	/*
XX	** start the format
XX	*/
XX	puts("Now erasing ....");
XX	puts("Please wait ....");
XX	intdosx(&inregs, &outregs, &segregs);
XX
XX	/*
XX	** see what happened
XX	*/
XX	if ( outregs.x.cflag )
XX		printf("DOS error %d occured during erase.\n", _doserrno);
XX	else
XX		puts("Erasing complete.");
XX	close(fd);
XX	exit(0);
XX}
XX
XXusage()
XX{
XX	puts("usage: serase");
XX	exit(1);
XX}
SHAR_EOF
if test 1658 -ne "`wc -c < 'serase.c'`"
then
echo shar: error transmitting "'serase.c'" '(should have been 1658 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'sformat.c'" '(1842 characters)'
if test -f 'sformat.c'
then
echo shar: will not over-write existing file "'sformat.c'"
else
sed 's/^XX//' > 'sformat.c' << \SHAR_EOF
XX/*
XX** SCSI Disk Formatter (Low Level)
XX**
XX** usage: sformat drive: [interleave]
XX**
XX** Revision History:
XX**
XX** Version 1.0  08/03/90 Initial Release
XX**
XX** Version 1.1  08/20/90 Add verification message.
XX*/
XX#include <stdio.h>
XX#include <dos.h>
XX
XX#define TRUE (1)
XX#define FALSE (0)
XX#define VERSION "sformat Version 1.1 BWA"
XX
XXextern int _doserrno;
XX
XXstruct cmd {
XX	short command;		/* command type */
XX	short args;			/* command args */
XX} ioctl_data;
XXunion REGS inregs, outregs;
XXstruct SREGS segregs;
XXunsigned short interleave = 0;
XXunsigned char drive;
XXchar far *cp;
XX
XXmain(argc, argv)
XXint argc;
XXchar *argv[];
XX{
XX	/*
XX	** say hello
XX	*/
XX	puts(VERSION);
XX
XX	/*
XX	** figure out who to format
XX	*/
XX	switch (argc)
XX	{
XX		case 3:
XX		interleave = atoi(argv[2]);
XX		case 2:
XX		if (argv[1][1] != ':') usage();
XX		drive = argv[1][0];
XX		drive = toupper(drive);
XX		drive -= '@';
XX		break;
XX
XX		default:
XX		usage();
XX		break;
XX	}
XX
XX	/*
XX	** verify that this is what the user really wants to do
XX	*/
XX	printf("Do you really wish to format the SCSI\n");
XX	printf("device that contains drive %c: (y,n)? ", argv[1][0]);
XX	fflush(stdout);
XX	if ( getchar() != 'y' )
XX	{
XX		puts("Aborting low level format ....");
XX		exit(1);
XX	}
XX
XX	/*
XX	** put together the command
XX	*/
XX	inregs.h.ah = 0x44;			/* ioctl */
XX	inregs.h.al = 0x05;			/* write */
XX	inregs.h.bl = drive;		/* unit */
XX	inregs.x.cx = sizeof(struct cmd);
XX	cp = (char *) &ioctl_data;
XX	inregs.x.dx = FP_OFF(cp);
XX	segregs.ds = FP_SEG(cp);
XX	ioctl_data.command = 'F';
XX	ioctl_data.args = interleave;
XX
XX	/*
XX	** start the format
XX	*/
XX	puts("Now formating ....");
XX	puts("Please wait ....");
XX	intdosx(&inregs, &outregs, &segregs);
XX
XX	/*
XX	** see what happened
XX	*/
XX	if ( outregs.x.cflag )
XX		printf("DOS error %d occured during format.\n", _doserrno);
XX	else
XX		puts("Formating complete.");
XX	exit(0);
XX}
XX
XXusage()
XX{
XX	puts("usage: sformat drive: [interleave]");
XX	exit(1);
XX}
SHAR_EOF
if test 1842 -ne "`wc -c < 'sformat.c'`"
then
echo shar: error transmitting "'sformat.c'" '(should have been 1842 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'struct.inc'" '(6833 characters)'
if test -f 'struct.inc'
then
echo shar: will not over-write existing file "'struct.inc'"
else
sed 's/^XX//' > 'struct.inc' << \SHAR_EOF
XX;
XX; Structures for SCSI commands
XX;
XXio_cmd		struc
XXio_cmd_op	db	?		;Opcode
XX		if extended_io
XXio_cmd_lun	db	?
XXio_cmd_lba_b3	db	?		;Logical Block Address
XXio_cmd_lba_b2	db	?
XXio_cmd_lba_b1	db	?
XXio_cmd_lba_b0	db	?
XXio_cmd_dummy1	db	?
XXio_cmd_cnt_b1	db	?		;Block Count
XXio_cmd_cnt_b0	db	?
XXio_cmd_dummy2	db	?
XX		else
XXio_cmd_lba_b2	db	?		;Logical Block Address / Lun
XXio_cmd_lba_b1	db	?
XXio_cmd_lba_b0	db	?
XXio_cmd_cnt_b0	db	?		;Block Count
XXio_cmd_dummy1	db	?
XX		endif
XXio_cmd		ends
XX
XXtio_cmd		struc
XXtio_cmd_op	db	?		;Opcode
XXtio_cmd_lun	db	?		;Lun
XXtio_cmd_cnt_b2	db	?		;Block Count
XXtio_cmd_cnt_b1	db	?
XXtio_cmd_cnt_b0	db	?
XXtio_cmd_dummy1	db	?
XXtio_cmd		ends
XX
XX;
XX; Format the Unit
XX;
XXfmt_cmd		struc
XXfmt_cmd_op	db	?		;Opcode
XXfmt_cmd_type	db	?		;Format Type
XXfmt_cmd_dummy1	db	?
XXfmt_cmd_il_b1	db	?		;Interleave (MSB)
XXfmt_cmd_il_b0	db	?		;Interleave (LSB)
XXfmt_cmd_dummy3	db	?
XXfmt_cmd		ends
XX
XX;
XX; Verify Sectors
XX;
XXver_cmd		struc
XXver_cmd_op	db	?		;Opcode
XXver_cmd_lun	db	?		;Lun
XXver_cmd_lba_b3	db	?		;Logical Block Address MSB
XXver_cmd_lba_b2	db	?
XXver_cmd_lba_b1	db	?
XXver_cmd_lba_b0	db	?		;Logical Block Address LSB
XXver_cmd_dummy1	db	?
XXver_cmd_len_b1	db	?		;Length MSB
XXver_cmd_len_b0	db	?		;Length LSB
XXver_cmd_dummy2	db	?
XXver_cmd		ends
XX
XX;
XX; Load / Unload a Tape
XX;
XXload_cmd	struc
XXload_cmd_op	db	?		;Opcode
XXload_cmd_lun	db	?		;Lun
XXload_cmd_dummy1	db	2 dup (?)
XXload_cmd_type	db	?		;Load / Unload
XXload_cmd_dummy2	db	?
XXload_cmd	ends
XX
XX;
XX; Structure returned by the sense command
XX;
XXsense		struc
XX		if extended_sense
XXsense_ccs	db	?		;0x70 for Extended Sense
XXsense_dummy1	db	?
XXsense_sense	db	?		;Sense (Error) Group
XXsense_lba_b3	db	?		;Failed Block Address
XXsense_lba_b2	db	?
XXsense_lba_b1	db	?
XXsense_lba_b0	db	?
XX		else
XXsense_sense	db	?		;Sense (Error) code
XXsense_lba_b2	db	?		;Failed Block Address
XXsense_lba_b1	db	?
XXsense_lba_b0	db	?
XX		endif
XXsense		ends
XX
XX;
XX; Structure returned by the unit inquiry command
XX;
XXinq		struc
XXinq_dev_type	db	?		;Device Type
XXinq_dev_qual	db	?		;Device Qualifier
XXinq_stand_rev	db	?		;Standard Revision Level
XXinq_format	db	?		;Response Format
XXinq_length	db	?		;Length of Extra Data
XXinq_reserv1	db	?
XXinq_reserv2	db	?
XXinq_reserv3	db	?
XXinq_manufact	db	8 dup (?)	;Manufacture
XXinq_product	db	16 dup (?)	;Product
XXinq		ends
XX
XX;
XX; Structure returned by the read drive capacity command
XX;
XXcap		struc
XXcap_sectors_b3	db	?		;MSB of sector count
XXcap_sectors_b2	db	?
XXcap_sectors_b1	db	?
XXcap_sectors_b0	db	?		;LSB of sector count
XXcap_size_b3	db	?		;MSB of sector size
XXcap_size_b2	db	?
XXcap_size_b1	db	?
XXcap_size_b0	db	?		;LSB of sector size
XXcap		ends
XX
XX;
XX; Structure Definitions For Our Device Driver
XX;
XXbpb		struc
XXbpb_ss		dw	?		;Sector Size
XXbpb_au		db	?		;Cluster Size in Sectors
XXbpb_rs		dw	?		;Reserved Sectors
XXbpb_nf		db	?		;Number of Fats
XXbpb_de		dw	?		;Number of Root Directory Entries
XXbpb_ts		dw	?		;Total Number Of Sectors
XXbpb_md		db	?		;Media Descriptor
XXbpb_fs		dw	?		;Number of Sectors in each Fat
XXbpb_st		dw	?		;Number of Sectors per Track
XXbpb_nh		dw	?		;Number of Heads
XXbpb_hs_lsw	dw	?		;Hidden Sectors (Least Sig Word)
XXbpb_hs_msw	dw	?		;Hidden Sectors (Most Sig Word)
XXbpb_ts_large	dd	?		;Large Total Sector Count
XXbpb_res		db	6 dup (?)	;Reserved
XXbpb		ends
XX
XX;
XX; ioctl function 42h
XX;
XXioctl_fmt	struc
XXioctl_fmt_spec	db	?		;Special Flags
XXioctl_fmt_head	dw	?		;Head to Format
XXioctl_fmt_cyl	dw	?		;Cylinder to Format
XXioctl_fmt	ends
XX
XX;
XX; ioctl function 60h
XX;
XXdpb		struc
XXdpb_special	db	?		;Special Flags
XXdpb_type	db	?		;Device Type
XXdpb_attr	dw	?		;Device Attributes
XXdpb_cyl		dw	?		;Device Cylinder Count
XXdpb_media	db	?		;Device Media Type if Diskette
XXdpb_bpb		db	size bpb dup (?)
XXdpb_sectors	dw	?		;Sectors in Track
XXdpb_track	dd	SECT_TRACK dup (?)
XXdpb		ends
XX
XX;
XX; The internal control structure for a SCSI device
XX;
XXunit		struc
XXunit_1st_drv	db	?		;DOS Drive Numbers
XXunit_num_drv	db	?		;DOS Drive Count
XXunit_select	db	?		;SCSI Select Bit
XXunit_mcheck	db	?		;Media Check Byte
XXunit_inq_buf	db	size inq dup (?)
XXunit_inq_term	db	?
XXunit_cap_buf	db	size cap dup (?)
XXunit_sense_buf	db	size sense dup (?)
XXunit		ends
XX
XX;
XX; Ioctl Commands
XX;
XXioc		struc
XXioc_command	dw	?		;Command
XXioc_param	dw	?		;Command Dependent Data
XXioc_unit	dw	?		;Forced SCSI Unit Number
XXioc_buf_ofs	dw	?		;Buffer Offset
XXioc_buf_seg	dw	?		;Buffer Segment
XXioc		ends
XX
XX;
XX; DOS requests
XX;
XXrh		struc
XXrh_len		db	?		;Length of Packet
XXrh_unit		db	?		;Unit Code (Block Only)
XXrh_cmd		db	?		;Command Code
XXrh_status	dw	?		;Returned Status
XXrh_res		db	8 dup (?)	;Reserved
XXrh		ends
XX
XXrh0		struc			;INITIALIZATION
XXrh0_rh		db	size rh dup (?)	;Fixed Portion
XXrh0_nunits	db	?		;Number of units (Block Only)
XXrh0_brk_ofs	dw	?		;Break Address (Offset)
XXrh0_brk_seg	dw	?		;Break Address (Segment)
XXrh0_bpb_tbo	dw	?		;Pointer to BPB Array (Offset)
XXrh0_bpb_tbs	dw	?		;Pointer to BPB Array (Segment)
XXrh0_drv_ltr	db	?		;First Available Drive (DOS 3+, Block Only)
XXrh0		ends
XX
XXrh1		struc			;MEDIA CHECK
XXrh1_rh		db	size rh dup (?)	;Fixed Portion
XXrh1_media	db	?		;Media Descriptor from DPB
XXrh1_md_stat	db	?		;Media Status returned by Device Driver
XXrh1_volid_ofs	dw	?		;Offset of Volume ID String (DOS 3+)
XXrh1_volid_seg	dw	?		;Segment of Volume ID String (DOS 3+)
XXrh1		ends
XX
XXrh2		struc			;GET BPB
XXrh2_rh		db	size rh dup (?)	;Fixed Portion
XXrh2_media	db	?		;Media Descriptor from DPB
XXrh2_buf_ofs	dw	?		;Offset of Data Transfer Area
XXrh2_buf_seg	dw	?		;Segment of Data Transfer Area
XXrh2_pbpbo	dw	?		;Offset of Pointer to BPB
XXrh2_pbpbs	dw	?		;Segment of Pointer to BPB
XXrh2		ends
XX
XXrh4		struc			;INPUT
XXrh4_rh		db	size rh dup (?)	;Fixed Portion
XXrh4_media	db	?		;Media Descriptor from DPB
XXrh4_buf_ofs	dw	?		;Offset of Data Transfer Area
XXrh4_buf_seg	dw	?		;Segment of Data Transfer Area
XXrh4_count	dw	?		;Transfer Count (Sectors)
XXrh4_start	dw	?		;Start Sector Number
XXrh4		ends
XX
XXrh8		struc			;OUTPUT
XXrh8_rh		db	size rh dup (?)	;Fixed Portion
XXrh8_media	db	?		;Media Descriptor from DPB
XXrh8_buf_ofs	dw	?		;Offset of Data Transfer Area
XXrh8_buf_seg	dw	?		;Segment of Data Transfer Area
XXrh8_count	dw	?		;Transfer Count (Sectors)
XXrh8_start	dw	?		;Start Sector Number
XXrh8		ends
XX
XXrh9		struc			;OUTPUT VERIFY
XXrh9_rh		db	size rh dup (?)	;Fixed Portion
XXrh9_media	db	?		;Media Descriptor from DPB
XXrh9_buf_ofs	dw	?		;Offset of Data Transfer Area
XXrh9_buf_seg	dw	?		;Segment of Data Transfer Area
XXrh9_count	dw	?		;Transfer Count (Sectors)
XXrh9_start	dw	?		;Start Sector Number
XXrh9		ends
XX
XXrh12		struc			;OUTPUT IOCTL
XXrh12_rh		db	size rh dup (?)	;Fixed Portion
XXrh12_media	db	?		;Media Descriptor from DPB
XXrh12_buf_ofs	dw	?		;Offset of Data Transfer Area
XXrh12_buf_seg	dw	?		;Segment of Data Transfer Area
XXrh12_count	dw	?		;Transfer Count (Sectors)
XXrh12_start	dw	?		;Start Sector Number
XXrh12		ends
XX
XXrh19		struc			;IOCTL
XXrh19_rh		db	size rh dup (?)	;Fixed Portion
XXrh19_major	db	?		;Major Code
XXrh19_minor	db	?		;Minor Code
XXrh19_si		dw	?		;Caller SI Register
XXrh19_di		dw	?		;Caller DI Register
XXrh19_buf_ofs	dw	?		;Caller Buffer Offset
XXrh19_buf_seg	dw	?		;Caller Buffer Segment
XXrh19		ends
SHAR_EOF
if test 6833 -ne "`wc -c < 'struct.inc'`"
then
echo shar: error transmitting "'struct.inc'" '(should have been 6833 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'subs.asm'" '(15406 characters)'
if test -f 'subs.asm'
then
echo shar: will not over-write existing file "'subs.asm'"
else
sed 's/^XX//' > 'subs.asm' << \SHAR_EOF
XX;
XX; Data storage for local subroutines
XX;
XXcmd_ready	db	SCSI_TESTREADY,0,0,0,0,0
XXcmd_sense	db	SCSI_REQSENSE,0,0,0,size sense,0
XXcmd_format	db	SCSI_FORMATUNIT,0,0,0,0,0
XX		if extended_io
XXcmd_read	db	SCSI_READBLK,0,0,0,0,0,0,0,1,0
XXcmd_write	db	SCSI_WRITEBLK,0,0,0,0,0,0,0,1,0
XX		else
XXcmd_read	db	SCSI_READBLK,0,0,0,1,0
XXcmd_write	db	SCSI_WRITEBLK,0,0,0,1,0
XX		endif
XXcmd_tread	db	SCSI_READBLK,1,0,0,0,0
XXcmd_twrite	db	SCSI_WRITEBLK,1,0,0,0,0
XXcmd_twritefm	db	SCSI_WRITEFM,0,0,0,1,0
XXcmd_inquire	db	SCSI_INQUIRY,0,0,0,size inq,0
XXcmd_erase	db	SCSI_ERASE,1,0,0,0,0
XXcmd_load	db	SCSI_LOAD,0,0,0,0,0
XXcmd_capacity	db	SCSI_READSIZE,0,0,0,0,0,0,0,0,0
XXcmd_verify	db	SCSI_VERIFYBLK,0,0,0,0,0,0,0,SECT_TRACK,0
XX
XX		even
XXdocmd_cmd	dw	?
XXdocmd_buf	dw	?
XXdocmd_buf_seg	dw	?
XXdocmd_len	dw	?
XXdocmd_status	db	?
XXdocmd_tempb	db	?
XX
XX		if dump_sense
XXsense_msg	db	0dh,07h,'SCSI Unit: 0x'
XXsense_unit	db	'xx, Sense Status: 0x'
XXsense_code	db	'xx, Block Address: 0x'
XX		if extended_sense
XXsense_addr3	db	'xx'
XX		endif
XXsense_addr2	db	'xx'
XXsense_addr1	db	'xx'
XXsense_addr0	db	'xx',0dh,0ah,'$'
XX		endif
XX
XX;
XX; Reset the SCSI Bus
XX;
XXscsi_reset	proc	near
XX		pusha
XX
XX		mov	ax,SCSI_CARD_SEG	;Point at the command port
XX		mov	es,ax
XX		mov	si,SCSI_CMD_PORT
XX
XX		mov	al,CMDBASE or CMDENABLE or CMDRST
XX		mov	es:[si],al		;Reset the bus
XX		call	wait1ms
XX		mov	al,CMDBASE
XX		mov	es:[si],al		;All done
XX		mov	cx,250			;Wait 250ms
XXreset_loop:	call	wait1ms
XX		loop	reset_loop
XX
XX		popa
XX		ret
XXscsi_reset	endp
XX
XX;
XX; Test the Ready Status of a unit
XX;
XX; al = return code, 'C' error
XX;
XXscsi_ready	proc	near
XX		lea	di,cmd_ready			;Command
XX		call	docmd
XX		ret
XXscsi_ready	endp
XX
XX;
XX; Request Sense data from a unit and display the result
XX; Called after every SCSI command with the exit code in 'al'
XX;
XXscsi_sense	proc	near
XX		pushf
XX		pusha
XX		mov	di,cur_unit			;Unit
XX		lea	bx,[di].unit_sense_buf		;Buffer Offset
XX		push	ds				;Buffer Segment
XX		pop	es
XX		mov	cx,size sense			;Buffer Size
XX		lea	di,cmd_sense			;Command
XX		call	docmd
XX		if dump_sense
XX		jc	sense_exit
XX		mov	di,cur_unit
XX		mov	dl,[di].unit_select
XX		if reserve_addr
XX		and	dl,07Fh			;Remove Cards Bit
XX		endif
XX		lea	bx,sense_unit		;Unit
XX		call	hex2asc2
XX		mov	dl,[di].unit_sense_buf.sense_sense
XX		lea	bx,sense_code		;Sense
XX		call	hex2asc2
XX		if extended_sense
XX		mov	dl,[di].unit_sense_buf.sense_lba_b3
XX		lea	bx,sense_addr3		;Address
XX		call	hex2asc2
XX		endif
XX		mov	dl,[di].unit_sense_buf.sense_lba_b2
XX		lea	bx,sense_addr2
XX		call	hex2asc2
XX		mov	dl,[di].unit_sense_buf.sense_lba_b1
XX		lea	bx,sense_addr1
XX		call	hex2asc2
XX		mov	dl,[di].unit_sense_buf.sense_lba_b0
XX		lea	bx,sense_addr0
XX		call	hex2asc2
XX		lea	dx,sense_msg
XX		call	puts
XX		endif
XXsense_exit:	popa
XX		popf
XX		ret
XXscsi_sense	endp
XX
XX;
XX; Inquire about the type of a unit
XX;
XX; al = return code, 'C' error indicates an error
XX;
XXscsi_inquire	proc	near
XX		push	cx
XX		mov	di,cur_unit			;Unit
XX		lea	bx,[di].unit_inq_buf		;Buffer Offset
XX		push	ds				;Buffer Segment
XX		pop	es
XX		mov	cx,size inq			;Buffer Size
XX		lea	di,cmd_inquire			;Command
XX		call	docmd
XX		jnc	inquire_exit
XX		call	scsi_sense
XXinquire_exit:	pop	cx
XX		ret
XXscsi_inquire	endp
XX
XX;
XX; Determine the size of a disk
XX;
XX; al = return code, 'C' error indicates an error
XX;
XXscsi_capacity	proc	near
XX		push	cx
XX		mov	di,cur_unit			;Unit
XX		lea	bx,[di].unit_cap_buf		;Buffer Offset
XX		push	ds				;Buffer Segment
XX		pop	es
XX		mov	cx,size cap			;Buffer Size
XX		lea	di,cmd_capacity			;Command
XX		call	docmd
XX		jnc	capacity_exit
XX		call	scsi_sense
XXcapacity_exit:	pop	cx
XX		ret
XXscsi_capacity	endp
XX
XX;
XX; Verify the Track given in an IOCTL Request
XX;
XX; al = return code, 'C' indicates an error
XX;
XXscsi_verify	proc	near
XX		mov	di,es:[bx].rh19_buf_ofs		;Command Offset
XX		mov	ax,es:[bx].rh19_buf_seg		;Command Segment
XX		mov	es,ax
XX		mov	ax,es:[di].ioctl_fmt_cyl	;Track
XX		shl	ax,SECT_2_CYL			;Convert to Sector
XX
XX		mov	di,cur_bpb			;Add to Drive Offset
XX		mov	dx,[di].bpb_hs_msw
XX
XX		lea	di,cmd_verify			;Command
XX		mov	[di].ver_cmd_lba_b3,dh		;Insert Sector
XX		mov	[di].ver_cmd_lba_b2,dl		; into Command
XX		mov	[di].ver_cmd_lba_b1,ah		;Insert Sector
XX		mov	[di].ver_cmd_lba_b0,al		; into Command
XX		call	docmd
XX		jnc	verify_exit
XX		call	scsi_sense
XXverify_exit:	ret
XXscsi_verify	endp
XX
XX;
XX; Read Some Blocks from the disk given
XX; the request header in es:bx
XX;
XX; al = return code, 'C' indicates an error
XX;
XXdisk_read	proc	near
XX		mov	di,bx
XX		mov	cx,es:[di].rh4_count		;Sector Count
XX		mov	dx,es:[di].rh4_start		;Starting Sector
XX		mov	bx,es:[di].rh4_buf_ofs		;Buffer Offset
XX		mov	ax,es:[di].rh4_buf_seg		;Buffer Segment
XX		mov	es,ax
XX
XX		mov	si,cur_bpb
XX		lea	di,cmd_read			;Command
XX		mov	ax,[si].bpb_hs_msw		;Drive Sector Offset
XX		if extended_io
XX		mov	[di].io_cmd_lba_b3,ah		;Insert Sector
XX		endif
XX		mov	[di].io_cmd_lba_b2,al		;Into the Command
XX
XX		if multi_sector
XX		mov	ax,cx				;Get Sector Count
XX		and	ax,CHUNK_MAX-1			;Mask Off the I/O Chunk
XX		jnz	disk_r_cok1			;Check for Boundary
XX		mov	ax,CHUNK_MAX
XXdisk_r_cok1:	shl	ax,9				;Convert to Buffer Size
XX		add	ax,bx				;Check for Wrap
XX		else
XX		mov	ax,bx				;Check for Wrap
XX		add	ax,P_SECT			;The First Time
XX		endif
XXdisk_r_loop:	jnc	disk_r_nowrap
XX		mov	ax,bx				;Normalize the
XX		shr	ax,4				;Segment and
XX		mov	si,es				;Offset so that
XX		add	si,ax				;It dosn't Wrap
XX		mov	es,si
XX		and	bx,000Fh
XXdisk_r_nowrap:	push	cx
XX		mov	[di].io_cmd_lba_b1,dh		;Insert Sector
XX		mov	[di].io_cmd_lba_b0,dl		;Into the Command
XX		if multi_sector
XX		and	cx,CHUNK_MAX-1			;Mask Off the I/O Chunk
XX		jnz	disk_r_cok2			;Check for Boundary
XX		mov	cx,CHUNK_MAX
XXdisk_r_cok2:
XX		if extended_io
XX		mov	[di].io_cmd_cnt_b1,ch		;Insert Sector Count
XX		endif
XX		mov	[di].io_cmd_cnt_b0,cl		;Into the Command
XX		shl	cx,9				;Convert to Buffer Size
XX		else
XX		mov	cx,P_SECT			;Buffer Size
XX		endif
XX		call	docmd
XX		pop	cx
XX		jc	disk_r_exit
XX		if multi_sector
XX		mov	ax,cx				;Get Sector Count
XX		and	ax,CHUNK_MAX-1			;Mask Off the I/O Chunk
XX		jnz	disk_r_cok3			;Check for Boundary
XX		mov	ax,CHUNK_MAX
XXdisk_r_cok3:	sub	cx,ax				;Dec Sector Count
XX		jz	disk_r_exit
XX		add	dx,ax				;Bump to next Sector
XX		shl	ax,9				;Convert to Buffer Size
XX		add	bx,ax
XX		jmp	short disk_r_loop
XX		else
XX		inc	dx				;Bump to next Sector
XX		add	bx,P_SECT
XX		loop	disk_r_loop
XX		clc
XX		endif
XXdisk_r_exit:	jnc	disk_r_exit2			;If no error occured
XX		call	scsi_sense			;Display Sense Status
XXdisk_r_exit2:	mov	es,rh_seg
XX		mov	bx,rh_off
XX		pushf
XX		mov	ax,es:[bx].rh4_count		;Update the Count
XX		sub	ax,cx
XX		mov	es:[bx].rh4_count,ax
XX		popf
XX		ret
XXdisk_read	endp
XX
XX;
XX; Write Some Blocks to the disk given
XX; the request header in es:bx
XX;
XX; al = return code, 'C' indicates an error
XX;
XXdisk_write	proc	near
XX		mov	di,bx
XX		mov	cx,es:[di].rh8_count		;Sector Count
XX		mov	dx,es:[di].rh8_start		;Starting Sector
XX		mov	bx,es:[di].rh8_buf_ofs		;Buffer Offset
XX		mov	ax,es:[di].rh8_buf_seg		;Buffer Segment
XX		mov	es,ax
XX
XX		mov	si,cur_bpb
XX		lea	di,cmd_write			;Command
XX		mov	ax,[si].bpb_hs_msw		;Drive Sector Offset
XX		if extended_io
XX		mov	[di].io_cmd_lba_b3,ah		;Insert Sector
XX		endif
XX		mov	[di].io_cmd_lba_b2,al		;Into the Command
XX
XX		if multi_sector
XX		mov	ax,cx				;Get Sector Count
XX		and	ax,CHUNK_MAX-1			;Mask Off the I/O Chunk
XX		jnz	disk_w_cok1			;Check for Boundary
XX		mov	ax,CHUNK_MAX
XXdisk_w_cok1:	shl	ax,9				;Convert to Buffer Size
XX		add	ax,bx				;Check for Wrap
XX		else
XX		mov	ax,bx				;Check for Wrap
XX		add	ax,P_SECT			;The First Time
XX		endif
XXdisk_w_loop:	jnc	disk_w_nowrap
XX		mov	ax,bx				;Normalize the
XX		shr	ax,4				;Segment and
XX		mov	si,es				;Offset so that
XX		add	si,ax				;It dosn't Wrap
XX		mov	es,si
XX		and	bx,000Fh
XXdisk_w_nowrap:	push	cx
XX		mov	[di].io_cmd_lba_b1,dh		;Insert Sector
XX		mov	[di].io_cmd_lba_b0,dl		;Into the Command
XX		if multi_sector
XX		and	cx,CHUNK_MAX-1			;Mask Off the I/O Chunk
XX		jnz	disk_w_cok2			;Check for Boundary
XX		mov	cx,CHUNK_MAX
XXdisk_w_cok2:
XX		if extended_io
XX		mov	[di].io_cmd_cnt_b1,ch		;Insert Sector Count
XX		endif
XX		mov	[di].io_cmd_cnt_b0,cl		;Into the Command
XX		shl	cx,9				;Convert to Buffer Size
XX		else
XX		mov	cx,P_SECT			;Buffer Size
XX		endif
XX		call	docmd
XX		pop	cx
XX		jc	disk_w_exit
XX		if multi_sector
XX		mov	ax,cx				;Get Sector Count
XX		and	ax,CHUNK_MAX-1			;Mask Off the I/O Chunk
XX		jnz	disk_w_cok3			;Check for Boundary
XX		mov	ax,CHUNK_MAX
XXdisk_w_cok3:	sub	cx,ax				;Dec Sector Count
XX		jz	disk_w_exit
XX		add	dx,ax				;Bump to next Sector
XX		shl	ax,9				;Convert to Buffer Size
XX		add	bx,ax
XX		jmp	short disk_w_loop
XX		else
XX		inc	dx				;Bump to next Sector
XX		add	bx,P_SECT
XX		loop	disk_w_loop
XX		clc
XX		endif
XXdisk_w_exit:	jnc	disk_w_exit2			;If no error occured
XX		call	scsi_sense			;Display Sense Status
XXdisk_w_exit2:	mov	es,rh_seg
XX		mov	bx,rh_off
XX		pushf
XX		mov	ax,es:[bx].rh8_count		;Update the Count
XX		sub	ax,cx
XX		mov	es:[bx].rh8_count,ax
XX		popf
XX		ret
XXdisk_write	endp
XX
XX;
XX; Read Some Blocks from the Tape
XX;
XXtape_read	proc	near
XX		mov	write_flag,FALSE		;Cancel if READ seen
XX		mov	di,bx
XX		mov	cx,es:[di].rh4_count		;Byte Count
XX		mov	ax,cx				;Test for invalid
XX		and	ax,P_SECT-1			;Byte Count
XX		jz	tape_r_ok
XX		mov	es:[di].rh4_count,0		;Nothing Read
XX		stc					;Oops
XX		ret
XXtape_r_ok:	mov	bx,es:[di].rh4_buf_ofs		;Buffer Offset
XX		mov	ax,es:[di].rh4_buf_seg		;Buffer Segment
XX		mov	es,ax
XX		mov	ax,bx				;Normalize the
XX		shr	ax,4				;Segment and
XX		mov	si,es				;Offset so that
XX		add	si,ax				;It dosn't Wrap
XX		mov	es,si
XX		and	bx,000Fh
XX		lea	di,cmd_tread
XX		mov	ax,cx				;Convert Bytes
XX		shr	ax,9				;to Blocks
XX		mov	[di].tio_cmd_cnt_b1,ah		;Insert into Command
XX		mov	[di].tio_cmd_cnt_b0,al
XX		call	docmd
XX		jnc	tape_r_exit
XX		call	scsi_sense
XXtape_r_exit:	ret
XXtape_read	endp
XX
XX;
XX; Write Some Blocks to the Tape
XX;
XXtape_write	proc	near
XX		mov	write_flag,TRUE			;Write Done
XX		mov	di,bx
XX		mov	cx,es:[di].rh8_count		;Byte Count
XX		mov	ax,cx				;Test for invalid
XX		and	ax,P_SECT-1			;Byte Count
XX		jz	tape_w_ok
XX		mov	es:[di].rh8_count,0		;Nothing Write
XX		mov	write_flag,FALSE		;Cancel if ERROR!
XX		stc					;Oops
XX		ret
XXtape_w_ok:	mov	cx,es:[di].rh8_count		;Byte Count
XX		mov	bx,es:[di].rh8_buf_ofs		;Buffer Offset
XX		mov	ax,es:[di].rh8_buf_seg		;Buffer Segment
XX		mov	es,ax
XX		mov	ax,bx				;Normalize the
XX		shr	ax,4				;Segment and
XX		mov	si,es				;Offset so that
XX		add	si,ax				;It dosn't Wrap
XX		mov	es,si
XX		and	bx,000Fh
XX		lea	di,cmd_twrite
XX		mov	ax,cx				;Convert Bytes
XX		shr	ax,9				;to Blocks
XX		mov	[di].tio_cmd_cnt_b1,ah		;Insert into Command
XX		mov	[di].tio_cmd_cnt_b0,al
XX		call	docmd
XX		jnc	tape_w_exit
XX		mov	write_flag,FALSE		;Cancel if ERROR!
XX		call	scsi_sense
XXtape_w_exit:	ret
XXtape_write	endp
XX
XX;
XX; Do a command
XX;
XX; bx => buffer for returned information
XX; cx = buffer len
XX; di => command string
XX; es = buffer segment for returned information
XX;
XX; al = return code, 'C' indicates an error
XX;
XXdocmd		proc	near
XX		pusha
XX		push	es
XX
XX		mov	docmd_buf,bx		;Save our arguments
XX		mov	docmd_buf_seg,es
XX		mov	docmd_len,cx
XX		mov	docmd_cmd,di
XX
XX		mov	ax,SCSI_CARD_SEG	;Point at the Card
XX		mov	es,ax
XX		mov	si,SCSI_CMD_PORT	;Command Port
XX
XX;
XX; Wait for the Bus to become free
XX;
XX		mov	cx,65535
XXidle_loop:	mov	al,es:[si]		;Get the Status
XX		and	al,FREE_MASK
XX		jz	try_sel
XX		loop	idle_loop
XX
XX		call	scsi_reset
XX		mov	al,CBUSBUSY		;Bus still BUSY?
XX		jmp	docmd_exit
XX
XXtry_sel:	mov	al,CMDBASE		;Try to select target
XX		mov	es:[si],al
XX
XX		mov	di,cur_unit
XX		mov	al,[di].unit_select	;Get our Select Bit
XX		mov	di,SCSI_DATA_PORT	;Data Port
XX		mov	es:[di],al
XX
XX		call	wait100us		;Spec says wait 90us here
XX		mov	al,CMDBASE or CMDENABLE or CMDSEL
XX		mov	es:[si],al
XX
XX;
XX; Wait 250 ms for the Target to be SELected
XX;
XX		mov	cx,2500
XXsel_loop:	test	byte ptr es:[si],STBSY	;Look for BSY bit
XX		jnz	cmd_xfer
XX		call	wait100us
XX		loop	sel_loop
XX
XX		mov	al,CMDBASE or CMDSEL	;Release the data BUS
XX		mov	es:[si],al
XX		call	wait100us		;Spec says wait 290us
XX		call	wait100us		;to abort selection phase
XX		call	wait100us
XX		test	byte ptr es:[si],STBSY	;Look one final time
XX		jnz	cmd_xfer		;Device did answer
XX		mov	al,CNOCONNECT		;Nothing Answered
XX		jmp	docmd_exit
XX
XX;
XX; Start the Command
XX;
XXcmd_xfer:	call	wait100us		;Spec say wait 90us here
XX		mov	al,CMDBASE or CMDENABLE	;Drop SEL and begin talking
XX		mov	es:[si],al
XXxfer_loop:	mov	al,es:[si]
XX		test	al,STBSY		;Look for BSY bit
XX		jz	xfer_error
XX		test	al,STREQ		;And REQ bit
XX		jz	xfer_loop
XX
XX		and	al,REQ_MASK		;Look at REQ type
XX
XX		cmp	al,REQ_DATAOUT		;Is it Data Out?
XX		jnz	try_datain
XX		call	send_data
XX		jmp	short xfer_loop
XX
XXtry_datain:	cmp	al,REQ_DATAIN		;Is it Data In?
XX		jnz	try_cmdout
XX		call	receive_data
XX		jmp	short xfer_loop
XX
XXtry_cmdout:	cmp	al,REQ_CMDOUT		;Is it Command Out?
XX		jnz	try_statin
XX		call	send_cmd
XX		jmp	short xfer_loop
XX
XXtry_statin:	cmp	al,REQ_STATIN		;Is it Status In?
XX		jnz	try_msgout
XX		mov	al,es:[di]		;Get the Status Byte
XX		mov	docmd_status,al
XX		jmp	short xfer_loop
XX
XXtry_msgout:	cmp	al,REQ_MSGOUT		;Is it Message Out?
XX		jnz	try_msgin
XX		call	send_nop
XX		jmp	short xfer_loop
XX
XXtry_msgin:	cmp	al,REQ_MSGIN		;Is it Message In?
XX		jnz	xfer_error
XX
XX		mov	al,es:[di]		;Get Message Byte
XX		cmp	al,MSG_COMPLETE		;Are We All Done?
XX		jnz	short xfer_loop
XX		mov	al,docmd_status
XX		or	al,al			;Did we have an error?
XX		mov	al,COK			;Preload OK code
XX		jz	docmd_exit
XX
XXxfer_error:	mov	al,CERROR		;Command Failed Somehow
XX
XXdocmd_exit:	mov	docmd_tempb,al
XX		mov	al,CMDBASE		;Release the BUS
XX		mov	es:[si],al
XX		pop	es
XX		popa
XX		mov	al,docmd_tempb
XX		cmp	al,COK
XX		jz	docmd_exit_ok
XX		stc
XXdocmd_exit_ok:	ret
XXdocmd		endp
XX
XX;
XX; Receive a Data Stream from the card
XX; On entry es:[di] points at the data port
XX;          es:[si] points at the command port
XX;
XXreceive_data	proc	near
XX		mov	dx,es			;Save ES
XX
XX		mov	bx,si
XX		mov	ax,es
XX		mov	cx,docmd_len		;Length
XX		mov	di,docmd_buf		;Dest Offset
XX		mov	es,docmd_buf_seg	;Dest Segment
XX		mov	si,SCSI_DATA_PORT	;Source Offset
XX		mov	ds,ax			;Source Segment
XX		mov	al,STREQ
XX		cld
XX
XXreceive_loop:	movsb
XX		if multi_sector
XX		dec	si			;Don't blow the card buffer
XX		endif
XX		dec	cx
XX		jz	receive_exit
XXreceive_wait:	test	byte ptr [bx],al	;Ready?
XX		jnz	receive_loop
XX		jmp	short receive_wait
XX
XXreceive_exit:	mov	si,SCSI_CMD_PORT	;Restore the Environment
XX		mov	di,SCSI_DATA_PORT
XX		mov	ax,cs
XX		mov	ds,ax
XX		mov	es,dx
XX		ret
XXreceive_data	endp
XX
XX;
XX; Send a Command to the card
XX; On entry es:[di] points at the data port
XX;          es:[si] points at the command port
XX;
XXsend_cmd	proc	near
XX		mov	bx,docmd_cmd		;Get Command Pointer
XX		mov	al,[bx]			;Get a Command Byte
XX		mov	es:[di],al		;Send it to Card
XX		inc	bx			;Bump for Next Time
XX		mov	docmd_cmd,bx
XX		ret
XXsend_cmd	endp
XX
XX;
XX; Send a Data Stream to the card
XX; On entry es:[di] points at the data port
XX;          es:[si] points at the command port
XX;
XXsend_data	proc	near
XX		mov	bx,si
XX		mov	cx,docmd_len		;Get the Data Count
XX		mov	si,docmd_buf		;Source Offset
XX		mov	ds,docmd_buf_seg	;Source Segment
XX		mov	al,STREQ
XX		cld
XX
XXsend_loop:	movsb
XX		if multi_sector
XX		dec	di			;Don't blow the card buffer
XX		endif
XX		dec	cx
XX		jz	send_exit
XXsend_wait:	test	byte ptr es:[bx],al	;Ready?
XX		jnz	send_loop
XX		jmp	short send_wait
XX
XXsend_exit:	mov	si,SCSI_CMD_PORT	;Restore the Environment
XX		mov	di,SCSI_DATA_PORT
XX		mov	ax,cs
XX		mov	ds,ax
XX		ret
XXsend_data	endp
XX
XX;
XX; Send a NOP Message
XX;
XXsend_nop	proc	near
XX		mov	al,MSG_NOP		;Oops, send a nop
XX		mov	es:[di],al
XX		mov	al,CMDBASE or CMDENABLE
XX		mov	es:[si],al
XX		ret
XXsend_nop	endp
XX
XX;
XX; Wait One Milli second
XX;
XX; The value of 'cx' is computed for an 8 Mhz Clock
XX;
XXwait1ms		proc	near
XX		push	cx		;   (3) = 375ns
XX		mov	cx,798		;   (2) = 250ns
XXwait_m_loop:	loop	wait_m_loop	;  (10) = 1250ns * X
XX		pop	cx		;   (5) = 625ns
XX		ret			; (11+) = 1375ns
XXwait1ms		endp
XX
XX;
XX; Wait One Hundred Micros Seconds
XX;
XX; The value of 'cx' is computed for an 8 Mhz Clock
XX;
XXwait100us	proc	near
XX		push	cx		;   (3) = 375ns
XX		mov	cx,78		;   (2) = 250ns
XXwait_u_loop:	loop	wait_u_loop	;  (10) = 1250ns * X
XX		pop	cx		;   (5) = 625ns
XX		ret			; (11+) = 1375ns
XXwait100us	endp
SHAR_EOF
if test 15406 -ne "`wc -c < 'subs.asm'`"
then
echo shar: error transmitting "'subs.asm'" '(should have been 15406 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'units.asm'" '(3322 characters)'
if test -f 'units.asm'
then
echo shar: will not over-write existing file "'units.asm'"
else
sed 's/^XX//' > 'units.asm' << \SHAR_EOF
XX;
XX; target information/control structures
XX;
XX		even
XXunit0		db	size unit dup (-1)
XX		even
XXunit1		db	size unit dup (-1)
XX		even
XXunit2		db	size unit dup (-1)
XX		even
XXunit3		db	size unit dup (-1)
XX		even
XXunit4		db	size unit dup (-1)
XX		even
XXunit5		db	size unit dup (-1)
XX		even
XXunit6		db	size unit dup (-1)
XX		ife reserve_addr
XX		even
XXunit7		db	size unit dup (-1)
XX		endif
XX
XX		even
XXbpb0		db	size bpb dup (-1)
XX		even
XXbpb1		db	size bpb dup (-1)
XX		even
XXbpb2		db	size bpb dup (-1)
XX		even
XXbpb3		db	size bpb dup (-1)
XX		even
XXbpb4		db	size bpb dup (-1)
XX		even
XXbpb5		db	size bpb dup (-1)
XX		even
XXbpb6		db	size bpb dup (-1)
XX		even
XXbpb7		db	size bpb dup (-1)
XX		even
XXbpb8		db	size bpb dup (-1)
XX		even
XXbpb9		db	size bpb dup (-1)
XX		even
XXbpbA		db	size bpb dup (-1)
XX		even
XXbpbB		db	size bpb dup (-1)
XX		even
XXbpbC		db	size bpb dup (-1)
XX		even
XXbpbD		db	size bpb dup (-1)
XX		even
XXbpbE		db	size bpb dup (-1)
XX		even
XXbpbF		db	size bpb dup (-1)
XX
XX		even
XXunit_array	dw	unit0
XX		dw	unit1
XX		dw	unit2
XX		dw	unit3
XX		dw	unit4
XX		dw	unit5
XX		dw	unit6
XX		ife reserve_addr
XX		dw	unit7
XX		endif
XX
XX		even
XXbpb_array	dw	bpb0		;BPB Array for DOS
XX		dw	bpb1
XX		dw	bpb2
XX		dw	bpb3
XX		dw	bpb4
XX		dw	bpb5
XX		dw	bpb6
XX		dw	bpb7
XX		dw	bpb8
XX		dw	bpb9
XX		dw	bpbA
XX		dw	bpbB
XX		dw	bpbC
XX		dw	bpbD
XX		dw	bpbE
XX		dw	bpbF
XXbpb_hw_mark	dw	bpb_array
XX
XXtape_unit	dw	-1
XXcur_unit	dw	unit0
XXcur_bpb		dw	bpb0
XX
XX;
XX; Given the request header in es:bx
XX; Return a pointer in ds:di to the unit entry
XX; or 'C' if no such unit exists.
XX;
XX; Do not destroy es:bx !!!
XX;
XXfind_unit	proc	near
XX		pusha
XX		mov	ah,es:[bx].rh_unit	;What drive did they want
XX		lea	di,unit_array
XX		lea	si,bpb_array
XX		mov	cx,MAXUNIT		;How many to search
XXfind_loop:	mov	bx,[di]			;Point at a unit	
XX		mov	al,[bx].unit_num_drv	;Does this SCSI device
XX		or	al,al			;Have any Drives Defined?
XX		jz	find_next
XX		mov	dh,[bx].unit_1st_drv	;Get First Drive Number
XXfind_unit_loop:	cmp	ah,dh			;Is this the correct drive?
XX		jz	find_match
XX		inc	si			;Bump to next BPB
XX		inc	si
XX		inc	dh			;Bump Drive Number
XX		dec	al			;Dec Drive count
XX		jnz	find_unit_loop		;Try next Drive
XX		jmp	short find_next		;Try next SCSI device
XXfind_match:	mov	cur_unit,bx		;Found a match
XX		mov	ax,[si]
XX		mov	cur_bpb,ax
XX		clc
XX		jmp	find_exit
XXfind_next:	inc	di
XX		inc	di
XX		loop	find_loop
XX		stc				;No More units, Error
XXfind_exit:	popa
XX		ret
XXfind_unit	endp
XX
XX;
XX; Given the data in a unit entry,
XX; create the bpb for the unit.
XX;
XXmake_bpb	proc	near
XX		mov	di,cur_bpb		;Get the current BPB
XX		mov	bx,cur_unit		;Get the current Unit
XX		mov	[di].bpb_ss,P_SECT
XX		mov	[di].bpb_au,CLUSTSIZE
XX		mov	[di].bpb_rs,1
XX		mov	[di].bpb_nf,2
XX		mov	[di].bpb_de,512
XX		mov	ah,[bx].unit_cap_buf.cap_sectors_b3
XX		mov	al,[bx].unit_cap_buf.cap_sectors_b2
XX		or	ax,ax
XX		jz	make_bpb_last		;Use up the last few sectors
XX		dec	ax			;Use up 65536 Sectors
XX		mov	[bx].unit_cap_buf.cap_sectors_b3,ah
XX		mov	[bx].unit_cap_buf.cap_sectors_b2,al
XX		mov	dx,65535		;Max of 32 Meg
XX		jmp	short make_bpb_ts
XXmake_bpb_last:	mov	dh,[bx].unit_cap_buf.cap_sectors_b1
XX		mov	[bx].unit_cap_buf.cap_sectors_b1,0
XX		mov	dl,0			;Round to nearest Cyl
XX		mov	[bx].unit_cap_buf.cap_sectors_b0,0
XX		dec	dx			;Make it zero relative
XXmake_bpb_ts:	mov	[di].bpb_ts,dx
XX		mov	[di].bpb_md,0F8h
XX		shr	dx,SECT_2_FS
XX		inc	dx			;Allow for round-off
XX		mov	[di].bpb_fs,dx
XX		mov	[di].bpb_st,SECT_TRACK
XX		mov	[di].bpb_nh,1
XX		mov	[di].bpb_hs_lsw,0
XX		mov	[di].bpb_hs_msw,0
XX		ret
XXmake_bpb	endp
SHAR_EOF
if test 3322 -ne "`wc -c < 'units.asm'`"
then
echo shar: error transmitting "'units.asm'" '(should have been 3322 characters)'
fi
fi # end of overwriting check
#
# End of shell archive
#
exit 0