;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ;
; DSKTIM.M68 - Disk timing program (combines REDALL,RNDRED,REDEND) ;
; ;
; Usage: DSKTIM dev0: reads /S/R/L/E /D:n /B:n /P:n /N ;
; ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; (c) Bob Fowler, 08/01/86, runs on any AMOSL 1.0 or later.
; Permission is given to copy and use this program, but not for profit.
; The references to REDEND refer to REDEND.M68, published in SF/AMUS.
; Explanation of options:
; /S - is the fastest read possible (sequential reads, like REDALL)
; /R - is a typical worst case (random reads, like RNDRED)
; /L - same as /R, but use the AMOSL method if possible (<65536 blocks)
; /E - is the slowest possible case (read first/last blocks, REDEND)
; Other options:
; /D:n - simulates a physical device with n logical devices
; /B:n - simulates a logical device with n blocks
; /B:a-b - simulates device from blocks a to b (>0)
; /P:n - displays progress reports every n reads (beware, slows timing)
; /N - no displays except for final timings
; Both /B and /D allow read ranges beyond one logical drive.
; If neither /B nor /D appear, the read range defaults to the given drive.
; A respresentative set of timings for each device is as follows:
; DSKTIM 2000 dev0: /S/N (only one short timing necessary)
; DSKTIM 2000 dev0: /E/N/D:n (several short timings necessary)
; DSKTIM 10000 dev0: /R/N/D:n (several longer timings necessary)
; where a separate timing is done for each of n = 1,2,4,8,16,32,max,min
; (max and min being the maximum and minimum "allowed" logical devices).
; If you do timings on any interesting disks (eg, non-Alpha Micro drives),
; please phone the results to Bob Fowler at 415-527-7631,
; and I will ad them to my growing list (goes back to Persci).
; Current liberal limitations:
; (a) a timing longer than 23.8 hours will calculate wrong average time
; (b) only 65536 drives on one device; DSK65536: will not work.
; Notes on usage
; (a) Timings are only in seconds, at least 100 seconds is recommended.
; (b) On random reads, at least 10000 reads are suggested.
; To monitor the convergence behavior of /R and /L timings,
; do a test run with /P:100, then do a final run without /P.
; (c) Note that /B:1 can be used to read the same block repeatedly.
; /B:68 can be used to read the same track (on an AM-1000 10MB).
; /E/B:69 or /E/B:136 can be used to always seek one track.
; /B:a-b can be used to randomly read one file, or one area of a disk.
; (d) Note that /D:1/R simulates random access of all devices.
; Notes on random read methods.
; Two random read methods are available, via switches /L and /R.
; (a) /L uses the same method as AMOSL RNDRED.LIT.
; This method is limited to 16-bit block ranges (65536 or less).
; It uses AMOSL.MON to generate random numbers as follows.
; Start X as garbage, then repeat the following ad infinitum:
; MOD ( 2*X + monitor word 1 + monitor word 2 , 65536) / blocks
; divides to a quotient Q and remainder R, read block R,
; set X = Q + R, and repeat loop.
; (b) /R uses a method based on Knuth (Semi-Numerical Algorithms, p1-24).
; It generates 32-bit pseudo-random numbers via the expression:
; X = ( (X + monitor word) x (1 + 2^2 + 2^14 + 2^23) + 1) MOD 2^32
; The monitor word can be left out, but is currently included.
; The tradeoffs: /R is more versatile, but has more CPU overhead.
; CPU overhead (ie, all operations other than the READ monitor calls):
; /S = .044 ms/read
; /E = .029 ms/read
; /L = .071 ms/read
; /R = .534 ms/read (/B:1) , .397 (/B:1000) , .337 (/B:8000000)
; /P = 16.6 ms per line displayed (one disk rotation) on the AM-1000
; Note the relatively large overhead for /R (because of 32-bit divisions).
; Any difference between /R and /L is undetectable in /B:1 and /B:68 ranges.
; Wide ranges are difficult to test definitively, due to randomness,
; but in several 4-hour timing comparisons of /L and /R in the /D:2 range,
; /R was consistently about 1.6% (1.3 ms/read) slower than /L.
; This is either due to /R overhead, or defects in one/both random algorithms.
; The overhead for /P does affect timings, but in a very regular fashion.
; Each /P line on the AM-1000 adds 16.6 ms (one disk rotation) to the timing.
; Hence, /P should not be used during a definitive timing run.
; Use of registers:
; A0 = base of impure work area
; A1 = device driver address
; A2 = [user input command string]
; A3,A4, D0,D1,D2,D3 = [work areas]
; D4 = random generator (during random read)
; = blocks on logical device (sequential read)
GETROM: GETIMP MEMLTH,A0 ; get impure user work area
MOV #-1,D6 ; get only device code (devn:)
FSPEC DDB(A0) ; process devn: input
INIT DDB(A0) ; get I/O buffer
MOV DDB+D.DVR(A0),A1 ; device driver address
MOV DD.DSZ(A1),LOGBLK(A0) ; blocks on drive (undocumented)
MOV A3,AMRLIM(A0) ; A3 used in AMOSL RNDRED algorithm
; For non-winchesters, physical blocks = logical blocks
MOVW #1,TOTLOG(A0) ; default to 1 logical device
MOV LOGBLK(A0),PHYBLK(A0) ; default physical to logical blocks
CLRW HCS(A0) ; Head/Cylinder/Sector not used
CMPW 70(A1),#170707 ; Winchester?
BEQ GETPHY ; yes - get physical blocks
; The following is used by Xebec drivers
CMPW 120(A1),#70707 ; Winchester?
BEQ GETPHY ; yes - get physical blocks
BR GOTPHY ; no - already have physical blocks
; For winchesters, physical blocks = heads x cylinders x sectors
GETPHY: MOVW 110(A1),TOTLOG(A0) ; save total # of logical drives
CLR D0 ; clear upper word
MOVW 74(A1),D0 ; D0 = heads
CLR D1 ; clear upper word
MOVW 100(A1),D1 ; sectors
MULU D0,D1 ; D0 = heads * sectors
MOVW 76(A1),D1 ; D1 = cylinders
MULU D0,D1 ; D0 = heads * sectors * cylinders
MOV D0,PHYBLK(A0) ; Total physical blocks
MOVW #1,HCS(A0) ; Head/Cylinder/Sector used
GOTPHY: MOVW TOTLOG(A0),USRLOG(A0) ; User logicals defaults to total log
; Get number of reads from user input (defaults to 2^32-1)
MOV #-1,D1 ; default lasts 414+ days ...
BYP ; bypass blanks
LIN ; anything there ?
BEQ GOTRDS ; no - use default
CMPB @A2,#'/ ; is next character "/" ?
BEQ GOTRDS ; yes - use default
GTDEC ; get user input
GOTRDS: MOV D1,REQRDS(A0) ; save
; Initialize default switch values
CLRW REDALL(A0) ; don't do REDALL
CLRW RNDRED(A0) ; don't do RNDRED
CLRW REDEND(A0) ; don't do REDEND
MOVW #1,DSPFLG(A0) ; do displays
MOV #-1,DSPRAT(A0) ; don't do progress displays
MOV DD.DSZ(A1),REQRNG(A0) ; range defaults to logical drive
MOV #1,BOTBLK(A0) ; default first block is one
MOV DD.DSZ(A1),TOPBLK(A0) ; default last block is logical size
SWLOOP: BYP ; bypass blanks
LIN ; anything there?
JEQ SWEND ; no - user input complete
CMPB (A2)+,#'/ ; slash?
JNE USAGE ; no - invalid input
CMPB @A2,#'S ; /S ?
BNE SWR ; no - try another
MOVW #1,REDALL(A0) ; do REDALL
SWBYP: ADD #1,A2 ; pass over /S
BR SWLOOP ; look for another switch
SWR: CMPB @A2,#'R ; /R ?
BNE SWL ; no - try another
MOVW #1,RNDRED(A0) ; do RNDRED
BR SWBYP ; look for another switch
SWL: CMPB @A2,#'L ; /L ?
BNE SWE ; no - try another
MOVW #1,RNDRED(A0) ; do RNDRED
MOVW #1,AMOSL(A0) ; use AMOSL method
BR SWBYP ; look for another switch
SWE: CMPB @A2,#'E ; /E ?
BNE SWN ; no - try another
MOVW #1,REDEND(A0) ; do REDEND
BR SWBYP ; look for another switch
SWN: CMPB @A2,#'N ; /N ?
BNE SWD ; no - try another
CLRW DSPFLG(A0) ; don't do displays
BR SWBYP ; look for another switch
; /D:n switch - emulate any number of logical devices
SWD: CMPB @A2,#'D ; /D ?
BNE SWB ; no - try another
ADD #1,A2 ; skip past "D"
CMPB (A2)+,#': ; ":" ?
JNE USAGE ; no - invalid format
GTDEC ; emulated number of logical devices
MOVW D1,USRLOG(A0) ; save (from longword to word)
JEQ USAGE ; zero logical devices illegal
MOV PHYBLK(A0),D0 ; recall total physical blocks
CALL DIVIDE ; extended divide routine (D2=D0/D1)
MOV D2,REQRNG(A0) ; quotient is requested block range
MOV BOTBLK(A0),D0 ; first block in requested range
ADD D2,D0 ; + requested range
SUB #1,D0 ; - 1
MOV D0,TOPBLK(A0) ; = last block in requested range
JMP SWLOOP ; look for another switch
; /B:n - range of blocks to read
SWB: CMPB @A2,#'B ; /B ?
BNE SWP ; no - try another
ADD #1,A2 ; skip past "B"
CMPB (A2)+,#': ; ":" ?
JNE USAGE ; no - invalid format
GTDEC ; input first block range number
TST D1 ; zero?
JEQ USAGE ; yes - not allowed
; one input number n indicates range 1 to n
MOV #1,BOTBLK(A0) ; first block defaults to 1
MOV D1,TOPBLK(A0) ; last block is user input
CMPB @A2,#'- ; "-" ?
BNE FINSWB ; no - done
; two input numbers a-b indicate range a to b
MOV D1,BOTBLK(A0) ; first block in requested range
ADD #1,A2 ; skip past "-"
GTDEC ; input second block range number
MOV D1,TOPBLK(A0) ; last block in requested range
FINSWB: SUB BOTBLK(A0),D1 ; range = last - first
ADD #1,D1 ; + 1
MOV D1,REQRNG(A0) ; range of requested blocks
JMP SWLOOP ; look for another switch
; /P:n switch - display progress reports
SWP: CMPB @A2,#'P ; /P ?
BNE SWZ ; no - try another
ADD #1,A2 ; skip past "P"
CMPB (A2)+,#': ; ":" ?
JNE USAGE ; no - invalid format
GTDEC ; reads between displays
MOV D1,DSPRAT(A0) ; save
JMP SWLOOP ; look for another switch
; invalid switch
SWZ: JMP USAGE ; none of the above ...
SWEND:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ;
; #4 - display disk data ;
; ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Display physical device parameters
TSTW DSPFLG(A0) ; do display?
JEQ ENDSP1 ; no - skip displays
TYPE <Physical device is>
MOV PHYBLK(A0),D1 ; physical blocks
DCVT 0,OT$TRM!OT$LSP!OT$TSP
TYPE <blocks>
TSTW HCS(A0) ; Head/Cylinder/Sector used?
BEQ FINHCS ; no - skip rest of line
TYPE < (>
CLR D1 ; clear upper word
MOVW 74(A1),D1 ; heads
DCVT 0,OT$TRM!OT$TSP
TYPE <heads x>
MOVW 76(A1),D1 ; cylinders
DCVT 0,OT$TRM!OT$LSP!OT$TSP
TYPE <cylinders x>
MOVW 100(A1),D1 ; sectors
DCVT 0,OT$TRM!OT$LSP!OT$TSP
TYPE <sectors)>
FINHCS: CRLF
; Display logical device parameters
TYPE <Logical device is>
MOV LOGBLK(A0),D1 ; logical blocks
DCVT 0,OT$TRM!OT$LSP!OT$TSP
TYPE <blocks>
TSTW HCS(A0) ; Head/Cylinder/Sector used?
BEQ FINLOG ; no - skip rest of line
TYPE < (>
MOV PHYBLK(A0),D1 ; physical blocks
DCVT 0,OT$TRM!OT$TSP
MOVB #'/,D1 ; can't put into TYPE
TTY
CLR D1 ; clear upper word
MOVW TOTLOG(A0),D1 ; logical devices
DCVT 0,OT$TRM!OT$LSP
TYPE <)>
FINLOG: CRLF
ENDSP1:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ;
; #5 - set up read range ;
; ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Range may span more than 1 logical device - allow for this
MOVW DDB+D.DRV(A0),BOTDRV(A0); bottom drive defaults to input
MOVW DDB+D.DRV(A0),TOPDRV(A0); top drive defaults to input
MOV REQRNG(A0),D1 ; final block range
MOV D1,ACTRNG(A0) ; actual range (default value)
MOV BOTBLK(A0),D0 ; first block in requested range
SUB #1,D0 ; blocks go from 0 to X-1
MOV D0,BOTREC(A0) ; bottom record of actual range
ADD D0,D1 ; + requested range
SUB #1,D1 ; - 1 = last block in actual range
MOV D1,TOPREC(A0) ; top record of actual range
CLR NOTAVL(A0) ; default to 0 blocks not available
CHKLOG: CMP D1,LOGBLK(A0) ; range beyond this logical device?
BLO GOTRNG ; no - range calculated
CLR D0 ; clear upper word
MOVW TOPDRV(A0),D0 ; pick up current drive
ADD #1,D0 ; convert to range starting with 1
CMPW D0,TOTLOG(A0) ; it is beyond last drive?
BHIS OVRFLO ; yes - user request not possible
ADDW #1,TOPDRV(A0) ; top drive goes to next logical
SUB LOGBLK(A0),D1 ; reduce block number
MOV D1,TOPREC(A0) ; save record number
BR CHKLOG ; check next logical drive
; Too many blocks requested. This can even happen on a simple /D:n request.
; If (eg) physical drive has 3002 blocks, in 3 logical devices,
; each logical device has 1000 blocks, and last 2 blocks are "unavailable".
OVRFLO: SUB LOGBLK(A0),D1 ; overflow block count
ADD #1,D1 ; starts at 0
MOV LOGBLK(A0),TOPREC(A0) ; top record number
SUB #1,TOPREC(A0) ; blocks go from 0 to X-1
SUB D1,ACTRNG(A0) ; subtract overflow from actual range
MOV D1,NOTAVL(A0) ; save overflow count
GOTRNG:
; AMOSL method is possible if first and last device are same
TSTW AMOSL(A0) ; is /L selected?
BEQ FINAML ; no - no problem
; CLR D0 ; clear upper word
MOVW BOTDRV(A0),D0 ; pick up first drive number
CMPW D0,TOPDRV(A0) ; same as last drive?
BEQ FINAML ; yes - no problem
CLRW AMOSL(A0) ; no - can't use AMOSL method
TYPECR <AMOSL method not possible>
FINAML:
; Displays
TSTW DSPFLG(A0) ; do display?
JEQ ENDSP2 ; no - skip displays
; Block range requested
MOV REQRNG(A0),D1 ; block range requested
DCVT 0,OT$TRM
TYPECR <-block range requested>
; Range overflow
MOV NOTAVL(A0),D1 ; get overflow count
BEQ FINAVL ; zero - no display
TYPE <Last> ; tell user what happened
DCVT 0,OT$TRM!OT$LSP!OT$TSP
TYPECR <blocks not available>
FINAVL:
; Actual test range
TYPE <Test range is> ; display range used
MOV ACTRNG(A0),D1 ; final block range
DCVT 0,OT$TRM!OT$LSP!OT$TSP
TYPE <blocks (>
PUSH A1
LEA A1,DDB+D.DEV(A0) ; location of RAD50 device name
LEA A2,PRTDEV(A0) ; location of ASCII output area
UNPACK ; device name in ASCII (dev)
CLRB @A2 ; following by a null
POP A1
SUB #3,A2 ; go back to beginning of string
TTYL @A2
CLR D1 ; clear upper word
MOVW BOTDRV(A0),D1 ; bottom drive #
DCVT 0,OT$TRM
TYPE <:>
MOV BOTREC(A0),D1 ; bottom record #
OCVT 0,OT$TRM!OT$LSP!OT$TSP
TYPE <to >
TTYL @A2
CLR D1 ; clear upper word
MOVW TOPDRV(A0),D1 ; top drive #
DCVT 0,OT$TRM
TYPE <:>
MOV TOPREC(A0),D1 ; top record #
OCVT 0,OT$TRM!OT$LSP!OT$TSP
TYPECR <octal)>
; REDALL routine - preserve D4
TSTW REDALL(A0) ; do a REDALL?
JEQ ENDALL ; no - skip to end
CALL DOCRLF
TYPE <REDALL - >
MOV ACTRNG(A0),D1 ; actual range
MOV REQRDS(A0),D0 ; requested reads
CMP D0,#-1 ; request number entered?
BEQ BEGALL ; no - use actual range
CMP D0,D1 ; requested reads too high?
BHI BEGALL ; yes - use actual range
MOV D0,D1 ; no - use requested reads
BEGALL: CALL BEFORE
MOVW BOTDRV(A0),DDB+D.DRV(A0); bottom drive number
MOV BOTREC(A0),DDB+D.REC(A0); bottom block number
MOV LOGBLK(A0),D4 ; logical blocks on drive
NXTALL: CTRLC FINALL ; check for impatient user ...
READ DDB(A0) ; read!
ADD #1,CURRDS(A0) ; one more read done
SUB #1,REMRDS(A0) ; one less read to do
BNE CNTALL ; skip display if countdown not 0
CALL AFTER ; display progress
BEQ ENDALL ; if done, don't continue
CNTALL:
ADD #1,DDB+D.REC(A0) ; go to next block
CMP D4,DDB+D.REC(A0) ; block beyond this drive?
BHI NXTALL ; no - keep going
ADDW #1,DDB+D.DRV(A0) ; yes - go to next logical drive
CLR DDB+D.REC(A0) ; block number starts at zero
BR NXTALL ; do another read
FINALL: CALL LSTDSP ; display timings
ENDALL:
; REDEND routine - preserve no regs
TSTW REDEND(A0) ; do a REDEND?
JEQ ENDEND ; no - skip to end
CALL DOCRLF
TYPE <REDEND - >
MOV REQRDS(A0),D1 ; requested reads
CALL BEFORE
NXTEND: CTRLC FINEND ; check for impatient user ...
MOVW BOTDRV(A0),DDB+D.DRV(A0); bottom drive number
MOV BOTREC(A0),DDB+D.REC(A0); bottom block number
READ DDB(A0) ; read!
ADD #1,CURRDS(A0) ; one more read done
SUB #1,REMRDS(A0) ; one less read to do
BNE CNTEND ; skip display if countdown not 0
CALL AFTER ; display progress
BEQ ENDEND ; if done, don't continue
CNTEND:
MOVW TOPDRV(A0),DDB+D.DRV(A0); top drive number
MOV TOPREC(A0),DDB+D.REC(A0); top block number
READ DDB(A0) ; read!
ADD #1,CURRDS(A0) ; one more read done
SUB #1,REMRDS(A0) ; one less read to do
BNE NXTEND ; skip display if countdown not 0
CALL AFTER ; display progress
BNE NXTEND ; if done, don't continue
BR ENDEND ; don't display again
FINEND: CALL LSTDSP ; display timings
ENDEND:
; RNDRED routine - preserve A3,A4,D4
TSTW RNDRED(A0) ; do a RNDRED?
JEQ ENDRND ; no - skip to end
CALL DOCRLF
TSTW AMOSL(A0) ; use AMOSL method?
JNE AMRND ; yes - go to it
TYPE <RBF RNDRED - >
MOV REQRDS(A0),D1 ; requested reads
CALL BEFORE
MOV MEMBAS,A4 ; use monitor up to first user
CLR D4 ; start random seed
CYCLE1: MOV #SYSTEM,A3 ; base of AMOSL.MON
NXTRND: CTRLC FINRND ; check for impatient user ...
MOV D4,D2 ; D4 x 2^0
; following computes D4 = (D4 x (1 + 2^2 + 2^14 + 2^23) + 1) MOD 2^32
ASL D2,#2 ; D4 x 2^2
ADD D2,D4
ASL D2,#6
ASL D2,#6 ; D4 x 2^14
ADD D2,D4
ASL D2,#3
ASL D2,#6 ; D4 x 2^23
ADD D2,D4
ADD #1,D4 ; +1
; 32-bit number / physical blocks ===> remainder is random block
MOV D4,D0 ; random number
MOV ACTRNG(A0),D1 ; / block range
CALL DIVIDE ; D0 / D1 ===> D2 rem D0
; block / logical blocks ===> quotient (drive #) rem (logical block)
ADD BOTREC(A0),D0 ; may not start at block 0
MOV LOGBLK(A0),D1 ; logical blocks
CALL DIVIDE ; D0 / D1 ===> D2 rem D0
ADDW BOTDRV(A0),D2 ; may not start at drive 0
MOVW D2,DDB+D.DRV(A0) ; longword to word
MOV D0,DDB+D.REC(A0)
READ DDB(A0)
ADD #1,CURRDS(A0) ; one more read done
SUB #1,REMRDS(A0) ; one less read to do
BNE CNTRND ; skip display if countdown not 0
CALL AFTER ; display progress
BEQ ENDRND ; if done, don't continue
CNTRND:
; To remove AMOSL.MON from random calculation, comment out next 3 lines
ADD (A3)+,D4 ; add in AMOSL.MON longword
CMP A4,A3 ; here yet?
BLO CYCLE1 ; yes - cycle through again
BR NXTRND ; no - just do other stuff
FINRND: CALL LSTDSP ; display timings
ENDRND:
JMP END
; AMOSL RNDRED routine - preserve A3,A4,D4,D5
AMRND: TYPE <AMOSL RNDRED - >
MOV REQRDS(A0),D1 ; requested reads
CALL BEFORE
MOV AMRLIM(A0),A4 ; use monitor up to first user
MOV ACTRNG(A0),D5 ; logical blocks
CYCLE2: MOV #SYSTEM,A3 ; start at beginning of monitor
NXTAMR: CTRLC FINRND ; check for control-C exit
ASL D4 ; random number x 2
ADDW (A3)+,D4 ; + first monitor word
ADDW (A3)+,D4 ; + second monitor word
AND #177777,D4 ; use only lower order word
DIV D4,D5 ; / blocks
MOV D4,D1 ; get remainder (high order word)
SWAP D1
AND #177777,D1
ADD D1,D4 ; + quotient is next random #
ADD BOTREC(A0),D1 ; may not start at block 0
MOV D1,DDB+D.REC(A0) ; next block to read
READ DDB(A0) ; read this block
ADD #1,CURRDS(A0) ; one more read done
SUB #1,REMRDS(A0) ; one less read to do
BNE CNTAMR ; skip display if countdown not 0
CALL AFTER ; display progress
BEQ ENDRND ; if done, don't continue
CNTAMR:
CMP A4,A3 ; are we at end of used memory ?
BLO CYCLE2 ; yes - recycle through monitor
BR NXTAMR ; no - get another word from memory
DOCRLF: TSTW DSPFLG(A0) ; do display?
BEQ CRLF1 ; no - skip displays
CRLF
CRLF1: RTN
BEFORE:
TSTW DSPFLG(A0) ; do display?
BEQ BEFOR1 ; no - skip following
TYPE <Doing>
DCVT 0,OT$TRM!OT$LSP!OT$TSP
TYPECR <reads (control-C allowed) ...>
BEFOR1: JOBIDX A1 ; get address of job status word
ANDW #^CJ.CCC,JOBSTS(A1) ; clear any pending control-C
CLR CURRDS(A0) ; clear read counter
MOV D1,ACTRDS(A0) ; save actual number of reads
MOV DSPRAT(A0),D0 ; pick up display rate
CMP D0,D1 ; is it higher?
BHI BEGCNT ; yes - use actual number of reads
MOV D0,D1 ; no - use display rate instead
BEGCNT: MOV D1,REMRDS(A0) ; start progress display countdown
GTIMEI D0 ; get starting time (seconds)
MOV D0,BEGTIM(A0) ; save it
GDATEI D0 ; get starting date (days)
MOV D0,BEGDAT(A0) ; save it
RTN
LSTDSP: MOV CURRDS(A0),ACTRDS(A0) ; Control-C abort - end reads
AFTER: MOV CURRDS(A0),D1 ; total number of reads
CMP D1,ACTRDS(A0) ; is this the last display?
BHIS AFTER1 ; yes - must display results
TSTW DSPFLG(A0) ; do displays? (/N)
JEQ FINDSP ; no - skip displays this time
AFTER1: TST D1 ; did zero blocks get read?
BNE GOTRED ; no - no problem
TYPECR <no reads> ; yes - avoid division by zero
JMP FINDSP ; bypass all further display
GOTRED: DCVT 0,OT$TRM!OT$TSP
TYPE <disk reads in>
GDATEI D0 ; get current date (days)
SUB BEGDAT(A0),D0 ; subtract starting date
MULU D0,#43200. ; convert to seconds
ADD D0,D0 ; cannot multiply by 86400 (> 65536)
GTIMEI D1 ; get current time (seconds)
ADD D1,D0 ; add to total
SUB BEGTIM(A0),D0 ; subtract beginning time (seconds)
MOV D0,D1
DCVT 0,OT$TRM!OT$LSP!OT$TSP
TYPE <seconds >
TST D1 ; positive?
BGT TIMEOK ; yes - calculate average
TYPECR <(time not valid)> ; no - end
BR FINDSP
TIMEOK: TYPE <=>
; following overflows if timings go longer than 84000 seconds (.9 days)
MULU D0,#50000. ; get .02 ms units (must be < 86400)
MOV CURRDS(A0),D1 ; total number of reads
CALL DIVIDE ; big fat divide ...
DIVU D2,#50. ; quotient is ms, remainder is .01 ms
CLR D1 ; clear upper word
MOVW D2,D1 ; ms part
DCVT 0,OT$TRM!OT$LSP ; "nnnn"
TYPE <.> ; "."
SWAP D2 ; get remainder
MOVW D2,D1 ; centi-ms part
ADD D1,D1 ; convert from n/50 to n/100
DCVT 2,OT$TRM!OT$TSP ; "nn"
TYPECR <ms per read>
FINDSP: MOV ACTRDS(A0),D1 ; total reads planned
SUB CURRDS(A0),D1 ; - reads accomplished
BEQ ENDDSP ; done (Z bit still set on return)
MOV DSPRAT(A0),D0 ; pick up display rate
CMP D0,D1 ; is it higher?
BHI SETDSP ; yes - use actual number of reads
MOV D0,D1 ; no - use display rate instead
SETDSP: MOV D1,REMRDS(A0) ; start progress display countdown
ENDDSP: RTN ; read some more (Z bit is returned)
USAGE: LEA A2,HELP ; give 'em some assistance
TTYL @A2
EXIT ; and leave
HELP: ASCII |Usage: DSKTIM dev0: reads /S/R/L/E /D:n/B:n/P:n/N|
BYTE 15
ASCII | /S does a sequential read (REDALL)|
BYTE 15
ASCII | /R does a random read (RNDRED)|
BYTE 15
ASCII | /L does a random read using AMOSL method|
BYTE 15
ASCII | /E does a first-last read (REDEND)|
BYTE 15
ASCII | /D:n simulates n logical devices|
BYTE 15
ASCII | /B:n simulates device with n decimal blocks|
BYTE 15
ASCII | /B:a-b simulates device from blocks a to b (>0)|
BYTE 15
ASCII | /P:n display progress every n reads (slows timing!)|
BYTE 15
ASCII | /N no display except final timings|
BYTE 15
BYTE 0
EVEN
; The 68000 can only divide 32 bits / 16 bits ===> 16-bit quot , 16-bit rem
; The routine below divides 32 bits / 32 bits ===> 32-bit quot , 32-bit rem
; This can be done in one instruction on the 68020
; Following is the 68000 routine
CLR D2 ; D2 = quotient (clear first)
MOV #1,D3 ; D3 = current quotient bit
SHIFT1: CMP D0,D1 ; can we subtract?
BLO TEST ; no - divisor is shifted enough
ASL D1 ; shift again
BCS BACK ; we went too far!
ASL D3 ; quotient bit similiarly shifted
BR SHIFT1 ; keep going
BACK: ROXR D1 ; undo last shift
TEST: CMP D0,D1 ; can we subtract?
BLO SHIFT2 ; no - go shift
SUB D1,D0 ; subtract divisor multiple
ADD D3,D2 ; add bit to quotient
SHIFT2: LSR D3 ; quotient bit halved
BEQ ENDDIV ; if zero, we are done
LSR D1 ; divisor multiple is halved
BR TEST ; test next quotient subtract
ENDDIV: RTN
; Following is the equivalent 68020 routine
; DIVUL D2:D0,D1
; EXG D0,D2
; RTN
ASECT
.=0
; Symbols for impure work.buffer area - MUST BE PUT HERE
; (if put at beginning, FIX uses these labels instead of the PSECT labels !!!)
AMRLIM: BLKL 1 ; contents of A3 upon entering program
HCS: BLKW 1 ; Head/Cylinder/Sector used flag
REDALL: BLKW 1 ; REDALL requested flag (/S)
RNDRED: BLKW 1 ; RNDRED requested flag (/R)
REDEND: BLKW 1 ; REDEND requested flag (/E)
AMOSL: BLKW 1 ; Use AMOSL method for random read (/L)
DSPRAT: BLKL 1 ; Reads between each progress display (/P)
DSPFLG: BLKL 1 ; Display flag (/N)
REQRDS: BLKL 1 ; Requested number of reads (user)
ACTRDS: BLKL 1 ; Actual number of reads planned
REMRDS: BLKL 1 ; Remaining number of reads (until display)
TOTLOG: BLKW 1 ; Total logical devices in physical drive
USRLOG: BLKW 1 ; User (emulated) logical devices
LOGBLK: BLKL 1 ; Blocks on logical device (1+)
PHYBLK: BLKL 1 ; Blocks on physical device (1+)
BOTBLK: BLKL 1 ; First block in requested range (1+)
TOPBLK: BLKL 1 ; Last block in requested range (1+)
REQRNG: BLKL 1 ; Block range of reads - requested
ACTRNG: BLKL 1 ; Block range of reads - actual
CURRDS: BLKL 1 ; Current number of reads done so far
BOTDRV: BLKW 1 ; Bottom Drive Number
BOTREC: BLKL 1 ; Bottom Block Number
TOPDRV: BLKW 1 ; Top Drive Number
TOPREC: BLKL 1 ; Top Block Number
BEGTIM: BLKL 1 ; Beginning time (seconds)
BEGDAT: BLKL 1 ; Beginning date (days)
PRTDEV: BLKB 3 ; For RAD50 unpacking (ASCII)
NULL: BLKB 1 ; For RAD50 unpacking (null)
NOTAVL: BLKL 1 ; Not available for reading
DDB: BLKB D.DDB ; DDB for reading
MEMLTH: ; Memory used