;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; 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) COPY SYS SEARCH SYSSYM DSKTIM: BYP ; bypass blanks LIN ; empty? JEQ USAGE ; yes - missing device ;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; #1 - process filespec ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;; 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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; #2 - process read count ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 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 ;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; #3 - process switches ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 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 MOV PHYBLK(A0),D1 ; physical blocks DCVT 0,OT$TRM!OT$LSP!OT$TSP TYPE 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 MOVW 76(A1),D1 ; cylinders DCVT 0,OT$TRM!OT$LSP!OT$TSP TYPE MOVW 100(A1),D1 ; sectors DCVT 0,OT$TRM!OT$LSP!OT$TSP TYPE FINHCS: CRLF ; Display logical device parameters TYPE MOV LOGBLK(A0),D1 ; logical blocks DCVT 0,OT$TRM!OT$LSP!OT$TSP TYPE 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 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 ; tell user what happened DCVT 0,OT$TRM!OT$LSP!OT$TSP TYPECR FINAVL: ; Actual test range TYPE ; display range used MOV ACTRNG(A0),D1 ; final block range DCVT 0,OT$TRM!OT$LSP!OT$TSP TYPE 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 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 ENDSP2: ;;;;;;;;;;;;;;;;;;; ; ; ; #6 - do reads ; ; ; ;;;;;;;;;;;;;;;;;;; ; REDALL routine - preserve D4 TSTW REDALL(A0) ; do a REDALL? JEQ ENDALL ; no - skip to end CALL DOCRLF TYPE 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 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 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 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 END: EXIT ;;;;;;;;;;;;;;;;;;; ; ; ; #7 - routines ; ; ; ;;;;;;;;;;;;;;;;;;; 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 DCVT 0,OT$TRM!OT$LSP!OT$TSP TYPECR 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 ; yes - avoid division by zero JMP FINDSP ; bypass all further display GOTRED: DCVT 0,OT$TRM!OT$TSP TYPE 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 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 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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Extended Divide Routine ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 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 ; Reg Input Output ; --- ----------- --------- ; D0 Dividend Remainder ; D1 Divisor Divisor ; D2 [garbage] Quotient ; D3 [garbage] [zero] DIVIDE: ; 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 END .