 page	64,132
 title	MSULTRA - DMS virtual disk driver for 100K, 360K, 740K and 870K
;========================================================================
;=			CHANGE HISTORY					=
;=----------------------------------------------------------------------=
;= 1.0 02/15/84 - change version number for release			=
;= a  02/06/84	- parameterize to generate 100, 360, 740, 870 K versions=
;========================================================================
;= This is an installable device driver for a memory diskette (virtual) =
;= with either a 100K, 360K, 740K, or 870K capacity, depending on the   =
;= compile flags IS100K, IS360K, IS740K, or IS870K.			=
;========================================================================
cseg	segment para public 'code'
;
;	DRIVER SIZE SWITCH is in msultra.inc
;		IS100K	- 100 K drive
;		IS360K	- 360 K drive
;		IS740K	- 740 K drive
;		IS870K	- 870 K drive
;
include	msultra.inc
;
;	E Q U A T E S
;
;read/write
;
srh		equ	0	;static request header
srh_len		equ	13	;length
srh_len_fld	equ	srh	;length field
srh_ucd_fld	equ	srh+1	;unit code field
srh_ccd_fld	equ	srh+2	;command code field
srh_sta_fld	equ	srh+3	;status field
srh_res_fld	equ	srh+5	;reserved area field
;
md		equ	srh+srh_len	;media desciptor byte
md_len		equ	1		;length
dta		equ	md+md_len	;disk transfer address
dta_len		equ	4		;length
count		equ	dta+dta_len	;byte/sector count
count_len	equ	2		;length
ssn		equ	count+count_len	;start sector no.
ssn_len		equ	2		;length
;
; media check
;
ret_byte	equ	md+md_len	;byte returned from driver
;
; build bpb
;
bpba_ptr	equ	dta+dta_len	;ptr to bpb
bpba_ptr_len	equ	4		;length
;
; init
;
units		equ	srh+srh_len
units_len	equ	1
br_addr_0	equ	units+units_len
br_addr_1	equ	br_addr_0+2
br_addr_len	equ	4
bpb_ptr_off	equ	br_addr_0+br_addr_len
bpb_ptr_seg	equ	bpb_ptr_off+2
;
; F O R    B P B    D E S C R I P T I O N	
;
sect_size	equ	512
;
IFDEF IS100K
sect_alloc	equ	1
num_dirs	equ	64
dir_size	equ	4
sect_FAT	equ	1
sect_total	equ	200
sect_track	equ	8
media		equ	0FEh
ENDIF
;
IFDEF IS360K
sect_alloc	equ	2
num_dirs	equ	112
dir_size	equ	7
sect_FAT	equ	2
sect_total	equ	720
sect_track	equ	9
media		equ	0FDh
ENDIF
;
IFDEF IS740K
sect_alloc	equ	2
num_dirs	equ	112
dir_size	equ	7
sect_FAT	equ	3
sect_total	equ	1480
sect_track	equ	9
media		equ	0FDh
ENDIF
;
IFDEF	IS870K
sect_alloc	equ	2
num_dirs	equ	112
dir_size	equ	7
sect_FAT	equ	3
sect_total	equ	1740
sect_track	equ	9
media		equ	0FDh
ENDIF
;
total_para	equ	(sect_size / 16) * sect_total
;
vdsk		proc	far
	assume	cs:cseg,es:cseg,ds:cseg
begin:
start		equ	$
;
;	S P E C I A L  D E V I C E  H E A D E R
;
next_dev	dd	-1		;ptr to next device
attribute	dw	2000h		;block device (non-IBM format)
strategy	dw	dev_strategy	;ptr to device strategy
interrupt	dw	dev_int		;ptr to device int handler
dev_name	db	1		;no. of block devices
		db	7 dup(?)	;7 bytes of filler
rh_off		dw	?		;request header offset
rh_seg		dw	?		;request header segment
;
; current virtual disk information
;
total		dw	?	;total sectors to transfer
verify		db	0	;verify 1=yes, 0=no
start_sec	dw	0	;start sector no.
vdisk_ptr	dw	0	;start sector of virtual disk
user_dta	dd	?	;ptr to callers dsk transfer address
boot_rec	equ	$	;dummy DOS boot record
		db	3 dup(0)	;3 byte jump to boot (not bootable)
		db	'DMS 1.0 '	;vendor ID
;
;	BIOS Parameter Block
;
bpb		equ	$
		dw	sect_size
		db	sect_alloc
		dw	1		;1 reserved sector
		db	2		;2 FATS
		dw	num_dirs
		dw	sect_total
		db	media
		dw	sect_FAT
;
bpb_ptr		dw	bpb		;BIOS parameter block ptr array
;						(1 entry)
;
; function table
;
funtab		label	byte
		dw	init	;initialisation
		dw	media_check	;media check (block only)
		dw	build_bpb	;build bpb
		dw	ioctl_in	;ioctl input
		dw	input		;read
		dw	nd_input	;non-destructive read, no wait (char only)
		dw	in_stat		;read status
		dw	in_flush	;read flush
		dw	output		;write
		dw	out_verify	;write with verify
		dw	out_stat	;write status
		dw	out_flush	;write flush
		dw	ioctl_out	;ioctl write
;
;	L O C A L  P R O C E D U R E S
;
in_save		proc	near
		mov	ax,es:word ptr dta[bx]	;save callers dta
		mov	cs:word ptr user_dta,ax
		mov	ax,es:word ptr dta+2[bx]
		mov	cs:word ptr user_dta+2,ax
		mov	ax,es:word ptr count[bx] ;get no. of sectors to read
		xor	ah,ah
		mov	cs:total,ax		;move no. of sectors to total
		ret
in_save	endp
;
calc_addr	proc	near
		mov	ax,cs:start_sec	;get start sector no.
		mov	cx,20h		;mov 512 to cx segment style
		mul	cx		;multiply to get actual sector
		mov	dx,cs:vdisk_ptr	;get segment of virtual disk
		add	dx,ax		;add that segment to initial segment
		mov	ds,dx		;save that as the actual segment
		xor	si,si	;	it's on a paragraph boundary
		mov	ax,cs:total	;total no. of sectors to read
		mov	cx,512		;bytes/sector
		mul	cx		;compute length
		or	ax,ax		;check for > 64K
		jnz	move_it
		mov	ax,0ffffh	;length for 64K
move_it:
		xchg	cx,ax		;get length to cx
		ret
calc_addr	endp
;
sector_read	proc	near
		call	calc_addr		  ;calculate start sector
		mov	es,cs:word ptr user_dta+2 ;set destination (es:di) to
		mov	di,cs:word ptr user_dta	  ;  point to callers dta
;
; check for dta wrap in case we came through via verify
;
		mov	ax,di		;get dta offset
		add	ax,cx		;add copy length to it
		jnc	read_copy	;carry flag=0 if no wrap
		mov	ax,0ffffh	;max length
		sub	ax,di		;take dta offset from max
		mov	cx,ax		;use that as copy length to avoid wrap
read_copy:
		rep	movsb		;do the read
		ret
sector_read	endp
;
sector_write	proc	near
		call	calc_addr	;calculate start sector
		push	ds
		pop	es
		mov	di,si			  ;es:di points to disk
		mov	ds,cs:word ptr user_dta+2 ;ds:si points to callers dta
		mov	si,cs:word ptr user_dta
;
; check for dta wrap
;
		mov	ax,si		;get data offset
		add	ax,cx		;add copy length
		jnc	write_copy	;wrap?
		mov	ax,0ffffh	;yes, get max
		sub	ax,si		;less offset
		mov	cx,ax		;use as new copy length to avoid wrap
write_copy:
		rep	movsb		;do the write
		ret
sector_write	endp
;
;	D E V I C E  S T R A T E G Y
;
dev_strategy:
		mov	cs:rh_seg,es	;save seg of request header ptr
		mov	cs:rh_off,bx	;save offset    "      "     "
		ret
;
;	D E V I C E  I N T E R R U P T  H A N D L E R
;
dev_int:	;preserve machine state on entry
		cld
		push	ds
		push	es
		push	ax
		push	bx
		push	cx
		push	dx
		push	di
		push	si
;
; branch according to the function passed
;
		mov	al,es:[bx]+2	;get function byte
		rol	al,1		;get table offset
		lea	di,funtab	;function table address
		xor	ah,ah
		add	di,ax		;index into it
		jmp	word ptr[di]	;computed goto
;
; init
;
init:
		push	cs
		pop	dx		;current cs to dx
		lea	ax,cs:vdisk	;get address of current disk
		mov	cl,1
		ror	ax,cl		;divide by 16
		add	dx,ax		;add to current cs
		mov	cs:vdisk_ptr,dx	;save as start segment of virtual disk
		mov	ax,total_para	;add enough paragraphs to start of
		add	dx,ax		;    virtual disk
		mov	es:word ptr br_addr_0[bx],0
		mov	es:br_addr_1[bx],dx	;make that break address
		mov	es:byte ptr units[bx],1	;no. of diskette units
		lea	dx,bpb_ptr		;get address of bpb ptr array
		mov	es:bpb_ptr_off[bx],dx	;save offset in data packet
		mov	es:bpb_ptr_seg[bx],cs	;save segment in data packet
		mov	es,cs:vdisk_ptr		;get start sector of vdisk
		xor	di,di			;zero out boot record
		lea	si,boot_rec		;address of boot record
		mov	cx,24
		rep	movsb			;copy 24 bytes of boot record
		mov	cs:word ptr start_sec,1
		mov	cs:word ptr total,sect_FAT
		call	calc_addr		;address of logical sector 1
		push	ds
		pop	es
		mov	di,si			;move that address to es:di
		xor	al,al
		rep	stosb			;zero out FAT
		mov	ds:byte ptr [si],media	;set first FAT entry
		mov	ds:byte ptr 1[si],0ffh
		mov	ds:byte ptr 2[si],0ffh
		push	ds			;save ptr to FAT
		push	si
		mov	cs:word ptr start_sec,3
		mov	cs:word ptr total,sect_FAT
		call	calc_addr		;calculate address of l.s. 3
		push	ds
		pop	es
		mov	di,si			;move that address to es:di
		pop	si
		pop	ds			;restore address to first FAT
		rep	movsb			;copy first FAT to second
		mov	cs:word ptr start_sec,1+sect_FAT
		mov	cs:word ptr total,dir_size
		call	calc_addr		;address of ls 5 (start of dir)
		xor	al,al
		push	ds
		pop	es			;set up es:di to point to it
		xor	di,di
		rep	stosb			;zero directory
		mov	es,cs:rh_seg		;restore es:bx to request header
		mov	bx,cs:rh_off
		or	es:word ptr srh_sta_fld[bx],0100h
		or 	es:word ptr srh_sta_fld[bx],0
		jmp	exit
;
; media check
;
media_check:		;block only
		mov	es:byte ptr ret_byte[bx],1	;set media not changed
		or	es:word ptr srh_sta_fld[bx],0100h
		or 	es:word ptr srh_sta_fld[bx],0
		jmp	exit
;
; build BIOS parameter block
;
build_bpb:
		push	es			;save srh segment
		push	bx			;save srh offset
		mov	cs:word ptr start_sec,0
		mov	cs:word ptr total,1
		call	calc_addr		;address of first sector
		push	cs
		pop	es
		lea	di,bpb			;address of BIOS param block
		add	si,11
		mov	cx,13			;length of bpb
		rep	movsb
		pop	bx			;restore offset of srh
		pop	es			;restore seg of srh
	
		lea	dx,bpb			;get bpb array ptr
		mov	es:bpba_ptr[bx],dx	;save ptr tp bpb table
		mov	es:bpba_ptr+2[bx],cs
		mov	es:dta[bx],dx		;offset of sector buffer
		mov	es:dta+2[bx],cs
		or	es:word ptr srh_sta_fld[bx],0100h
		or 	es:word ptr srh_sta_fld[bx],0
		jmp	exit
;
; the following entries are not supported for this device
;
ioctl_in:
ioctl_out:
nd_input:
in_stat:
in_flush:
out_stat:
out_flush:
;
; disk read
;
input:
		call	in_save			;save parameters
		mov	ax,es:word ptr ssn[bx]	;set start sector
		mov	cs:start_sec,ax		;save it
		mov	ax,es:word ptr count[bx]
		mov	cs:total,ax		;save sectors to transfer
		call	sector_read		;get that many
		mov	bx,cs:rh_off		;restore es:bx as request header ptr
		mov	es,cs:rh_seg	
		or	es:word ptr srh_sta_fld[bx],0100h
		or 	es:word ptr srh_sta_fld[bx],0
		jmp	exit
;
; disk write
;
output:
		call	in_save
		mov	ax,es:word ptr ssn[bx]	;get start sector
		mov	cs:start_sec,ax
		mov	ax,es:word ptr count[bx]
		mov	cs:total,ax		;save total sectors to write
		call	sector_write		;write those sectors
		mov	bx,cs:rh_off		;restore request header ptr
		mov	es,cs:rh_seg
		cmp	cs:byte ptr verify,0	;verify write?
		jz	no_verify
		mov	cs:byte ptr verify,0	;yes, reset indicator
		jmp	input			;read those sectors back
no_verify:
		or	es:word ptr srh_sta_fld[bx],0100h
		or 	es:word ptr srh_sta_fld[bx],0
		jmp	exit
out_verify:		;write with verify
		mov	cs:byte ptr verify,1	;set verify flag
		jmp	output	;do write
;
; common exit
;
exit:
	pop	si
	pop	di
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	pop	es
	pop	ds
	ret
e_o_p:
;
; macro to align virtual disk on a paragraph boundary
;
if ($-start) mod 16
org	($-start)+16-(($-start) mod 16)
endif
vdisk	equ	$
vdsk	endp
cseg	ends
	end	begin
