.Title	THRASH - for trying out the queuing DU handler
.Nlist	bex
.Enabl	gbl
			.if eq 1
******************************************************************************
*		Requires linking with CVLLIB.OBJ			     *
******************************************************************************
			.endc
				.psect mesage con
vermes:	.ascii	/Thrash V4 1-Jan-89/<0>
				.psect

			.if eq 1
THRASH is designed for thrashing disc devices, especially using handlers
capable of internal queueing.  It was written to thrash the DU handler
written for Webster Computer Corporation, Melbourne, Australia.

It performs reads only, reading but one word per request, in any of the
patterns RANDOM, BUTTERFLY, SEQUENTIAL, and DECREASING SEQUENTIAL.

V1	25-Nov-84	Chester Wilson, Charleville, Australia 4470.
V2	15-Aug-85	Add file lookup.
V3	27-Sep-85	Search for unused completion routine rather than
				using next blindly.  Add START & END.
V3a	14-Jun-86	bug fix
V3b	22-Oct-88	Allow any block numbers if SIZE=0s
V4	 1-Jan-88	Tidy error reporting: if an error is found, ignore
				the rest of the queued blocks as they will
				all have been returned in error.
			.endc


.mcall	.qset,.readc,.gtim,.lookup,.gtlin,.ttinr,.spfun,.writw,.enter,.wait
.mcall	.spnd,.rsum,.cstat,.print,.csigen,.ttyin,.sreset,.exit,.rctrlo,.fetch

jsw=44
gtlin$=10
tcbit$=100
ttspc$=10000
ttlc$=40000

userrb=53
error$=4
fatal$=10

cr=15
lf=12
.Sbttl	Macros

.macro	type	arg
	.save
	.psect	mesage	con
	$$$=.
	.ascii	arg
	.restore
	mov	r0,-(sp)
	mov	#$$$,r0
	emt	^o351
	mov	(sp)+,r0
.endm	type


.macro	tx	arg
	.save
	.psect	mesage	con
	$$$=.
	.ascii	arg
	.restore
.endm	tx


.macro	movs	string,loc
	.save
	.psect	mesage	con
	$$$=.
	.ascii	string
	.restore
	mov	#$$$,loc
.endm	movs


.macro	ref	string
	.save
	.psect	mesage	con
	$$$=.
	.ascii	string
	.restore
	.word	$$$
.endm	ref

.macro	gtlin	prompt,buffer
	.save
	.psect	mesage	con
	$$$=.
	.ascii	prompt
	.restore
	.gtlin	buffer,#$$$
.endm	gtlin
.Sbttl	Data Locations

lo:	0		;Locations used in the routines for getting the
hi:	0		; next block.
blkflg:	0
size:	0		;For the device size in blocks.
blksub:	0		;Gets address of routine for getting next block
passes:	0		;Current pass count
pasmax:	0		;Number of passes requested by the user
tecnic:	0		;Stores type of technique used for thrashing

offset:	0		;Starting block if offsets used.

.iif ndf maxq$, maxq$=32. ;Number of queued requests allowed for.
.iif gt maxq$ - <35.>, .error
			;35 is the number of completion routines built in
			; in THRASH.

qarea:	.blkw	10.*<maxq$+2>	;Queue area required by RT but not by TSX
				; (TSX uses MAXQ to limit number of requests)
area:	.blkw	7	;General EMT area

queued:	0		;Number of requests currently queued.

crtab:			;Table of completion routine addresses.
	.word	cr1,cr2,cr3,cr4,cr5
	.word	cr6,cr7,cr8,cr9,cr10
	.word	cr11,cr12,cr13,cr14,cr15
	.word	cr16,cr17,cr18,cr19,cr20
	.word	cr21,cr22,cr23,cr24,cr25
	.word	cr26,cr27,cr28,cr29,cr30
	.word	cr31,cr32,cr33,cr34,cr35

blktab:	.blkw	35.	;Table of block numbers of current requests
buffer:	.blkw	35.	;Area for reading information into.
used:	.blkw	35.	;Zero if this completion routine/block no unused.

listit:0
tty:	.rad50	/tt          /
defext:	.rad50	/dsk         /

maxq:	0		;Number of queue elements in use.

outcnt:	0		;For line formatting
tblk:	0		;TT output block number
tbuff:	.blkw	100.	;For terminal output

logtab:	.blkw	200.	;To record blocks read.
logend:			;End of buffer
logrep:	0		;Pointer for blocks queued
logred:	0		;Pointer for blocks actually read

limit:	.limit
himem:	0
.Sbttl	Initialization & Setting up User Requirements

.Enabl	lsb

Badrun::
	bisb	#error$,@#userrb
Start::
	.sreset
	bisb	#ttlc$,@#jsw
	bicb	#gtlin$,@#jsw

	.rctrlo

	.csigen	limit+2,#defext
	mov	r0,himem
	bisb	#gtlin$,@#jsw

	.wait	#3
	bcc	10$
	.print	#vermes
	br	start
10$:
	.cstat	#area,#3,#tbuff		;Try for size from CSTAT in case file.
	mov	tbuff+4,size
	bne	20$

	.spfun	#area,#3,#373,#size,#0,#0 ;Get its size with SPFUN if device.
	bcc	20$
	type	<#?Thrash-E-Cannot obtain device size#<0>>
	br	badrun
20$:
	.fetch	himem,#tty
	mov	r0,himem
	bcc	25$
	type	<#?Thrash-F-Cannot fetch handler for TT:#<0>>
	br	die
25$:
	.enter	#area,#1,#tty		;Open a terminal channel
	bcc	30$
	type	<#?Thrash-F-Cannot open device TT:#<0>>
die:	bisb	#fatal$,@#userrb
	.exit
30$:

;***************************************************************************
;	Number of active queue elements allowed [32]:
;***************************************************************************

.save
.psect	mesage	con

nq:	.ascii	/Number of active queue elements allowed [/
	.byte	<maxq$/10.+'0>
	.byte	<maxq$ - <<maxq$/10.>*10.> + '0>
	.ascii	/]: /<200>

mq:	.ascii	/?Thrash-E-Number must be from 1 to /
	.byte	<maxq$/10.+'0>
	.byte	<maxq$ - <<maxq$/10.>*10.> + '0>
	.byte	0
.restore

	clr	queued
	.gtlin	#tbuff,#nq
	mov	#tbuff,r1
	call	rd
	tst	r0
	beq	40$
	cmp	r0,#maxq$
	blos	50$

	.rctrlo
	.print	#mq
	bisb	#error$,@#userrb
	br	30$
40$:
	mov	#maxq$,r0		;Default is maxq$
50$:	add	#2,r0			;Add 2 for terminal & spare
	mov	r0,maxq
	.qset	#qarea,maxq
	sub	#2,maxq

;***************************************************************************
;	Do you want a list of the blocks [N]?
;***************************************************************************

	mov	#-1,listit
	gtlin	</Do you want a list of the blocks [Y]? /<200>>,#tbuff
	movb	tbuff,r0
	bic	#40,r0
	cmp	r0,#'N
	beq	60$
	clr	listit
60$:

;***************************************************************************
;	Number of passes (0 for continuous) [0]:
;***************************************************************************

	mov	#tbuff,r1
	gtlin	</Number of passes (0 for continuous) [0]: /<200>>,r1
	call	rd
	mov	r0,pasmax
	clr	passes
70$:

;***************************************************************************
;	nnnnn blocks available.
;	Start [0]:
;	End [nnn]:
;***************************************************************************

	call	tcrlf
	mov	size,r3
	clr	r2
	call	wd2t
	type	</ blocks available./<0>>

	mov	#tbuff,r1
	gtlin	</Start [0]: /<200>>,r1
	call	rd
	tst	size
	beq	80$
	cmp	r0,size
	blo	80$
	  type	</?Thrash-E-You can't start past the end!/<0>>
	  bisb	#error$,@#userrb
	  br	70$
80$:
	mov	r0,offset

	mov	#tloub,r1		;Set up buffer with END message
	movs	</End [/<0>>,r2		; containing actual size.
90$:	movb	(r2)+,(r1)+
	bne	90$
	dec	r1
	mov	size,r3
	dec	r3
	clr	r2
	call	wd2
	movs	</]: /<200>>,r2
100$:	movb	(r2)+,(r1)+
	bpl	100$

	mov	#tbuff,r1
	.gtlin	r1,#tloub
	call	rd
	tst	r0
	beq	120$
	cmp	r0,offset
	bhis	110$
	  type	</?Thash-E-You can't end before the start!/<0>>
	  bisb	#error$,@#userrb
	  br	70$
110$:	tst	size
	beq	120$
	cmp	r0,size
	blos	130$
	  type	</?Thrash-E-You mustn't read past the end of the device!/<0>>
	  bisb	#error$,@#userrb
	  br	70$
120$:	mov	size,r0
	dec	r0
130$:	mov	r0,size
	sub	offset,size
	inc	size			;One past last block number.

;***************************************************************************
;	Random, Butterfly, Sequential, or Decreasing Sequential [B]?
;***************************************************************************

140$:
	gtlin	</Random, Butterfly, Sequential, or Decreasing Sequential [B]? /<200>>,#tbuff
	movb	tbuff,r1
	bne	150$
	movb	#'B,r1
150$:	bic	#40,r1			;(upper case)
	movb	r1,tecnic		;Remember technique required
xxrun:
	tst	size			;If size = 0, make it appear 65535
	bne	155$
	dec	size
155$:
	cmp	r1,#'R
	beq	xxran
	cmp	r1,#'B
	beq	xxbut
	cmp	r1,#'S
	beq	xxseq
	cmp	r1,#'D
	beq	xxdsq
	type	</?Thrash-E-Please specify the first letter/<0>>
	bisb	#error$,@#userrb
	br	140$
xxran:					;Wants RANDOM blocks read
	mov	#blkran,blksub
	mov	size,blkflg
	inc	blkflg
	.gtim	#area,#tbuff
	mov	tbuff,ran$nm		;Prime the random number generator
	br	160$
xxbut:					;Wants BUTTERFLY sequence
	mov	#blkbut,blksub
	clr	blkflg
	clr	lo
	mov	size,hi
	dec	hi
	br	160$
xxseq:					;Wants SEQUENTIAL reading
	mov	#blkseq,blksub
	clr	lo
	br	160$
xxdsq:					;Wants DECREASING SEQUENTIAL
	mov	#blkdsq,blksub
	mov	size,hi
	dec	hi
160$:
	clr	tblk			;TT output block count
	clr	outcnt			;Counter of items on line
	mov	#logtab,logrep		;Pointer to next block to report
	mov	#logtab,logred		;Pointer to next slot for read block

	mov	#used,r0
	mov	#35.,r1
170$:	clr	(r0)+			;Clear USED words
	dec	r1
	bne	170$

.Dsabl	lsb
.Sbttl	Block Reading & Reporting Loop

;You may well wonder why the block reporting was done using a table of
; returned values rather than output from the completion routines.
; This was most interesting:
;	If output was done from the completion routines using .PRINT
;		the output from one completion routine could be mixed
;		within the output from the main routine
;	If output was done from the completion routines using .WRITW
;		the monitor screwed up its list of queue elements
;		(see THRASH.QRY) and hung.  This was not tested under
;		TSX.  The list of queues was actually fragmented into
;		three queues, with only one active for some time
;		thereby ruining the idea of internal queuing in the DU
;		handler!

.Enabl	lsb

Bloop:
	clr	r5			;Pointer within completion/block/used
10$:					; tables.
	call	report			;Report any processed since last time.

; **** Get next block and check for end of pass ****

20$:
	call	@blksub			;Get next block
	bcc	50$			;Not end of pass
	.wait	#3
	.wait	#1
	call	report			;Report any we may have missed.
	type	<<cr><lf>/End of Pass /<200>>
	inc	passes
	mov	passes,r0
	call	wdt
	call	tcrlf
	mov	tecnic,r1
	tst	pasmax
	beq	30$			;If no limit, continue
	cmp	passes,pasmax
	bhis	40$
30$:	  jmp	xxrun
40$:
	type	<<cr><lf>/End of Run/<0>>
	jmp	start

; **** Queue this block for reading ****

50$:
	cmp	queued,maxq		;See if room for more
	blo	60$
	mov	r0,-(sp)
	.spnd				;Have a blow.
	mov	(sp)+,r0
	br	50$
60$:
	tst	used(r5)
	beq	70$
	add	#2,r5
	cmp	r5,#35.*2
	blo	60$
	clr	r5
	br	60$
70$:
	inc	used(r5)
	mov	r0,blktab(r5)
	cmpb	tecnic,#'R		;If random, need to report blocks
	bne	80$			; as they are queued as well as on
	mov	#'Q,r3			; return.
	call	tell
80$:
	mov	#2,r3			;Allow 2 tries if error, as channel
	mov	#buffer,r4		; gets fouled up if get one.
	add	r5,r4
90$:	.readc	#area,#3,r4,#1,crtab(r5),blktab(r5)
	bcc	100$
	dec	r3
	bne	90$
	type	<<cr><lf>#?Thrash-E-Input Error on .READC - block #<200>>
	bisb	#error$,@#userrb
	mov	blktab(r5),r3
	clr	r2
	call	wd2t
	call	tcrlf
100$:
	inc	queued			;Increment count of queued requests
	jmp	10$

.Dsabl	lsb
.Sbttl	Report - Report any blocks read since last queuing request

.Enabl	lsb

Report:
	cmp	logrep,logred
	bne	5$		;No more blocks to report
	  return
5$:	clr	r3
	cmp	tecnic,#'R	;If RANDOM, reporting queued as well.
	bne	10$
	mov	#'R,r3
10$:	mov	@logrep,r0	;Block number
	add	#2,logrep
	mov	@logrep,r1	;Status word
	add	#2,logrep
	call	tell
	bic	#^c1,r1		;Check error bit
	beq	30$		;OK
	mov	r0,r3		;Error - tell him block number.
	.wait	#3
	.wait	#1
	bisb	#error$,@#userrb
	.rctrlo
	type	<<cr><lf>/Error Reading Block: /<200>>
	clr	r2
	call	wd2pt
	type	<<cr><lf>/Type <return> to continue /<200>>
20$:	.ttyin
	cmpb	r0,#lf
	bne	20$
	mov	#logtab,logred	;Ignore other queued blocks.
	br	40$
30$:
	cmp	logrep,#logend
	blo	report
40$:	mov	#logtab,logrep
	br	report

.Dsabl	lsb
.Sbttl	Block Getting Routines

blkran:				;RANDOM blocks
	dec	blkflg
	beq	endpass		;(blkflg used as a counter)
	mov	size,r0
	call	iran		;Random number routine from CVLLIB
	br	notend


blkbut:				;BUTTERFLY pattern
	inc	blkflg
	bit	#1,blkflg
	beq	10$
	mov	lo,r0
	cmp	r0,size
	bhis	endpass
	inc	lo
	br	notend
10$:
	mov	hi,r0
	cmp	r0,#-1
	beq	endpass
	dec	hi
	br	notend

blkseq:				;SEQUENTIAL pattern
	mov	lo,r0
	inc	lo
	cmp	r0,size
	bhis	endpass
	br	notend

blkdsq:				;DECREASING SEQUENTIAL pattern
	mov	hi,r0
	dec	hi
	cmp	r0,#-1
	bne	notend
endpass:
	sec
	return
notend:
	add	offset,r0
	clc
	return
.Sbttl	Writing to the Terminal

.Enabl	lsb

;Expects: number in R0
;	  R3 / 0 if no letter to be appended, or ASCII letter if required
;			(used with RANDOM to differentiate between blocks
;			queued and blocks returned).

Tell:
	tst	listit
	bne	notell

	mov	r0,-(sp)
	mov	r1,-(sp)
	mov	r2,-(sp)
	mov	r3,-(sp)
	bne	10$
	mov	#40,r3		;Space in R3 if zero given.
10$:
	mov	#tbuff,r1
	mov	#7.,r2		;7 positions + 1 for letter
	call	wdpx		;Write decimal + positions
	movb	r3,(r1)+	;Insert letter (or space)
	inc	outcnt
	cmp	outcnt,#8.	;8 per line
	blt	20$
	movb	#cr,(r1)+
	movb	#lf,(r1)+
	clr	outcnt
20$:	clrb	(r1)+		;Clear out succeeding bytes
	clrb	(r1)+
30$:	tstb	-(r1)		;Now go back to last non-zero byte
	beq	30$
	sub	#tbuff-2,r1
	asr	r1		;Number of words
	.writw	#area,#1,#tbuff,r1,tblk
	bcc	40$
	type	</Error writing to TT: (block/<200>>
	mov	tblk,r0
	call	wdt
	type	</)/<0>>
	bisb	#error$,@#userrb
	.exit
40$:	inc	tblk

	mov	(sp)+,r3
	mov	(sp)+,r2
	mov	(sp)+,r1
	mov	(sp)+,r0
notell:
	return

.Dsabl	lsb

wdpx:					;Use double word output as otherwise
	mov	r0,-(sp)		; get signed block numbers.
	mov	r2,-(sp)
	mov	r3,-(sp)		;(This is the hasty lazy way to do it)

	mov	r2,r0
	mov	4(sp),r3
	clr	r2
	call	wd2p

	mov	(sp)+,r3
	mov	(sp)+,r2
	mov	(sp)+,r0
	return
.Sbttl	Completion Routines	

;There have to be at least as many completion routines as there
; are <maximum queued number (maxq)> + <DU command queueing capability>
; or you can get lost data by its being overwritten in the tables.

cr1:
	mov	#<2*0.>,-(sp)
	br	crtell
cr2:
	mov	#<2*1.>,-(sp)
	br	crtell
cr3:
	mov	#<2*2.>,-(sp)
	br	crtell
cr4:
	mov	#<2*3.>,-(sp)
	br	crtell
cr5:
	mov	#<2*4.>,-(sp)
	br	crtell
cr6:
	mov	#<2*5.>,-(sp)
	br	crtell
cr7:
	mov	#<2*6.>,-(sp)
	br	crtell
cr8:
	mov	#<2*7.>,-(sp)
	br	crtell
cr9:
	mov	#<2*8.>,-(sp)
	br	crtell
cr10:
	mov	#<2*9.>,-(sp)
	br	crtell
cr11:
	mov	#<2*10.>,-(sp)
	br	crtell
cr12:
	mov	#<2*11.>,-(sp)
	br	crtell
cr13:
	mov	#<2*12.>,-(sp)
	br	crtell
cr14:
	mov	#<2*13.>,-(sp)
	br	crtell
cr15:
	mov	#<2*14.>,-(sp)
	br	crtell
cr16:
	mov	#<2*15.>,-(sp)
	br	crtell
cr17:
	mov	#<2*16.>,-(sp)
	br	crtell
cr18:
	mov	#<2*17.>,-(sp)
	br	crtell
cr19:
	mov	#<2*18.>,-(sp)
	br	crtell
cr20:
	mov	#<2*19.>,-(sp)
	br	crtell
cr21:
	mov	#<2*20.>,-(sp)
	br	crtell
cr22:
	mov	#<2*21.>,-(sp)
	br	crtell
cr23:
	mov	#<2*22.>,-(sp)
	br	crtell
cr24:
	mov	#<2*23.>,-(sp)
	br	crtell
cr25:
	mov	#<2*24.>,-(sp)
	br	crtell
cr26:
	mov	#<2*25.>,-(sp)
	br	crtell
cr27:
	mov	#<2*26.>,-(sp)
	br	crtell
cr28:
	mov	#<2*27.>,-(sp)
	br	crtell
cr29:
	mov	#<2*28.>,-(sp)
	br	crtell
cr30:
	mov	#<2*29.>,-(sp)
	br	crtell
cr31:
	mov	#<2*30.>,-(sp)
	br	crtell
cr32:
	mov	#<2*31.>,-(sp)
	br	crtell
cr33:
	mov	#<2*32.>,-(sp)
	br	crtell
cr34:
	mov	#<2*33.>,-(sp)
	br	crtell
cr35:
	mov	#<2*34.>,-(sp)

crtell:
	add	#used,(sp)
	clr	@(sp)
	add	#blktab-used,(sp)
	mov	@(sp)+,@logred
	add	#2,logred
	mov	r0,@logred
	add	#2,logred
	cmp	logred,#logend
	blo	10$
	mov	#logtab,logred
10$:
	dec	queued
	.rsum
	return

.end	start
