**********************************************
*
*	This is a cypher tape driver for tar
*
*	  Written by Paul Moreau 11/5/90
*
*	Rename the tapedvr.prg program to tapedvrX.prg
*	where X is a number from 0 to 6 to coinside with
*	with the Atari DMA host adapter address.  I used 2.
*
**********************************************

	magic = $54617065		* 'Tape'
	wdc = $ffff8604			* WD Controller
	port = $fffffa01		* DMA port pseudo intr
	dma = $ffff8609			* DMA control register
	flock = $43e			* Resource Lock flag location
*
*  For 'C' bindings see tapeio.h
*
	READY		= 0
	READ		= 1
	WRITE		= 2
	REWIND		= 3
	LOAD		= 4
	UNLOAD		= 5
	FILEMK		= 6
	RSENSE		= 7
	MSELECT		= 8
	MSENSE		= 9
	SPARAM		= 10
	SPACE		= 11
	ERASE		= 12
	INQUIRE		= 13

	text

auto:	bra.w	instal		* auto boot entry point - init driver
	dc.l	magic		* Magic word !
*********************************
*
*	TRAP #13 Handler
*
trap13:	move.w	(sp),d0		* trap #13 entry point
	btst	#13,d0		* super mode test ?
	bne.b	nousp		* 
	move	usp,a0		* get usp
	bra.b	dousp		*
nousp:	movea.l	sp,a0		* point to incomming arguments
	addq.l	#6,a0		*
dousp:	cmpi.w	#$ACE,(a0)	* TAPE I/O ?
	beq.b	tape_io		* Yes go to it.
do_13:	movea.l	BIOS13,a0	* get BIOS trap #13 entry address
	jmp	(a0)		* go to normal handler
	
*
*	Tape operation called for
*
tape_io:	
	move.l	$8(a0),nblock	* get and save number of blocks
	move.l	$4(a0),bufadr	* get and save buffer address to out data
	move.w	$2(a0),d0	* get and save read/write flag
	
	tst.w	d0		* < 0 
	bmi.s	error		*  if so then error
	cmp.w	#13,d0		* > 13
	bgt.s	error		*  if so then error
	lsl.w	#2,d0		* convert to long table offsets
	lea	jmptbl,a0	* get jump table base
	move.l	0(a0,d0.w),a0	* get function address from table
	jmp	(a0)		* perform fuction as requested

error:	rte			* return with error function

jmptbl:
	dc.l	t_ready		* Test Unit Ready
	dc.l	t_read		* Read tape
	dc.l	t_write		* Write tape
	dc.l	t_rewind	* Rewind tape
	dc.l	t_load		* Load tape
	dc.l	t_unload	* Unload tape
	dc.l	t_filmk		* Write File marks
	dc.l	t_rsense	* Request Sense
	dc.l	t_mselect	* Mode Select
	dc.l	t_msense	* Mode Sense
	dc.l	t_setparm	* Set vendor unique parameters
	dc.l	t_space		* Space command
	dc.l	t_erase		* Erase tape
	dc.l	t_inquire	* Inquiery

*	
* Perform a test unit ready
*
*	bios(0xACE, READY, 0L, 0l);
*	
t_ready:	
	movem.l	a1/d1,-(sp)	* save usage
	lea	cdb,a0	
	clr.b	(a0)+		*
	clr.b	(a0)+		*
	clr.b	(a0)+		*
	clr.b	(a0)+		*
	clr.b	(a0)+		*
	clr.b	(a0)		*
	move.w	#1,dmacnt	*
	move.l	#lbuf,bufadr	* get local scratch buffer
	bsr	dout		*
	movem.l	(sp)+,a1/d1	*
	rte			*
*	
* Perform a tape read
*
*	bios(0xACE, READ, (long)(BUFADR), (long)(blocks) );
*	
t_read:	
	movem.l	a1/d1,-(sp)	* save usage
	lea	cdb,a0		* Get cdb	
	move.b	#8,(a0)+	* read command
	move.b	#1,(a0)+	* Fixed bit
	move.b	nblock+1,(a0)+	* high
	move.b	nblock+2,(a0)+	* medium
	move.b	nblock+3,(a0)+	* low
	clr.b	(a0)		*
	move.w	nblock+2,d0	* get 16 bit block read count
	move.w	d0,dmacnt	* value for dma block xfer
	bsr	din		*
	movem.l	(sp)+,a1/d1	*
	rte			*
*	
* Perform a tape write
*
*	bios(0xACE, WRITE, (long)(BUFADR), (long)(blocks) );
*	
t_write:
	movem.l	a1/d1,-(sp)	* save usage
	lea	cdb,a0		* Get cdb	
	move.b	#$A,(a0)+	* write command
	move.b	#1,(a0)+	* Fixed bit
	move.b	nblock+1,(a0)+	* high
	move.b	nblock+2,(a0)+	* medium
	move.b	nblock+3,(a0)+	* low
	clr.b	(a0)		*
	move.w	nblock+2,d0	* get 16 bit block write count
	move.w	d0,dmacnt	* value for dma block xfer
	bsr	dout		*
	movem.l	(sp)+,a1/d1	*
	rte			*
	
*	
* Perform a tape rewind
*
*	bios(0xACE, REWIND, 0L, 0L );
*	
t_rewind:
	movem.l	a1/d1,-(sp)	* save usage
	lea	cdb,a0		*
	move.b	#1,(a0)+	* rewind command
	clr.b	(a0)+		* 
	clr.b	(a0)+		* 
	clr.b	(a0)+		* 
	clr.b	(a0)+		* 
	clr.b	(a0)		* 
	move.w	#1,dmacnt	*
	move.l	#lbuf,bufadr	* get local scratch buffer
	bsr	dout		*
	movem.l	(sp)+,a1/d1	*
	rte
	
*	
* Perform a tape load
*
*	bios(0xACE, LOAD, 0L, (long)(retension));
*		place 0x02 in retension param to perform a tape retension
*	
t_load:
	movem.l	a1/d1,-(sp)	* save usage
	lea	cdb,a0		*
	move.b	#$1b,(a0)+	* load/unload command
	clr.b	(a0)+		* 
	clr.b	(a0)+		* 
	clr.b	(a0)+		* 
	move.b	nblock+3,d0	* 0 or 2 for retension
	lsl.b	#1,d0		*
	and.b	#2,d0		*
	or.b	#1,d0		*
	move.b	d0,(a0)+	*
	clr.b	(a0)		* 
	move.w	#1,dmacnt	*
	move.l	#lbuf,bufadr	* get local scratch buffer
	bsr	dout		*
	movem.l	(sp)+,a1/d1	*
	rte			*

*	
* Perform a vendor unique set write/read parameters
*
*	bios(0xACE, SPARAM, 0L, (long)(params));
*		place 0x02 in retension param to perform a tape retension
*		params is a 32 bit value in which 16 are used
*		params & 0xFF00 = reconnect count (prefer 12)
*		params & 0x00FF = extended (0 or 2) 2 = extended
*	
t_setparm:
	movem.l	a1/d1,-(sp)	* save usage
	lea	cdb,a0		*
	move.b	#6,(a0)+	* Set Read/Write params (vendor unique)
	move.b	#0,(a0)+	* must be set
	move.b	#8,(a0)+	* 9 tracks
	move.b	nblock+2,(a0)+	* reconnect count
	move.b	nblock+3,(a0)+	* Extended write mode and QIC 24
	clr.b	(a0)		* 
	move.w	#1,dmacnt	*
	move.l	#lbuf,bufadr	* get local scratch buffer
	bsr	dout		*
	movem.l	(sp)+,a1/d1	*
	rte			*

*	
* Perform a Mode Select
*
*	bios(0xACE, MSELECT, *(long)(parameter_list), (long)(list_len));
*		the parameter_list is a pointer to a buffer containing a
*		4 byte header, 8 byte descriptor and 1 byte vendor block.
*		list_len (13 for full select)
*	
t_mselect:
	movem.l	a1/d1,-(sp)	* save usage
	lea	cdb,a0		*
	move.b	#$15,(a0)+	* Set Mode Select
	clr.b	(a0)+		*
	clr.b	(a0)+		*
	clr.b	(a0)+		*
	move.b	nblock+3,(a0)+	* list length
	clr.b	(a0)		* 
	move.w	#1,dmacnt	*
	bsr	dout		*
	movem.l	(sp)+,a1/d1	*
	rte			*

*	
* Perform a Mode Sense
*
*	bios(0xACE, MSENSE, *(long)(parameter_list), (long)(list_len));
*		the parameter_list is a pointer to a buffer containing a
*		4 byte header, 8 byte descriptor and 1 byte vendor block.
*		list_len (13 for full sense)
*	
t_msense:
	movem.l	a1/d1,-(sp)	* save usage
	lea	cdb,a0		*
	move.b	#$1A,(a0)+	* Set Mode Select
	clr.b	(a0)+		*
	clr.b	(a0)+		*
	clr.b	(a0)+		*
	move.b	nblock+3,(a0)+	* list length
	clr.b	(a0)		* 
	move.w	#1,dmacnt	*
	bsr	din		*
	movem.l	(sp)+,a1/d1	*
	rte			*

*	
* Perform a tape unload
*
*	bios(0xACE, UNLOAD, 0L, (long)(retension));
*	
t_unload:
	movem.l	a1/d1,-(sp)	* save usage
	lea	cdb,a0		*
	move.b	#$1b,(a0)+	* load/unload command
	clr.b	(a0)+		* 
	clr.b	(a0)+		* 
	clr.b	(a0)+		* 
	move.b	nblock+3,d0	* 0 or 2 for retension
	lsl.b	#1,d0		*
	and.b	#2,d0		*
	move.b	d0,(a0)+	*
	clr.b	(a0)		* 
	move.w	#1,dmacnt	*
	move.l	#lbuf,bufadr	* get local scratch buffer
	bsr	dout		*
	movem.l	(sp)+,a1/d1	*
	rte
*	
* Perform a write of file mark(s)
*
*	bios(0xACE, FILEMK, 0L, (long)(marks));
*	
t_filmk:
	movem.l	a1/d1,-(sp)	* save usage
	lea	cdb,a0		*
	move.b	#$10,(a0)+	* write filemark command
	clr.b	(a0)+		* 
	move.b	nblock+1,(a0)+	* high
	move.b	nblock+2,(a0)+	* medium
	move.b	nblock+3,(a0)+	* low
	clr.b	(a0)		*
	move.w	#1,dmacnt	*
	move.l	#lbuf,bufadr	* get local scratch buffer
	bsr	dout		*
	movem.l	(sp)+,a1/d1	*
	rte			*
*	
* Perform a Request Sense
*
*	bios(0xACE, RSENSE, *(long)(buffer), (long)(bytecount));
*		Supply buffer address pointer
*		and bytecount of sense data (11)
*	
t_rsense:
	movem.l	a1/d1,-(sp)	* save usage
	lea	cdb,a0		*
	move.b	#3,(a0)+	* Request sense command
	clr.b	(a0)+		* 
	clr.b	(a0)+		* 
	clr.b	(a0)+		* 
	move.b	nblock+3,(a0)+	* byte count of sense data
	clr.b	(a0)		*
	move.w	#1,dmacnt	*
	bsr	din		*
	movem.l	(sp)+,a1/d1	*
	rte			*

*	
* Perform a Space Command
*
*	bios(0xACE, SPACE, 0L, (long)(code/count));
*		code is a 2 bit field places on bits 24-25
*		count is a 24 bits field
*	
t_space:
	movem.l	a1/d1,-(sp)	* save usage
	lea	cdb,a0		*
	move.b	#$11,(a0)+	* Space command
	move.b	nblock,(a0)+	* Code field
	move.b	nblock+1,(a0)+	* 3 byte count
	move.b	nblock+2,(a0)+	*
	move.b	nblock+3,(a0)+	*
	clr.b	(a0)		*
	move.w	#1,dmacnt	*
	move.l	#lbuf,bufadr	* get local scratch buffer
	bsr	dout		*
	movem.l	(sp)+,a1/d1	*
	rte			*

*	
* Perform a Tape Erase
*
*	bios(0xACE, ERASE, 0L, 0L);
*	
t_erase:
	movem.l	a1/d1,-(sp)	* save usage
	lea	cdb,a0		*
	move.b	#$19,(a0)+	* Erase command
	move.b	#1,(a0)+	* Long bit
	clr.b	(a0)+		*
	clr.b	(a0)+		*
	clr.b	(a0)+		*
	clr.b	(a0)		*
	move.w	#1,dmacnt	*
	move.l	#lbuf,bufadr	* get local scratch buffer
	bsr	dout		*
	movem.l	(sp)+,a1/d1	*
	rte			*

*	
* Perform an Inquiry
*
*	bios(0xACE, INQUIRE, *(long)(buffer), (long)(bytecount));
*		Supply buffer address pointer
*		and bytecount of sense data (11)
*	
t_inquire:
	movem.l	a1/d1,-(sp)	* save usage
	lea	cdb,a0		*
	move.b	#$12,(a0)+	* Inquiry command
	clr.b	(a0)+		* 
	clr.b	(a0)+		* 
	clr.b	(a0)+		* 
	move.b	nblock+3,(a0)+	* byte count of sense data
	clr.b	(a0)		*
	move.w	#1,dmacnt	*
	bsr	din		*
	movem.l	(sp)+,a1/d1	*
	rte			*

*************************************************************************
*
* Issue command and RECIEVE data from SCSI device
*
din:	move.l	#wdc,a0		*
	bsr.w	sendcdb		* setup DMA and send DCB to controller
	bmi.w	tout		* controller/DMA time-out
	move.w	#$190,2(a0)	*
	nop			*
	nop			*
	move.w	#$90,2(a0)	*
	nop			*
	nop			*
	move.w	dmacnt,(a0)	*
	clr.l	d1		*
	move.w	#$8A,2(a0)	*
	move.b	(a1),d1		* Get last byte of CDB
	swap	d1		*
	move.l	d1,(a0)		* last byte of CDB (initiate)
	move.w	#$8A,d1		*
	bra.b	exec		*
*
*  Issue command and SEND data to SCSI device
*
dout:	move.l	#wdc,a0		*
	bsr.w	sendcdb		* setup DMA and send DCB to controller
	move.w	#$90,2(a0)	*
	nop			*
	nop			*
	move.w	#$190,2(a0)	*
	nop			*
	nop			*
	move.w	dmacnt,(a0)	* DMA block count setup
	clr.l	d1		*
	move.w	#$18A,2(a0)	*
	move.b	(a1),d1		* Get last byte of CDB
	swap	d1		*
	move.w	#$100,d1	*
	move.l	d1,(a0)		* last byte of CDB (initiate)
	move.w	#$18A,d1	*

exec:	bsr.w	wait		* go wait for complete intr.
	bmi.b	tout		* loop timed out --->
	move.w	d1,2(a0)	*
	nop			*
	move.w	(a0),d0		* get HDC/DMA status in d0
	and.w	#$FF,d0		* Strip all but HDC bits
	bra.s	ndto		*

tout:	move.w	#-2,d0		* Device time-out

ndto:	move.w	#$80,2(a0)	*
	nop			*
	tst.w	(a0)		*
	clr.w	flock		* clear Floppy VBI
	rts			*

*
* Set parameters (Set DMA, and send first 5 bytes of DCB)
*
sendcdb:
	st	flock		* Set flock
	move.b	bufadr+3,dma+4	* Set DMA buffer address LOW
	move.b	bufadr+2,dma+2	* Set DMA buffer address MED
	move.b	bufadr+1,dma	* Set DMA buffer address HIGH
	move.w	#$88,2(a0)	* Set HDC access, (Device Select ?)

	lea	cdb,a1		* Get Base address of CDB
	clr.l	d0		*
	move.b	acsi,d0		* Place DEVICE ID in upper 3 bits of cmd
	and.b	#$e0,d0		* make sure only top 3 bits
	or.b	(a1)+,d0	* and put 1st byte of CDB in also
	swap	d0		*
	move.w	#$8A,d0		* put in ST's needed direction and addr
	move.l	d0,(a0)		* DCB byte #0 (Command + Device Number)

	move.w	#3,d1		* all but last byte but last byte of CDB
	move.w	#$8A,2(a0)	* Issue first byte (not included in CDBSIZ)
	bsr	swait		*
	bmi	parmo		*

scdbl:	clr.l	d0		* Now send all but last byte ....
	move.b	(a1)+,d0	* Get Next Byte
	swap	d0		*
	move.w	#$8A,d0		*
	move.l	d0,(a0)		* Send Next DCB byte
	bsr.w	swait		* wait for intr
	bmi.b	parmo		* Timeout exit
	dbf	d1,scdbl	*
parmo:	rts			*

*
* Wait for Command Complete
*
wait:	move.l	#$2000000,d0	* init long wait (aprox 1 min)
	bra.b	waitl		* goto wait loop
swait:	move.l	#$100000,d0	* init short wait (aprox 10 secs)
waitl:	subq.l	#1,d0		* wait loop - decrement time-out counter
	bmi.b	waito		* rollover means time-out!
	btst	#$5,port	* interrupt yet (pseudo parallel port bit 5)
	bne.b	waitl		* not yet , keep waiting till intr or time-out
	clr.l	d0		* clear CPU's N flag = interrupt received.
waito:	rts			* return to caller.

*
*	DATA
*
BIOS13:	dc.l	0
nblock:	dc.l	0
bufadr:	dc.l	0
dmacnt:	dc.w	0
acsi:	dc.w	$4000		* Default to device 2 
cdb:	dc.b	0,0,0,0,0,0
*
* Local 512 byte junk DMA buffer
*
lbuf:	dc.l	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.l	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.l	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.l	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.l	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.l	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.l	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.l	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

*
*    End of Resident trap code
*
*********************************
*
*	START-UP
*
instal:	movea.l	#stack,sp	* set stack pointer
	clr.l	-(sp)		* 
	move.w	#$20,-(sp)	* Supervisor mode
	trap	#$1		*
	addq.l	#6,sp		*
	move.l	d0,stkp		*
	move.l	$B4,a0		* is the driver already resident ?
	cmp.l	#magic,-4(a0)	* Check the magic word ....
	beq	already		* yes - exit with message
	move.l	#dta,-(sp)	* set dta
	move.w	#$1a,-(sp)	*
	trap	#1		*
	addq.l	#6,sp		*
	clr.w	-(sp)		* Fsfirst in local directory
	move.l	#dvrnl,-(sp)	* Search local directory first incase
	move.w	#$4e,-(sp)	*
	trap	#1		*
	addq.l	#8,sp		*
	tst.w	d0		* did we find the driver ?
	beq.s	found		*
	clr.w	-(sp)		* Fsfirst in auto folder
	move.l	#dvrna,-(sp)	* Search auto folder for boot search
	move.w	#$4e,-(sp)	*
	trap	#1		*
	addq.l	#8,sp		*
	tst.w	d0		* did we find the driver ?
	bne	nfound		*
found:	move.b	dta+37,d0	* get the last leter of name (before ext)
	sub.b	#'0',d0		* change from ascii to binary value
	bmi	ivdid		* < 0 invalid ID
	cmp.b	#6,d0		* > 6 invalid ID
	bhi	ivdid		*
	lsl.b	#5,d0		* move bits to proper location
	move.b	d0,acsi		* and save for driver
	move.l	$B4,BIOS13	* save trap #13 vector
	move.l	#trap13,$B4	* install our own
	move.l	#greet,-(sp)	* print out greeting
	move.w	#$9,-(sp)	*
	trap	#$1		*
	addq.l	#6,sp		*
	clr.w	d0		*
	move.b	dta+37,d0	* put device number out
	move.w	d0,-(sp)	*
	move.w	#2,-(sp)	*
	trap	#1		*
	addq.l	#4,sp		*
	move.l	#grex,-(sp)	* finnish printing out greeting
	move.w	#$9,-(sp)	*
	trap	#$1		*
	addq.l	#6,sp		*
	move.l	stkp,-(sp)	* back to user mode
	move.w	#$20,-(sp)	*
	trap	#$1		*
	addq.l	#6,sp		*
	clr.w	-(sp)		* no error on term
	move.l	#instal,d0	* calculate driver code size
	sub.l	#auto,d0	* auto entry point
	add.l	#$200,d0	* add chicken factor
	move.l	d0,-(sp)	* keep process memory resident
	move.w	#$31,-(sp)	*
	trap	#$1		* return to auto boot

nfound:
	move.l	#nfm,-(sp)	* not found message
	bra.s	pae		* go Print message And Exit
ivdid:
	move.l	#ividm,-(sp)	* Ivalid Device ID Message
	bra.s	pae		*
already:
	move.l	#ari,-(sp)	* print out already installed message

pae:	move.w	#$9,-(sp)	*
	trap	#$1		*
	addq.l	#6,sp		*
	move.l	stkp,-(sp)	* back to user mode with usp
	move.w	#$20,-(sp)	*
	trap	#$1		*
	addq.l	#6,sp		*
	clr.w	-(sp)		* and terminate
	trap	#1		*

*
*	Pseudo Data segment
*
greet:	dc.b	'*** Streaming Tape Driver Ver 1.1 Installed for ID#',0
grex:	dc.b	' ***',7,13,10,0
ari:	dc.b	'*** Streaming Tape Driver is already resident ***',13,10,0	
ividm:	dc.b	'*** Invalid Device ID in file name Install ABORTED ***',13,10,0
nfm:	dc.b	'*** Driver could not locate itself Install ABORTED ***',13,10,0

	even
	
	dc.l	0
	ds.l	62
stack:	dc.l	0
stkp:	dc.l	0
dta:	ds.b	44
dvrnl:	dc.b	'TAPEDVR?.PRG', 0
dvrna:	dc.b	'\AUTO\TAPEDVR?.PRG', 0
	end
