	.TITLE	QREPORT - Report disk quota usage for current default disk
	.IDENT /V1.0/
;******************************************************************************
;	QREPORT was written for use with the DISKUSE utility found on the
; fall 83 DECUS tape. QREPORT produces a report showing disk utilization for
; the current default disk family. This report includes the following
; information;
;
;		UIC
;		Username (from UAF)
;		Blocks used
;		Percentage of base quota used
;		Base quota (Overdraft)
;		Extended quota (Permanent)
;
;	The username is read from the UAF using RMS ISAM. If multiple users
; have the same UIC, the first user (alphabetically) is reported as the owner.
;
;	If a UIC cannot be found in the UAF, the user name will be printed as
; '????????????'.
;
;	The report is broken up into UIC groups with the exception of the first
; page, which reports those UIC's having unlimited quotas (signified on our
; system by having a base quota of 999999). Group totals and grand totals are
; given. Grand totals do not include the totals for the unlimited quota UIC's. 
;
;	NOTE: This program assumes that you are using the DISKUSE program
; found on the Fall 1983 DECUS tape. DISKUSE treats the overdraft quota as the
; base quota and treats the permanent quota as your extended quota. QREPORT
; labels the two quotas as if you are using DISKUSE. You can use this program
; without DISKUSE if you change the follwing lines;
;
;	MOVL	DQF$L_OVERDRAFT(R11),R2	 ; ***** BASE QUOTA     *****
;	MOVL	DQF$L_PERMQUOTA(R11),R5	 ; ***** EXTENDED QUOTA *****
;
;			TO
;
;	MOVL	DQF$L_OVERDRAFT(R11),R5	 ; ***** EXTENDED QUOTA *****
;	MOVL	DQF$L_PERMQUOTA(R11),R2	 ; ***** BASE QUOTA     *****
;
;	NOTE: Use of this program requires a system UIC, BYPASS or SYSPRV. It
; is not a good idea to install it with BYPASS or SYSPRV.
;
;	NOTE: QREPORT has not been tested under VMS V4.0 and may not run
; because of the expanded UIC size in V4.0 and possible changes to the UAF.
;
;	Compile and link thusly;
;		MACRO QREPORT
;		LINK QREPORT,SYS$SYSTEM:SYS.STB/SELECTIVE
;******************************************************************************

	.LIBRARY	/SYS$LIBRARY:LIB/

	$FABDEF		; define FAB
	$RABDEF		; define RAB
	$RMSDEF		; define RMS status codes
	$DQFDEF		; define Disk Quota File block
	$FIBDEF		; define File ID Block
	$IODEF		; define I/O def's
	$SSDEF		; define SS definitions
	$PCBDEF		; define Process Control Block
	$UAFDEF		; define User Authorization File

;
;	macros
;
;MACRO ERRORCK - checks R0 for error condition
	.MACRO	ERRORCK,?L1
	BLBS	R0,L1
	$EXIT_S	CODE = R0
L1:
	.ENDM	ERRORCK
;
; MACRO TO SET A BIT BY BIT NUMBER
;
;CALL:
;	SETBIT	BITNUM,FLAGWORD
;
; WHERE:
;	BITNUM IS ANY VALID SOURCE OPERAND SECIFYING THE BIT
;		OFFSET FROM THE FLAG BASE TO SET
;
;	FLAGWORD IS ANY VALID DESTINATION OPERAND
;
	.MACRO	SETBIT	VAL,FLAG
	.NTYPE	_$$_	VAL
	.IF EQ	<_$$_-^X0EF>
	.IF NDF	VAL
	BBSS	S^#VAL,FLAG,.+1
	.IFF
	.IF LT <VAL-8>
	BISB	#<1@VAL>,FLAG
	.IFF
	BBSS	#VAL,FLAG,.+1
	.ENDC
	.ENDC
	.IFF
	BBSS	VAL,FLAG,.+1
	.ENDC
	.ENDM	SETBIT

	RETRY_RLK = 2			; number of retries if record is locked
	SLEEP_RLK = 75			; MS to sleep before retrying a GET
	UAF$_NORMAL = 1			; normal completion on UAF read
	UAF$_INVUSR = -2		; user not found in UAF

	.PSECT	QREPORT_LOCAL_DATA,WRT,PIC,NOEXE,NOSHR,LONG,GBL
;
;---

;+++
; Usage Message
;
FAOLINE:
	.ASCID ' [!OB,!OB] !AS !9SL        !3SL%      !9SL      !9SL '

HEADLINE:
	.ASCID '    UIC        USER         USED      % OF BASE     '-
'BASE QUOTA   EXTENDED QUOTA'

FFLINE:
	.ASCID ' !^ '

TOT1LINE:
	.ASCID '!/ Total                  !9SL '

TOT2LINE:
	.ASCID '!/ Total                  !9SL        !3SL%      !9SL      '-
'!9SL '

TOT3LINE:
	.ASCID '!/!/Grand Total             !9SL        !3SL%      !9SL      '-
'!9SL'

;
; $ FAO stuff
;
; DO NOT REARRANGE -- contains string descriptors
FAODESC:
	.WORD		80
	.WORD		0
	.LONG		FAOBUF
FAOBUF:	.BLKB		80

;
;	DISK block stuff
;
; DO NOT REARRANGE -- contains string descriptors
DRIVE:	.ASCID /SYS$DISK/
DKNAME:	.LONG 63
DKADDR:	.LONG DK
DK:	.BLKB 63
DKCHAN:	.WORD 0

;
; $ ACP CONTROL stuff
;
FIBDESC:					; FIB descriptor
	.WORD		FIB$C_LENGTH		; length
	.WORD		0			; not used
	.ADDRESS	FIBBLK			; address of fib block
FIBBLK:	.BLKB		FIB$C_LENGTH		; an FIB

DQFDESC:					; disk quota file descriptor
	.WORD		DQF$C_LENGTH		; length
	.WORD		0			; not used
	.ADDRESS	DQFBLK			; address of dqf block
DQFBLK:	.BLKB		DQF$C_LENGTH		; a DQF block (only need one)

IOSB:	.BLKW	4				; I/O status block

	.ADDRESS	GRPBLK			; group valid table
GRPBLK:	.BLKB		^O377			;  with 377 (octal) entries

TOTAL_USED:	.BLKL	1			; group blocks used
TOTAL_PERM:	.BLKL	1			; group blocks perm quota
TOTAL_EXTD:	.BLKL	1			; group blocks extended quota

GRAND_USED:	.BLKL	1			; system blocks used
GRAND_PERM:	.BLKL	1			; system blocks perm quota
GRAND_EXTD:	.BLKL	1			; system blocks extended quota

QRP$Q_UAFREC:					; desc for UAF record
	.LONG	UAF$K_LENGTH,QRP$G_UAFREC

QRP$G_UAFREC:					; UAF record buffer
	.BLKB	UAF$K_LENGTH			; space for record

NAME_DESC:
	.LONG		12			; user name descriptor
	.ADDRESS	QRP$G_UAFREC

QMARKS:						; default user name
	.ASCII /????????????/
;
; UAF file name string
;

UAFNAME:	.ASCII	/SYSUAF/		; UAF file name
UAFSIZE=.-UAFNAME

DEFNAME:	.ASCII	/SYS$SYSTEM:.DAT/	; UAF location and extension
DEFSIZE=.-DEFNAME

WAKEDELTA:					; time to sleep before retry
	.LONG	-10*1000*SLEEP_RLK,-1

UIC:	.BLKL	1				; UIC to look-up

SAVESP:	.BLKL	1				; place to stash Stack Pointer
SAVER6:	.BLKL	1				; place top stash R6
SAVER7:	.BLKL	1				; place to stash R7

	.PSECT  QREPORT_CODE,EXE,PIC,NOWRT,SHR,LONG
	.ENTRY	QREPORT, ^M<>
;******************************************************************************
;	Qreport has 2 passes. The first pass prints out those UIC's having
; unlimited (999999) base quotas and sets a flag in an array indicating which
; groups are valid.
;	Pass 2 uses the array to determine which groups to print. This allows
; the report to be in order by group number without having to sort.
;******************************************************************************

START:
; open UAF
	MOVAB	-FAB$K_BLN(SP),R6	; allocate a FAB in the stack
	MOVAB	-RAB$K_BLN(R6),R7	; then a RAB
	MOVL	R7,SP			; now create the space
	MOVC5	#0,(SP),#0,#FAB$K_BLN+RAB$K_BLN,(SP) ; zero FAB and RAB
	ASSUME	FAB$B_BLN EQ FAB$B_BID+1 ; check for byte adjacency
	MOVW	#FAB$K_BLN@8!FAB$C_BID,- ; set block length and block ID-
		FAB$B_BID(R6)		; values into proper fields of FAB
	MOVAL	UAFNAME,FAB$L_FNA(R6)	; insert file name address
	MOVB	#UAFSIZE,FAB$B_FNS(R6)	; insert file name size
	MOVAL	DEFNAME,FAB$L_DNA(R6)	; insert default file name address
	MOVB	#DEFSIZE,FAB$B_DNS(R6)	; insert default name size
	SETBIT	FAB$V_GET,FAB$B_FAC(R6)	; get access only
	MOVB	#FAB$M_GET!FAB$M_PUT!-	; share all operations
		FAB$M_UPD!FAB$M_DEL,FAB$B_SHR(R6)
	MOVB	#FAB$C_IDX,FAB$B_ORG(R6) ; indexed organization
	MOVB	#FAB$C_VAR,FAB$B_RFM(R6) ; variable length records
	ASSUME	RAB$B_BLN EQ RAB$B_BID+1 ; check for byte adjacency
	MOVW	#RAB$K_BLN@8!RAB$C_BID,- ; set block length and block ID-
		RAB$B_BID(R7)		; values into proper fields of RAB
	MOVB	#RAB$C_KEY,RAB$B_RAC(R7) ; keyed access
	SETBIT	RAB$V_NLK,RAB$L_ROP(R7)	; don't lock anbody's record
	MOVB	#10,RAB$B_MBC(R7)	; multi-block reads
	MOVB	#1,RAB$B_KRF(R7)	; use uic key (key 1)
	MOVAQ	QRP$Q_UAFREC,R5		; get user descriptor address
	MOVW	(R5),RAB$W_USZ(R7)	; user buffer size
	MOVL	4(R5),R5		; save buffer address in R5
	MOVL	R5,RAB$L_UBF(R7)	; put user buffer address in rab
	MOVAL	UIC,RAB$L_KBF(R7)	; stash address of key
	MOVB	#4,RAB$B_KSZ(R7)	; stash size of key
	MOVAL	(R6),RAB$L_FAB(R7)	; insert FAB address

; Open the File and connect RAB

	$OPEN	FAB=(R6)
	BLBS	R0,14$			; skip err checking if no err
	CMPL	R0,#RMS$_SNE		; If sharing is not enabled try
	BEQL	10$			; accessing it without sharing.
	CMPL	R0,#RMS$_SPE		; The same goes for insufficient
	BEQL	10$			; dynamic memory problems.
	CMPL	R0,#RMS$_DME
	BEQL	10$			
	ERRORCK				; exit on error

10$:
	CLRB	FAB$B_SHR(R6)		; clear share flag
	$OPEN	FAB=(R6)		
	ERRORCK				; exit on error

14$:
	$CONNECT RAB=(R7)		; connect RAB to FAB
	ERRORCK				; exit on error
	MOVL	R6,SAVER6		; stash R6
	MOVL	R7,SAVER7		; stash R7

; UAF is now open, begin processing

	MOVW	#80,FAODESC		; set fao length to 80
	$FAO_S	-			; write header line
		CTRSTR = HEADLINE, -	; control string
		OUTLEN = FAODESC, -	; output length
		OUTBUF = FAODESC	; output descriptor
	ERRORCK				; exit on error
	PUSHAL	FAODESC			; push fao output descriptor on stack
	CALLS	#1,G^LIB$PUT_OUTPUT	; output to sys$output

; assign channel to users default disk

	$ASSIGN_S -			; assign channel to SYS$DISK
		DEVNAM=DRIVE, -
		CHAN=DKCHAN
	ERRORCK				; exit on error
	$TRNLOG_S -			; translate SYS$DISK
		LOGNAM=DRIVE,-		; -- we assume only one translation
		RSLLEN=DKNAME,-		; -- will be necessary. This
		RSLBUF=DKNAME		; -- can be risky.
	ERRORCK				; everything OK?
	MOVAB	GRPBLK,R10		; set r10 up to index grpblk
	MOVAB	FIBBLK, R0		; stash fib address in r0
	MOVW	#FIB$C_EXA_QUOTA, FIB$W_CNTRLFUNC(R0)	; examine quota entry
	BISB2	#FIB$M_ALL_MEM,FIB$L_CNTRLVAL(R0) ; we will be wildcarding
	BISB2	#FIB$M_ALL_GRP,FIB$L_CNTRLVAL(R0) ; group and member
	MOVL	#0,FIB$L_WCC(R0)		; 
	MOVAB	DQFBLK, R11			; R11 is not scratch anymore!!!
	MOVW	#0, DQF$L_UIC(R11)		; wildcard member
	MOVW	#0, DQF$L_UIC+2(R11)		; ... and group

READ_QUOTA_1:
	MOVAB	DQFDESC,R0			; stash quota descriptor in R0
	$QIOW_S	-				; do the read
		CHAN=DKCHAN, -			; disk channel
		FUNC=#IO$_ACPCONTROL, -		; ACP function
		IOSB=IOSB, -			; IO status block
		P1=FIBDESC, -			; FIB descriptor
		P2=#DQFDESC, -			; Disk Quota Descriptor-IN
		P4=#DQFDESC			; Disk Quota Descriptor-OUT
	ERRORCK					; exit on error
	MOVZWL	IOSB,R0				; get IOSB
	BLBS	R0,GOTO_CALC_1			; if 1, then continue
	BRB	HANDLE_ERRS_1			; go handle recoverable errors

GOTO_CALC_1:
	BRW	CALC_USAGE_1

HANDLE_ERRS_1:
	CMPL	R0,#SS$_NODISKQUOTA		; ... compare to NODISKQUOTA
	BEQLU	PASS_DONE_1			; we are done with pass 1
	ERRORCK					; exit on error

PASS_DONE_1:
	MOVL	TOTAL_USED, R9
	MOVW	#80,FAODESC			; set fao length to 80
	$FAO_S	-				; format ascii output
		CTRSTR = TOT1LINE, -		; control string
		OUTLEN = FAODESC, -		; output length
		OUTBUF = FAODESC, -		; output descriptor
		P1=R9
	ERRORCK					; exit on error
	PUSHAL	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	MOVL	#0, TOTAL_USED
	BRW	SETUP_READ_QUOTA_2		; go to next pass

CALC_USAGE_1:
	CVTWL	DQF$L_UIC+2(r11),r4		; group #
	CMPW	#0,R4				; is this [0,0]?
	BEQL	READ_NEXT_1			;   yes, so skip it
	MOVL	DQF$L_USAGE(R11),R1		; get blocks in use

	MOVL	DQF$L_OVERDRAFT(R11),R2	 ; ***** BASE QUOTA     *****
	MOVL	DQF$L_PERMQUOTA(R11),R5	 ; ***** EXTENDED QUOTA *****

	CMPL	#^D999999,R2			; do they have unlimited quota?
	BNEQ	SET_GRPBLK_1			; no, skip print, set grpblk
	ADDL2	R1, TOTAL_USED			; add to total used
	CVTLF	R1, R6				; setup for percent calculation
	CVTLF	R2, R7
	DIVF3	R7, R6, R8			; get % of quota used
	MULF3	#^F100, R8, R6
	CVTFL	R6, R7
	BRB	PRINT_USAGE_1

SET_GRPBLK_1:
	MOVB	#1,(R10)[R4]			; set group flag in grpblk

READ_NEXT_1:
	BRW	READ_QUOTA_1

PRINT_USAGE_1:
	BSBW	SEARCH_UAF			; search uaf for user name
	MOVW	#80,FAODESC			; set fao length to 80
	$FAO_S	-				; format ascii output
		CTRSTR = FAOLINE, -		; control string
		OUTLEN = FAODESC, -		; output length
		OUTBUF = FAODESC, -		; output descriptor
		P1=DQF$L_UIC+2(R11), -		; group
		P2=DQF$L_UIC(R11), -		; member
		P3=#NAME_DESC,-			; user name
		P4=R1,-				; blocks used
		P5=R7,-				; % of base quota used
		P6=R2,-				; base quota
		P7=R5				; extended quota
	ERRORCK					; exit on error
	PUSHAL	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	BRW	READ_QUOTA_1

SETUP_READ_QUOTA_2:
	MOVAB	FIBBLK, R0
	MOVW	#FIB$C_EXA_QUOTA, FIB$W_CNTRLFUNC(R0) ; examine quota entry
	BICB2	#FIB$M_ALL_GRP,FIB$L_CNTRLVAL(R0) ;
	BISB2	#FIB$M_ALL_MEM,FIB$L_CNTRLVAL(R0) ;
	MOVAB	DQFBLK, R11			; R11 is not scratch anymore!!!
	MOVL	#0,R4				; set group pointer to zero
	BRB	GET_NEXT_GROUP_2

ALL_DONE_2:
	BRW	DONE

GET_NEXT_GROUP_2:
	INCL	R4				; increment group pointer
	CMPL	#^O400,R4			; are we done?
	BEQLU	ALL_DONE_2			;   yes, so get out
	CMPB	#0,(R10)[R4]			; do we have any members in grp
	BEQL	GET_NEXT_GROUP_2		;   no, so check next group
	MOVAB	FIBBLK, R0
	MOVL	#0,FIB$L_WCC(R0)
	MOVW	R4, DQF$L_UIC+2(R11)		; set group
	MOVW	#0, DQF$L_UIC(R11)		; ... wildcard member
	MOVW	#80,FAODESC			; set fao length to 80
	$FAO_S	-				; format ascii output
		CTRSTR = FFLINE, -		; control string
		OUTLEN = FAODESC, -		; output length
		OUTBUF = FAODESC		; output descriptor
	ERRORCK					; exit on error
	PUSHAL	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	MOVW	#80,FAODESC			; set fao length to 80
	$FAO_S	-				; format ascii output
		CTRSTR = HEADLINE, -		; control string
		OUTLEN = FAODESC, -		; output length
		OUTBUF = FAODESC		; output descriptor
	ERRORCK					; exit on error
	PUSHAL	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT

READ_QUOTA_2:
	MOVAB	DQFDESC,R0
	$QIOW_S	-				; do the read
		CHAN=DKCHAN, -			; disk channel
		FUNC=#IO$_ACPCONTROL, -		; ACP function
		IOSB=IOSB, -			; IO status block
		P1=FIBDESC, -			; FIB descriptor
		P2=#DQFDESC, -			; Disk Quota Descriptor-IN
		P4=#DQFDESC			; Disk Quota Descriptor-OUT
	ERRORCK					; exit on error
	MOVZWL	IOSB,R0				; get IOSB
	BLBS	R0,GOTO_CALC_2			; if 1, then continue
	BRB	HANDLE_ERRS_2

GOTO_CALC_2:
	BRW	CALC_USAGE_2

HANDLE_ERRS_2:
	CMPL	R0,#SS$_NODISKQUOTA		; ... compare to NODISKQUOTA
	BEQLU	GOTO_GET_NEXT_2			; ... and get the next group
	ERRORCK					; exit on error

GOTO_GET_NEXT_2:
	CVTLF	TOTAL_USED, R6
	CVTLF	TOTAL_PERM, R7
	DIVF3	R7, R6, R8			; get % of quota used
	MULF3	#^F100, R8, R6
	CVTFL	R6, R7
	MOVL	TOTAL_USED, R9
	MOVL	TOTAL_PERM, R8
	MOVL	TOTAL_EXTD, R6
	MOVW	#80,FAODESC			; set fao length to 80
	$FAO_S	-				; format ascii output
		CTRSTR = TOT2LINE, -		; control string
		OUTLEN = FAODESC, -		; output length
		OUTBUF = FAODESC, -		; output descriptor
		P1=R9, -
		P2=R7, -
		P3=R8, -
		P4=R6
	ERRORCK					;exit on error
	PUSHAL	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	MOVL	#0, TOTAL_USED
	MOVL	#0, TOTAL_PERM
	MOVL	#0, TOTAL_EXTD
	BRW	GET_NEXT_GROUP_2		; go to next group

GET_NEXT_MEMBER_2:
	BRW	READ_QUOTA_2

CALC_USAGE_2:
	MOVL	DQF$L_USAGE(R11),R1		; get blocks in use
	MOVL	DQF$L_OVERDRAFT(R11),R2		; and permanent quota
	CMPL	#^D999999,R2			; do they have unlimited quota?
	BEQL	GET_NEXT_MEMBER_2		; yes, skip print
	MOVL	DQF$L_PERMQUOTA(R11),R5		; and extended quota
	ADDL2	R1, TOTAL_USED
	ADDL2	R1, GRAND_USED
	ADDL2	R2, TOTAL_PERM
	ADDL2	R2, GRAND_PERM
	ADDL2	R5, TOTAL_EXTD
	ADDL2	R5, GRAND_EXTD
	CVTLF	R1, R6
	CVTLF	R2, R7
	DIVF3	R7, R6, R8			; get % of quota used
	MULF3	#^F100, R8, R6
	CVTFL	R6, R7
	BSBW	SEARCH_UAF			; search uaf for username
	MOVW	#80,FAODESC			; set fao length to 80
	$FAO_S	-				; format ascii output
		CTRSTR = FAOLINE, -		; control string
		OUTLEN = FAODESC, -		; output length
		OUTBUF = FAODESC, -		; output descriptor
		P1=DQF$L_UIC+2(R11), -		; group
		P2=DQF$L_UIC(R11), -		; member
		P3=#NAME_DESC, -		; user name
		P4=R1,-				; blocks used
		P5=R7,-				; % of base quota used
		P6=R2,-				; base quota
		P7=R5				; extended quota
	ERRORCK					; exit on error
	PUSHAL	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	BRW	READ_QUOTA_2

DONE:
	CVTLF	GRAND_USED, R6
	CVTLF	GRAND_PERM, R7
	DIVF3	R7, R6, R8			; get % of quota used
	MULF3	#^F100, R8, R6
	CVTFL	R6, R7
	MOVL	GRAND_USED, R9
	MOVL	GRAND_PERM, R8
	MOVL	GRAND_EXTD, R6
	MOVW	#80,FAODESC			; set fao length to 80
	$FAO_S	-				; format ascii output
		CTRSTR = TOT3LINE, -		; control string
		OUTLEN = FAODESC, -		; output length
		OUTBUF = FAODESC, -		; output descriptor
		P1=R9, -
		P2=R7, -
		P3=R8, -
		P4=R6
	ERRORCK					; exit on error
	PUSHAL	FAODESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	MOVZWL	#SS$_NORMAL,R0			; clear out R0
	$DASSGN_S CHAN=DKCHAN			; deassign disk
	ERRORCK					; exit on error
	MOVL	SAVER6,R6			; restore R6
	MOVL	SAVER7,R7			; restore R7
	$DISCONNECT	RAB=(R7)		; disconnect UAF rab
	$CLOSE		FAB=(R6)		; close UAF
	$EXIT_S	CODE=R0				; exit on error
	

SEARCH_UAF:
	MOVL	DQF$L_UIC(R11),UIC		; load uic to scratch area
	PUSHR	#^M<R0,R1,R2,R3,R4,R5,R6,R7,R8,R9,R10,R11> ; save the registers
	MOVL	SP,SAVESP			; save the Stack Pointer
	MOVL	SAVER6,R6			; restore R6
	MOVL	SAVER7,R7			; restore R7
	MOVC	#12,QMARKS,QRP$G_UAFREC		; move the question marks in
	MOVL	#RETRY_RLK,R3			; prepare for retries
	BRB	20$

10$:
	$SCHDWK_S DAYTIM=WAKEDELTA	; before retrying schedule wake up
	BLBC	R0,20$
	$HIBER_S			; if that worked we can take a nap

20$:
	$GET	RAB=(R7)		; try to get the record
	CMPL	R0,#RMS$_RLK		; was it locked?
	BNEQ	30$			; no, continue
	SOBGEQ	R3,10$			; yes, try again
	BRB	25$			; record locked, default user to ??????

30$:	
	CMPL	R0,#RMS$_RNF		; was the record found?
	BEQL	25$			; no, but act like it was
	ERRORCK				; exit if other errors

25$:
	MOVL	SAVESP,SP		; restore Stack Pointer
	POPR	#^M<R0,R1,R2,R3,R4,R5,R6,R7,R8,R9,R10,R11> ; restore registers
	RSB				; return

	.END	QREPORT
