Google
 

Trailing-Edge - PDP-10 Archives - BB-P363B-SM_1985 - mcb/drivers/cr.m11
There are no other files named cr.m11 in the archive.
.enabl	lc
.title	CR	CR11 LLC and Driver
.ident	"X02070"

;                    COPYRIGHT (c) 1980, 1981, 1982
;                    DIGITAL EQUIPMENT CORPORATION
;                        Maynard, Massachusetts
;
;     This software is furnished under a license and may  be  used
;     and copied only in accordance with the terms of such license
;     and with the inclusion of the above copyright notice.   This
;     software  or any other copies thereof may not be provided or
;     otherwise made available to any other person.  No  title  to
;     and ownership of the software is hereby transferred.
;
;     The information  in  this  software  is  subject  to  change
;     without  notice  and should not be construed as a commitment
;     by DIGITAL EQUIPMENT CORPORATION.
;
;     DIGITAL assumes no responsibility for the use or reliability
;     of  its  software  on  equipment  which  is  not supplied by
;     DIGITAL.
;
;
;
; This crock was originally foisted upon the  unsuspecting
;	world in June, 1978 by R. South who was preoccupied
;	by travels through the legendary dungeon of Mordatz Idu.
;
; X01010 thru X01170 omitted because following update constituted
;		such a major change that previous audit trail
;		was rendered meaningless - or at best useless.
;
; X02000	10-June-81 Buren Hoffman
;		Extensive modifications to correspond to MCB V3.0,
;		and a major facelift to aid maintainability.
;
; X02010	12-June-81	Buren Hoffman
;		Fixed old calls to $CCBGT and $CCBRT to use new
;		$CBBGT and $CBBRT.
;
; X02020	Added NOOP dispatches for Power-Failure and Initialization
;		entries in the Timer Dispatch table.
;
; X02030	1-July-81
;		General update to fix numerous bugs found via desk checking.
;
; X02040	8-July-81	Buren Hoffman
;		Call TIMPIN on initialization
;
; X02050	16-July-81	Buren Hoffman
;		General upgrade to include fixes incorporated into LE.
;
; x02060	10-sept-81	Ron Platukis
;		-fix bugs in interrupt and synch level code.
;
; x02070	2-december-81	Ron Platukis
;		reduce small buffer size from 80. to 60. bytes.
.sbttl	Macro Calls & Dispatch Tables

.mcall	NURDF$,CCBDF$,SYNDF$,SCDF$
.mcall	PHD$B,DSP$,DSP$B,DSP$E,PUSH$S,POP$S,MAP$,SMAP$,DAT$,END$
	NURDF$
	CCBDF$
	SYNDF$
	SCDF$

;+
;  Identify us to mcb
;+
	PHD$B	CR
	PHD$D	CRMCB
	PHD$E

.psect	$PLIT$,D,RO

;+
; Dispatch Tables
;-
CRMCB:	DSP$B
	DSP$				; Asynchronous trap
	DSP$	FC.XME			; Xmit enable
	DSP$	FC.RCE			; Receive enable
	DSP$	FC.KIL			; Kill
	DSP$	FC.CTL,CRCTL		; Control		(start / stop)
	DSP$	FC.TIM,CRTIM		; Timeout		(clock)
	DSP$	FC.XCP,CRDONE		; Xmit complete		(resource ret)
	DSP$	FC.RCP,CRDSP		; Receive complete	(arriving data)
	DSP$	FC.KCP			; Kill complete
	DSP$	FC.CCP			; Control complete
	DSP$E

;+
; Control Dispatch
;-
CRCTL:	CALLR	@1$(R3)
1$:	DSP$B
	DSP$	
	DSP$	FM.STR,DVOPEN		; Start device
	DSP$	FM.STP,DVCLOS		; Stop device
	DSP$E

;+
; Timer Dispatch
;-
CRTIM:	CALLR	@1$(R3)
1$:	DSP$B
	DSP$	FM.STM			;Short timer
	DSP$	FM.LTM,CLOCK		;Long timer
	DSP$	FM.PWF,NOOP		;Power failure
	DSP$	FM.PIN,TIMPIN		;Initialization
	DSP$E
.sbttl	Data Base
;+
; CR Data Base Offsets and Values
;-

	DAT$	DSR
C.TIMC:	.blkb	1		; TIMER BYTE
C.NRD:	.blkb	1		; NRD'S PIX
C.CSR:	CSR$W			; ONE WORD CSR POINTER
	DTXTSZ= 16.		; DATA/FEATURES PROCESS CONTEXT ALLOCATED
C.DTXT:	.blkw	DTXTSZ		; CONTEXT BLOCK FOR DA/FEATURES PROCESS
C.DTXL:	.blkb	1		; NO. WORDS ON STACK AT SLEEP TIME
	.even
C.DFSP:	.blkw	1		; STACK ORIGIN

C.SYN:	SYN$X			; SYNCH BLOCK
C.RST:	.blkw	1		; CR11 STATUS READ AT INTERRUPT
C.COLC:	.blkb	1		; PHYSICAL COLUMN COUNT
C.HUNG:	.blkb	1		; DEVICE HUNG TIMER
C.PUBP:	.blkw	2		; BUFFER PTR DOUBLEWORD
C.SGHD:	.blkw	1		; CURRENT SEGMENT HEADER ADDRESS
C.LDAT:	.blkw	1		; LAST DATA ITEM READ
C.PUBS:	.blkw	1		; BUFFER INPUT COUNTER
C.NRDA:	.blkw	1		; BEG OF NURD MESSAGE(ADDRESS)
C.DDMQ:	LST$D			; CCB'S QUEUED FOR INPUT
C.ASTS:	.blkw	1		; ACTION STATUS BIT MAP
	CA.CLO== 1		; CLOSED
	CA.PAU = 2		; PAUSED
	CA.ADN = 4		; ABORT WHEN DONE WITH DDM QUEUE
	CA.ABO = 10		; ABORTING
	CA.ABE = 20		; CLEAR ABORT ON EOF (ELSE ON CLEAR)
	CA.EOF = 40		; CURRENT MESSAGE HAS EOF
	CA.HOP = 100		; HOPPER CHECK - EMPTY INPUT OR FULL OUTPUT
	CA.MOT = 200		; MOTION CHECK - PICK, STACK, OR MOTION ERROR
	CA.TER = 400		; TIMING ERROR - DATA OVERRUN CONDITION
	   CA.ERR= CA.HOP!CA.MOT!CA.TER	; SUMMARY - HARD ERROR
	CA.OFL = 1000		; READER OFFLINE
	CA.IVP = 2000		; INVALID PUNCH - MULTIPUNCH
	CA.TMO = 4000		; DEVICE TIME OUT
	CA.syn = 10000		; synch request in progress flag
	CA.XOF = 100000		; READER XOF'D
C.CDCT:	.blkw	1		; PHYSICAL CARD COUNT

; MISCELLANEOUS ITEMS
C.IOCN:	.blkw	1		; OUTSTANDING IO COUNT
C.IOWT:	.blkw	1		; CLOSE WAIT FLAG = CLOSE CCB

; CONTROL MESSAGE PROCESSING DATA BASE
C.CMSQ:	LST$D			; CONTROL MESSAGE QUEUE
C.RECO:	.blkb	1		; CONTROL MESSAGE RESOURCE ERROR FLAG
C.RATT:	.blkb	1		; PENDING ATTENTION MESSAGE FLAG - REASON CODE
C.CAPP:	.blkb	1		; PENDING CAPABILITIES MESSAGE FLAG
C.STRT:	.blkb	1		; PENDING READ START

; DATA & FEATURES PROCESSING DATA BASE
C.DLCQ:	LST$D			; CCB'S QUEUED INTO DLC
C.MFLG:	.blkb	1		; CURRENT NURD MESSAGE FLAGS
C.CAPN:	.blkb	1		; CURRENT INDEX INTO CAPABILITIES LIST
C.NFSP:	.blkb	1		; NO. FEATURE SPECS IN CURRENT MSG
	.even
C.DMAD:	.blkw	2		; DATA MSG ADDRESS DOUBLEWORD (PTR)
C.DMBS:	.blkw	1		; DATA MSG BUFFER SIZE
C.DMSQ:	.blkb	2		; LAST GOOD, CURRENT DATA SEQ NUMBERS
C.PBRC:	.blkb	1		; TEMP BYTE
	.even
C.MSGQ:	LST$D			; OUTGOING MSG QUEUE
C.MSGA:	.blkw	2		; CURRENT MSG BUFFER ADDRESS DOUBLEWORD
C.MSGS:	.blkw	1		; CURRENT MSG BUFFER SIZE

; FEATURES
; LP11 COMMON FEATURE LIST
C.CFET:	.blkb	1			; NUMBER OF COMMON FEATURES
	.blkb	1			; FEATURE FE.DAT     SUPPORTED
	.blkb	1			; FEATURE FE.SER NOT SUPPORTED
	.blkb	1			; FEATURE FE.LCR NOT SUPPORTED
	.blkb	1			; FEATURE FE.FWD NOT SUPPORTED
	.blkb	1			; FEATURE FE.EOF     SUPPORTED
	.blkb	1			; FEATURE FE.DVT NOT SUPPORTED
	.blkb	1			; FEATURE FE.TRN NOT SUPPORTED
	.blkb	1			; FEATURE FE.FNM NOT SUPPORTED
	.blkb	1			; FEATURE FE.DWD     SUPPORTED
	CCFETL== .-C.CFET-1

C.DFET:	.blkb	1		; NUMBER OF DEVICE SPECIFIC FEATURES
	.blkb	1			; FEATURE 129    NOT DEFINED
	.blkb	1			; FEATURE CD.CWD     SUPPORTED
	CDFETL== .-C.DFET-1

; FEATURE BLOCKS
F.FSB = 0			; (BM)FEATURES STATUS BYTE
	FB.CL0 = 0			; CLASS 0 FEATURE => <BIT> FORM
	FB.CL1== 1			; CLASS 1 FEATURE => <CNT><...> FORM
	FB.WRD = 2			; FEATURE IS A WORD AND
					;  F.FCV IS A DATABASE OFFSET TO IT
	FB.STR = 4			; FEATURE IS A STRING
					; F.FCV IS THE LENGTH
					; CURRENT VALUE BEGINS AT F.FCV+1
					; STANDARD VALUE(IF ANY) BEGINS AT F.FCV+1+(F.FCV)
	FB.STE = 10			; STANDARD VALUE EXISTS
	FB.SST = 20			; STANDARD IS SET
	FB.CP = 40			; CHANGE PENDING
	FB.CA = 100			; CHANGE ALLOWED
F.FCV = 1			; FEATURE CURRENT VALUE BYTE
F.FSV = 2			; FEATURE STANDARD VALUE BYTE

; DATA MODE
CR.DAT::.blkb	1
	CRDATS== FB.CL1!FB.CA!FB.SST!FB.STE	; INITIAL STATUS OF LE.DOF LE.DAT
	.blkb	1				; ASCII
	.blkb	1				; ASCII IS STD

; EOF RECOGNITION
CR.EOF::.blkb	1
	CREOFS== FB.CL1!FB.CA		; INITIAL STATUS OF CR.EOF
	.blkb	1

; DEVICE WIDTH
CR.DWD::.blkb	1
	.blkb	1				; STD WIDTH

; DEVICE SPECIFIC FEATURES
; CARD WIDTH
CR.CWD::.blkb	1
	CRCWDS== FB.CL1!FB.CA!FB.STE!FB.SST	; INITIAL STATUS OF CR.CWD
	.blkb	1
	.blkb	1

	END$

.psect	$CODE$,I,RO
.sbttl	Start / Stop Device  (FC.CTL)
;+
; Initialize unit
;	R4	CCB
;	R5	Database address
;-
DVOPEN:	PUSH$S	R4			; START DEVICE
	MOVB	C.PIX(R4),C.NRD(R5)	; SAVE NRD'S PIX
	BIT	#CA.CLO,C.ASTS(R5)	; CLOSED?
	BEQ	11$			; NO, THEN HOW CAN IT BE OPENED?
	CLR	@C.CSR(R5)		; BE VERY PESSIMISTIC
	MOV	#CA.PAU,C.ASTS(R5)	; INIT THE ACTION STATUS WORD
	CLR	C.PUBP+2(R5)		; FLUSH CURRENT READ BUFFER
	CLRB	C.DTXL(R5)		; Clear saved context indicator
	CLR	C.DMAD(R5)		; FLUSH SOURCE BUF IN USE
	CLR	C.DMAD+2(R5)
	CLR	C.DMBS(R5)
	CLR	C.MSGA(R5)		; INIT CURRENT OUT MSG
	CLR	C.MSGA+2(R5)
	CLR	C.MSGS(R5)
	CLR	C.IOCN(R5)		; INIT OUTSTANDING IO COUNT
	CLR	C.IOWT(R5)		; INIT CLOSE WAIT FLAG
	CLRB	C.COLC(R5)		; INIT COLUMN COUNT
	CLRB	C.HUNG(R5)		; INIT HUNG DEVICE TIMER
	CLRB	C.RECO(R5)		; INIT CONTROL RESOURCE ERROR FLAG
	CLRB	C.RATT(R5)		; INIT PENDING ATTENTION MSG FLAG
	CLRB	C.CAPP(R5)		; INIT PENDING CAPABILITIES MSG FLAG
	CLRB	C.STRT(R5)		; INIT PENDING READ FLAG
	CLR	C.CDCT(R5)		; INIT CARD COUNTER

; Set all features with standards to std
	MOV	#C.CFET,R0		; COMMON FEATURES
	CALL	100$			; DOO ALL
	MOV	#C.DFET,R0		; DEVICE SPECIFIC FEATURES
	CALL	100$			; ALL OF THEM TOO
	MOV	#S.SSUC,R3		; RETURN SUCCESS
	INCB	(R5)			; START THE TIMER
	CALL	$LTMEN			; ...
	BR	20$

10$:	BIS	#CA.CLO,C.ASTS(R5)	; NOTE REMAINING CLOSED
11$:	MOV	#S.ERBO,R3		; FAILED TO OPEN
20$:	POP$S	R4			; RESTORE POINTER FOR COMMAND CCB
	BR	CTLXIT

; Feature setting routine
100$:	ADD	R5,R0			; R0/FEATURE LIST OFFSET
	CLR	R1
	BISB	(R0)+,R1		; GET NO. OF FEATURES
110$:	CLR	R3
	BISB	(R0)+,R3		; GET ADDRESS OF FEATURE BLOCK
	BEQ	120$			; EMPTY SLOT
	ADD	R5,R3
	CALL	SETSTD			; SET IT TO STD
120$:	SOB	R1,110$
NOOP:	RETURN
;+
; Stop Unit
;
;	R5	Database Address
;-

DVCLOS:	CALL	$CRSTP			; STOP DEVICE
	CLRB	(R5)			; STOP THE CLOCK
	BIS	#CA.CLO,C.ASTS(R5)	; MARK AS CLOSED
	CALL	Q$PURG			; FLUSH THE QUEUES
	PUSH$S	R4			; SAVE COMMAND CCB POINTER

10$:	DEQ$	C.CMSQ,20$		; FLUSH ANY OUTSTANDING CONTROL MSGS
	MOVB	#FC.RCE,C.FNC(R4)
	CALL	$SCHED
	BR	10$

20$:	POP$S	R4			; RESTORE COMMAND CCB POINTER
	MOV	R4,C.IOWT(R5)		; SET THE CLOSE WAIT FLAG

; COMPLETE CLOSE ONLY WHEN ALL CCB'S RETURNED
STOPWT:	TST	C.IOCN(R5)
	BEQ	10$
	INCB	(R5)
	RETURN

10$:	CLRB	(R5)			; FLUSH THE TIMER FCNS
	MOV	C.IOWT(R5),R4		; GET THE CLOSE CCB BACK
	CLR	C.IOWT(R5)		; CLEAN UP BEHIND OURSELVES
	MOV	#S.SSUC,R3		; CLOSE NEVER FAILS

CTLXIT:	MOV	R3,C.STS(R4)
	MOVB	#FC.CCP,C.FNC(R4)
	CALLR	$SCHED
.sbttl	Timer handler  (FC.TIM)
;+
;	R5	Database address
;-
CLOCK:	TST	C.IOWT(R5)		; CHECK FOR CLOSE-WAIT
	BEQ	5$
	CALLR	STOPWT			; PREEMPTS ALL ELSE

5$:	TSTB	C.RECO(R5)		; CHECK CONTROL RESOURCE ERROR
	BEQ	10$
	CLRB	C.RECO(R5)		; YES - FLUSH THE FLAG
	CALL	MSGFIN			; RESTART THE PROCESS

10$:	TSTB	C.DTXL(R5)		; Check timesleep
	BEQ	20$			; Nope
	CALL	ZZ$WAK			; Restart process - will return here
20$:	BIT	#CA.ERR!CA.OFL,C.ASTS(R5) ; CHECK DEVICE PROBLEMS
	BEQ	30$			; NOT BECAUSE OF DEVICE ERROR
	BIT	#RDROFL,@C.CSR(R5)	; DEVICE ERROR WAS REASON
	BNE	30$			; NOT YET RESTORED TO ONLINE
	CALL	SN$ALR			; CURE HS BEEN EFFECTED, NOTIFY USER
	BCS	30$			; COULDN'T SEND ONE, COME BACK LATER
	BIC	#CA.ERR!CA.OFL,C.ASTS(R5) ; FLUSH ERROR BUT LEAVE CA.PAU
					; USER MUST RESUME
30$:	TSTB	C.STRT(R5)		; CHECK PENDING READ START
	BEQ	40$
	CALL	$CRSTR			; START THE READER

40$:	TSTB	C.HUNG(R5)		; CHECK HUNG TIMER
	BEQ	50$			; NOT SET
	DECB	C.HUNG(R5)		; COUNT THE TOCK
	BNE	50$			; STILL OK
	BIS	#CA.TMO,C.ASTS(R5)	; DEVICE IS COMATOSE
	TSTB	C.RATT(R5)		; CHECK PENDING ATTENTION MSG
	BNE	55$			; YES - DON'T GENERATE ANOTHER
	MOVB	#N.ASTC,C.RATT(R5)	; SET PENDING ATTENTION FLAG

50$:	TSTB	C.RATT(R5)		; CHECK PENDING ATTENTION MSG
	BEQ	60$
55$:	CALL	SN$ATT			; TRY TO SEND ONE

60$:	TSTB	C.CAPP(R5)		; CHECK PENDING CAPABILITIES MSG
	BEQ	70$
	CALL	SN$CAP			; TRY TO SEND ONE

70$:	INCB	(R5)			; RESET THE CLOCK FOR THE NEXT TOCK
	RETURN
.sbttl	Resource return
;+
; Resource return
;-

CRDONE:	DEC	C.IOCN(R5)		; COUNT THE RETURNED RESOURCE
	BGE	10$
	CLR	C.IOCN(R5)		; BLEW IT SOMEWHERE

10$:	TST	C.PRM2(R4)		; Discern buffer type
	BEQ	20$			; Small data buffer
	CALLR	$RDBRT			; Deallocate RDB and return
20$:	MOV	#60.,R1			; Set buffer length
	CALLR	$CBBRT			; Release CCB and buffer
.sbttl	Message Received  (FC.RCP)
;+
; Dispatch according to the type of nurd message received.
;
;  Inputs:	R4 - Points to the first, or only CCB in chain
;		R5 - Points to CR database
;
;  Outputs:	Dispatches to message handler, or returns an
;		error if the type is illegal or out of range.
;		The called routine is responsible for queuing
;		messages to be returned to the higher level.
;
; Msg format:	<msgtype><msgflgs><msgtxt.....>
;-

CRDSP:	BIT	#CA.CLO,C.ASTS(R5)	; DEVICE CLOSED?
	BNE	60$			; YES, HENCE INACCESSIBLE
	MAP$	C.BUF(R4)		; GET MAPPING
	MOV	C.BUF+2(R4),R0		; POINT TO CCB DATA BUFFER
	MOVB	(R0),R1			; GET NURD MESSAGE TYPE
	BIC	#^C<NM.TYP>,R1		; ISOLATE JUST MESSAGE TYPE
	CMP	R1,#NRDOTR		; TYPE OUT OF RANGE?
	BLO	30$			; NO, OKAY TO DISPATCH
	MOV	#NRDOTR,R1		; YES, SPECIFY ILLEGAL NURD HANDLER
30$:	ASL	R1			; GET WORD OFFSET
	CALLR	@40$(R1)		; DISPATCH TO MESSAGE PROCESSOR



40$:	.word	50$			; 0 - DATA MESSAGE
	.word	50$			; 1 - ATTENTION (ILLEGAL IF REC'D)
	.word	MSGDAT			; 2 - FEATURES MESSAGE
	.word	MSGCTL			; 3 - CONTROL MESSAGE
 NRDOTR = <.-40$>/2
	.word	50$			; N - ILLEGAL NURD TYPE SPEC

50$:	MOV	#S.EIDM,C.STS(R4)
	BR	70$
60$:	MOV	#S.EABO,C.STS(R4)
70$:	MOVB	#FC.RCE,C.FNC(R4)
	CALLR	$SCHED
;+
; Queue incoming NURD Control and Features Messages
;
;	R4	CCB
;	R5	Database address
;-
MSGDAT:	BIT	#CA.ABO,C.ASTS(R5)	; CHECK FOR ABORT STATE
	BEQ	MSGQUE			; OK TO PROCEED
	MOV	#S.EABO,C.STS(R4)	; DROP DATA ON THE FLOOR
	MOVB	#FC.RCE,C.FNC(R4)
	CALLR	$SCHED

MSGQUE:	ENQ$	C.DLCQ			; QUEUE THE PACKET
	CMP	R4,C.DLCQ(R5)		; WAS QUEUE EMPTY ?
	BNE	60$			; NO, ROUTINES OF INTEREST ARE WORKING
	TSTB	C.DTXL(R5)		; Maybe waiting for packet ?
	BNE	60$			; Could be

30$:	MOV	SP,C.DFSP(R5)		; SET THE STACK ORIGIN FOR THIS PROCESS
	CALL	RD$BYT			; EXTRACT MSG TYPE
	MOV	R0,R1
	CALL	RD$BYT			; EXTRACT MSG FLAGS
	MOV	R0,C.MFLG(R5)
	CMPB	#NM.CTL,R1		; DISTINGUISH CONTROL AND FEATURES
	BNE	40$			; FEATURES
	CALL	CTLSYN			; NON-INTERRUPT CONTROL
	BR	50$

40$:	CALL	FTRSYN			; FEATURES
50$:	MOV	C.DLCQ(R5),R4		; GET NEXT MSG
	BNE	30$			; MORE
60$:	RETURN				; EXIT
;+
; NURD Control Message Handler
;
;	R0	Buffer ptr: ->nurd msg type
;	R4	CCB
;	R5	Database address
;	KISAR6	Mapped to buffer
;
; Control msg format:	<seq no.><command><result>
;-
.enabl	lsb
MSGCTL:	CMPB	C.MOD(R4),#S$INT	; CHECK FOR INTERRUPT MSG
	BEQ	5$			; THEY ARE PROCESSED NOW
	CALLR	MSGQUE			; OTHERS ARE SYNCH'D WITH DATA STREAM

5$:	ENQ$	C.CMSQ			; Queue CCB til resources are checked
	CMP	#N.CCMD+1,C.CNT(R4)	; CHECK TO SEE IF ENOUGH SENT
	BGT	100$			; NOT EVEN 4 LOUSY BYTES!
	MOVB	N.CCMD(R0),R1		; GET CONTROL COMMAND BYTE
	BLE	100$			; SKIP OUT ON ILLEGAL CODE
	CMP	#CTLOTR,R1		; CHECK RANGE OF COMMAND
	BLOS	100$			; OUT OF RANGE, SKIP OUT
	ASL	R1			; IN RANGE, GET AS WORD OFFSET
	CLR	R0			; INITIALIZE CODE
	CALL	@CTLDSP(R1)		; EXECUTE DETAILED CONTROL ROUTINE
	CMP	R0,#-1			; ANY MESSAGE TO DO ?
	BEQ	6$			; NO

; Here when finished, or when performing a retry to get a resource
MSGFIN:	CALL	BF$SDB			; GET A RESPONSE BUFFER
	BCC	10$			; Ok
	INCB	C.RECO(R5)		; Control resource error flag
6$:	RETURN

10$:	BCS	100$			; OOPS
	PUSH$S	R4			; SAVE THE RESPONSE CCB
	DEQ$	C.CMSQ			; GET THE MSG CCB BACK
	MAP$	C.BUF(R4)		; R0/RESULT
	MOV	C.BUF+2(R4),R1
	MOVB	N.CCMD(R1),R2		; R2/COMMAND
	MOVB	N.CSEQ(R1),R1		; R1/SEQ NO. OF CONTROL MSG
	CLR	C.STS(R4)		; ACK THE CONTROL MESSAGE
	MOVB	#FC.RCE,C.FNC(R4)	;  ...
	CALL	$SCHED			;   ...
	POP$S	R4			; GET THE RESPONSE CCB BACK
	MAP$	C.BUF(R4)		; MAP TO THE BUFFER
	MOV	C.BUF+2(R4),R3		; GET BUF PTR
	MOVB	#NM.CTL,(R3)+		; FORMAT A NURD CONTROL MSG
	CLRB	(R3)+			; NO MSG FLAGS
	MOVB	R1,(R3)+		; SEQ NO.
	MOVB	R2,(R3)+		; COMMAND
	MOVB	R0,(R3)			; RESULT
	MOV	#N.CRES+1,C.CNT(R4)	; SET THE BUFFER COUNT
	INC	C.IOCN(R5)		; COUNT THE I/O
	CALLR	$SCHED

; Illegal function
100$:	DEQ$	C.CMSQ			; GET THE CCB BACK
	BIS	#CA.ADN,C.ASTS(R5)	; PAUSE AND COMPLAIN
	MOV	#S.ERBO,C.STS(R4)	; REJECT(?) THIS ONE
	MOVB	#FC.RCE,C.FNC(R4)	; ACK THE MESSAGE
	MOV	#-1,R0			; SET FLAG CODE
	CALLR	$SCHED

; THE DISPATCH TABLE
CTLDSP:	.word	100$		;  0 - CODE ZERO IS RESERVED
	.word	CTLABE		;  1 - ABORT TO END OF FILE
	.word	CTLABT		;  2 - ABORT ALL
	.word	CTLABC		;  3 - ABORT CLEAR
	.word	CTLSTS		;  4 - STATUS REQUESTED, SEND ATTENTION
	.word	CTLDMP		;  5 - DUMP OUTPUT BUFFERS
	.word	CTLPAU		;  6 - PAUSE
	.word	CTLRSM		;  7 - RESUME FROM ERROR OR PAUSE
	.word	100$		;  8 - UNDEFINED
	.word	CTLCPB		;  9 - SEND CAPABILITIES MESSAGE
CTLOTR = <. - CTLDSP>/2
.dsabl	lsb
;+
; Non-interrupt control message processing
;-
CTLSYN:	CALL	RD$BYT			; GET SEQ NO.
	MOVB	R0,C.DMSQ(R5)		; SAVE IT FOR RESPONSE
	CALL	RD$BYT			; GET COMMAND
	MOV	R0,R1
	BLE	50$			; SKIP OUT ON ILLEGAL CODE
	CMP	#CTLOTR,R1		; CHECK RANGE OF COMMAND
	BLOS	50$			; OUT OF RANGE, SKIP OUT
	PUSH$S	R1			; SAVE COMMAND CODE FOR RESPONSE
	ASL	R1			; IN RANGE, GET AS WORD OFFSET
	CLR	R0			; INITIALIZE FLAG
	CALL	@CTLDSP(R1)		; EXECUTE DETAILED CONTROL ROUTINE
	POP$S	R1			; GET COMMAND CODE BACK
	CMP	R0,#-1			; ANY MESSAGE STILL TO DO ?
	BEQ	60$			; NO
	PUSH$S	R0			; SAVE RESPONSE CODE
	CALL	RD$DQX			; ACK THE MSG
	MOV	#NM.CTL,R0		; CONSTRUCT A CONTROL MSG
	CALL	MS$BYT			; CONTROL CODE
	CLR	R0
	CALL	MS$BYT			; NULL MSG FLAGS
	MOVB	C.DMSQ(R5),R0
	CALL	MS$BYT			; SEQ NO. OF ORIGINAL COMMAND
	MOV	R1,R0
	CALL	MS$BYT			; COMMAND
	POP$S	R0
	CALL	MS$BYT			; RESPONSE CODE
	DEQ$	C.MSGQ			; GET MESSAGE OFF QUEUE
	SUB	C.MSGS(R5),C.CNT(R4)	; CALCULATE MESSAGE SIZE
	CLR	C.MSGS(R5)		; COVER OUR TRACKS
	INC	C.IOCN(R5)		; COUNT THIS I/O
	CALL	$SCHED			; SEND IT OFF
	TSTB	C.RATT(R5)		; CHECK FOR OTHER MESSAGES TO GOE OUT
	BEQ	20$			; NO ATTENTION MSGS
	CALL	SN$ATT			; PENDING ATTENTION MSG

20$:	TSTB	C.CAPP(R5)		; TRY CAPABILITIES MSG
	BEQ	60$
	CALLR	SN$CAP			; PENDING CAPABILITIES MSG

50$:	BIS	#CA.ADN,C.ASTS(R5)	; PAUSE AND COMPLAIN
	CALL	RD$DQX			; ACK THE MSG AND WAIT FOR DEATH
60$:	RETURN
;+
; Clear abort status
;-
CTLABC:	BIT	#CA.ABE!CA.ABO,C.ASTS(R5) ; ABORT SET?
	BEQ	10$			; NO
	BIC	#CA.ABE!CA.ABO,C.ASTS(R5) ; CLEAR ABORTING
	RETURN

10$:	MOV	#NR.ACN,R0		; NOT IN ABORT STATE
	RETURN

;+
; Abort to end of file
;-
CTLABE:	BIS	#CA.ABE,C.ASTS(R5)	; SET THE FLAG FOR READER
	RETURN

;+
; Abort until clear received
;-
CTLABT:	CALL	$CRSTP			; FLUSH THE DEVICE
	BIS	#CA.ABO,C.ASTS(R5)	; NOTE ABORTING
	MOV	C.DDMQ(R5),R1		; CHECK ALL QUEUES TO SEE IF
	BIS	C.DLCQ(R5),R1		;  ANYTHING HAPPENING
	BIS	C.MSGQ(R5),R1
	BISB	C.DTXL(R5),R1		; CHECK SUSPENDED PROCESSES
	BNE	10$			; NR.ABS
	MOV	#NR.NAB,R0		; NOTHING TO ABORT
10$:	CALLR	Q$PURG			; CLEAR THE WORLD

;+
; Request capabilities message
;-
CTLCPB:	INCB	C.CAPP(R5)		; SET CAPABILITIES PENDING FLAG
	RETURN

;+
; Dump output buffers
;-
CTLDMP:	TSTB	C.RATT(R5)		; PENDING ATTENTION?
	BNE	10$			; YES
	TSTB	C.CAPP(R5)		; PENDING CAPABILITIES?
	BNE	10$			; YES
	MOV	#NR.NOB,R0		; NOTHING TO DUMP
10$:	RETURN
;+
; Pause input
;-
CTLPAU:	BIT	#CA.PAU!CA.ADN,C.ASTS(R5) ; ALREADY PAUSED/PAUSING?
	BNE	20$			; YES
10$:	BIS	#CA.PAU,C.ASTS(R5)	; PAUSE NO MATTER WHAT
	RETURN

20$:	MOV	#NR.PAU,R0		; ALREADY PAUSED
	RETURN


;+
; Resume input
;-
CTLRSM:	BIT	#CA.PAU!CA.ADN!CA.TMO,C.ASTS(R5); CHECK PAUSEDNESS
	BEQ	20$			; NOTHING HUNG
	BIT	#CA.EOF,C.ASTS(R5)	; EOF ?
	BEQ	10$
	CLR	C.CDCT(R5)		; IF SO, CLEAR CARD COUNT FOR NEW FILE

10$:	BIC	#CA.PAU!CA.ADN!CA.TMO!CA.ERR!CA.EOF!CA.ABO!CA.ABE,C.ASTS(R5)
	CALL	$CRSTR			; TURN ON DEVICE AGAIN
	BR	30$

20$:	MOV	#NR.NPS,R0		; NOT PAUSED
30$:	RETURN


;+
; Status request
;-
CTLSTS:	TSTB	C.RATT(R5)		; CHECK ATTENTION ALREADY PENDING
	BNE	10$			; DON'T MUNGE ORIGNAL REASON
	MOVB	#N.ARQS,C.RATT(R5)	; SET ATTENTION PENDING FLAG
10$:	RETURN
;+
; NURD Features message handlers
;
; Feature msg format:
;  <seq no.><no. feature specs><...feature specs...>
;
; Feature spec format:
;  <feature id><feature flags><class><response>[<feature data>]
;
; Feature data format:
;  <cnt><...data...>
;
; NOTE: Incoming message has only one feature spec.
;
; Buffer has been set up and nurd type and flags bytes
; (1st & 2nd bytes) have been extracted.
;
;	R4	CCB AT TOP OF C.DLCQ
;	R5	Database address
;	KISAR6	Mapped to data buffer
;-
.enabl	LSB
FTRSYN:	MOV	#NM.FTR,R0		; BEGIN A REPLY
	CALL	MS$BYT			; INSERT NURD MSG TYPE
	CLR	R0
	CALL	MS$BYT			; INSERT MSG FLAGS
	CALL	RD$BYT			; GET SEQ NO.
	MOVB	R0,C.DMSQ(R5)
	CALL	MS$BYT			; SEND IT BACK
	CALL	RD$BYT			; GET NO. FEATURE SPECS
	CLR	R0			;  BUT IGNORE IT
	MOVB	R0,C.NFSP(R5)		; SAVE IT FOR SOMTHING
	CALL	MS$BYT			; STUFF IT IN REPLY
	CALL	RD$BYT			; GET FEATURE ID
	MOV	R0,R2
	CALL	RD$BYT			; GET FEATURE FLAGS
	MOV	R0,R1
	CALL	RD$BYT			; GET CLASS
	SWAB	R1
	BIS	R0,R1			; R1/FLAGS,,CLASS

	CALL	RD$BYT			; READ THE RESPONSE FIELD AND IGNORE IT

	CLRB	C.CAPN(R5)		; INIT INDEX INTO CAPABILITIES LIST
	CMPB	#FE.ALL,R2		; CHECK FOR ALLNESS
	BNE	30$			; SINGLE FEATURE
	CLR	R2			; INIT COMMON FID'S
	MOVB	C.CFET(R5),C.CAPN(R5)	; GET COUNT OF COMMON ENTRIES
	BIT	#NF.CMD*400,R1		; ALL FEATURES - CHECK READ/SET
	BEQ	10$			; READ - CONTINUE
	CMPB	#FC.SST,R1		; SET - CHECK SET TO STD
	BNE	FERERR			; NO - ILLEGAL COMBINATION

10$:	INCB	R2			; ADVANCE THRU LIST
	BMI	15$			; GRUBBLING THRU DEVICE FEATURES
	CMPB	R2,C.CAPN(R5)		; CHECK RANGE
	BLOS	20$			; OK
	MOVB	C.DFET(R5),C.CAPN(R5)	; INIT FOR DEVICE SPECFIC FEATURES
	MOV	#201,R2
15$:	MOV	R2,R0			; DEVICE FEATURE - HACK OFF SIGN
	BIC	#^C177,R0
	CMPB	R0,C.CAPN(R5)		; CHECK RANGE
	BHI	70$			; DONE
20$:	CALL	FTRFND			; LOOK IT UP
	BCC	40$			; SUPPORTED FEATURE
	BR	10$			; TRY NEXT ONE

30$:	CALL	FTRFND			; LOOK UP FEATURE 
	BCS	FERUSF			; UNSUPPORTED FEATURE

; AT THIS POINT:
;	R1	Feature msg flags word(hi) + class(lo)
;	R2	Feature id (byte)
;	R3	Points to the feature status byte
;	R5	Database address
;	C.DMSQ	Seq no.
;	C.NFSP	No. specs

40$:	BITB	#FB.CP,(R3)		; CHECK FOR CHANGE PENDING
	BNE	FERCPN			; CAN'T READ OR SET IF SO
	BIT	#NF.CMD*400,R1		; FEATURE READ OR FEATURE SET?
	BEQ	FTRSHO			; READ - GO PROCESS FEATURE READ
	CALLR	FTRSET			; SET - GO PROCESS IN FEATURE SET
FTRCON:	TSTB	C.CAPN(R5)		; CHECK FOR ALLNESS
	BNE	10$			; YES

70$:	MOV	C.MSGQ(R5),R4		; GET BEG OF MSG
	MAP$	C.BUF(R4)
	MOV	C.BUF+2(R4),R0		; GET THE PTR
	MOVB	C.NFSP(R5),N.NSPC(R0)	; SET IN THE FINAL SPEC COUNT
	CALL	RD$DQX			; RETURN ORIGNAL MSG TO NRD
	DEQ$	C.MSGQ			; GET MESSAGE OFF QUEUE
	SUB	C.MSGS(R5),C.CNT(R4)	; CALCULATE MESSAGE SIZE
	CLR	C.MSGS(R5)		; COVER OUR TRACKS
	INC	C.IOCN(R5)		; COUNT THIS I/O
	CALLR	$SCHED			; SEND IT OFF


FERUSF:	PUSH$S	#FR.USF
	BR	200$
FERBCL:	PUSH$S	#FR.BCL
	BR	200$
FERNST:	PUSH$S	#FR.NST
	BR	200$
FERERR:	PUSH$S	#FR.ERR
	BR	200$
FERCPN:	PUSH$S	#FR.CPN
	BR	200$
FERNEB:	PUSH$S	#FR.NEB
	BR	200$
FERDNP:	PUSH$S	#FR.DNP
200$:	MOV	R2,R0			; INSERT <FID><FLAGS><CLASS><ERROR>
	CALL	MS$BYT			; INSERT FID
	MOV	R1,R0
	SWAB	R0
	CALL	MS$BYT			; INSERT FLAGS
	MOV	R1,R0
	CALL	MS$BYT			; INSERT CLASS
	POP$S	R0			; GET ERROR CODE
	CALL	MS$BYT			; INSERT RESP
	INCB	C.NFSP(R5)		; COUNT THE SPEC
	BR	FTRCON

.dsabl	lsb
;+
; Features Read
;
;R1	Feature msg flags word(hi) + class(lo)
;R2	Feature id (byte)
;R3	Points to the feature status byte
;R5	Database address
;
; Reply has been formatted thru <no. specs>
; insert in msg:
;	<fid><flgs><class><resp><data>
;-

FTRSHO:	MOV	R2,R0
	CALL	MS$BYT			; INSERT FID
	MOV	R1,R0			; GET READ/SET BIT
	SWAB	R0
	BIC	#^C<NF.CMD>,R0		; NOW ISOLATED
	BITB	#FB.SST,(R3)		; FEATURE SET TO STD?
	BEQ	10$			; NO
	BIS	#NF.STD,R0		; SET TO STD
10$:	CALL	MS$BYT			; INSERT FLAGS
	CLRB	R1			; SET FEATURE CLASS IN R1 (LO)
	BITB	#FB.CL1,(R3)		; CHECK CLASS 1
	BEQ	20$			; TIS CLASS 0
	BISB	#FC.CL1,R1		; SET CLASS 1
20$:	MOV	R1,R0
	CALL	MS$BYT			; INSERT CLASS

	CLR	R0
	CALL	MS$BYT			; RESP - NO ERRORS

	TSTB	R1			; CHECK CLASS FOR LENGTH OF DATA
	BEQ	30$			; 1 BIT
	BITB	#FB.WRD!FB.STR,(R3)	; MORE THAN 1 BYTE ?
	BNE	60$			; YES
	INC	R0			; COUNT = 1
	CALL	MS$BYT			; INSERT COUNT

30$:	MOVB	1(R3),R0		; COPY FEATURE TABLE DATA ENTRY
40$:	CALL	MS$BYT			; INSERT DATA
50$:	INCB	C.NFSP(R5)		; COUNT THE SPEC
	BR	FTRCON			; CONTINUE FEATURE HACKING

; MULTIPLE BYTE VALUE
60$:	BITB	#FB.STR,(R3)		; DELINEATE WORD(SPECIAL) AND LONGER STRINGS
	BNE	70$			; STRING 
; WORD VALUE - F.FCV = DATABASE OFFSET TO WORD 
	MOV	#2,R0			; 2 BYTE VALUE
	CALL	MS$BYT			; INSERT CNT

	CLR	R0
	BISB	F.FCV(R3),R0
	ADD	R5,R0
	MOV	(R0),R0			; YES, READ SETTING FROM THE DATABASE
	CALL	MS$BYT			; INSERT LO BYTE
	SWAB	R0
	BR	40$			; INSERT HI BYTE

; STRING - F.FCV HAS THE LENGTH, CURRENT VALUE BEGINS AT F.FCV+1
70$:	INC	R3			; THIS DEPENDS ON F.FCV=1
	MOVB	(R3)+,R0		; GET THE LENGTH
	CALL	MS$BYT			; INSERT IN MSG
	MOV	R0,R4			; SAVE IT
80$:	MOVB	(R3)+,R0		; INSERT THE STRING IN MSG
	CALL	MS$BYT
	DECB	R4			; COUNT THE BYTE
	BNE	80$
	BR	50$
;+
; Features set
;
;	R1	Feature msg flags word(hi) + class(lo)
;	R2	Feature id (byte)
;	R3	Points to the feature status byte
;	R5	Database address
; 
; Reply has been formatted thru <no. specs=1>
;-
FTRSET:	CMPB	#FC.SST,R1		; CHECK STDNESS
	BEQ	40$			; STD RESULT
	BITB	#FB.CA,(R3)		; REAL SET - CHECK IF SETABLE
	BEQ	FERERR			; NO
	MOVB	(R3),R0			; FEATURE IS SETTABE
	XOR	R1,R0			; CHECK CLASS SPEC
	BIC	#^C<FB.CL1>,R0		; R0/ <CLASS SPEC'D>.NOT.EQUIV. <FEATURE CLASS>
	BNE	FERBCL			; RESULT SHOULD BE 0
	CMPB	#FC.CL1,R1		; CHECK DATA CLASS
	BNE	10$			; CLASS 0
	CALL	RD$BYT			; CLASS 1 - FEATURE DEPENDENT DATA
					; CHECK EXPLICITLY FOR ALL > 1
	BITB	#FB.WRD!FB.STR,(R3)	; CHECK MULTIPLE BYTE DATA
	BNE	30$			; GO DO IT
					; ONLY ONE BYTE FEATURES LEFT
	CMPB	#1,R0			; SEE IF IT IS
	BNE	FERERR			; LOSER
					; YES - GO SET THE FEATURE
10$:	CALL	RD$BYT			; CLASS 0 OR CLASS 1(1 BYTE) - GET THE BYTE
	MOVB	R0,1(R3)		; SET FEATURE CURRENT VALUE
12$:	BICB	#FB.SST,(R3)		; NOT SET TO STD
	BR	FTRSHO			; NOW READ THE FEATURE

; MULTIPLE BYTE VALUES
30$:	BITB	#FB.STR,(R3)		; DELINEATE WORD(SPECIAL) AND LONGER STRINGS
	BNE	35$			; STRING VALUE
	CMPB	#2,R0			; 2 BYTE LENGTH
	BNE	36$			; SCREWED IT UP
	CALL	RD$BYT			; GET THE PAGE LIMIT
	MOVB	R0,C.PBRC(R5)		; HIDE THE LO PART 
	CALL	RD$BYT			; GET THE HI PART
	SWAB	R0
	BISB	C.PBRC(R5),R0		; R0/NEW WORD VALUE
	MOV	R0,-(SP)
	CLR	R0
	BISB	F.FCV(R3),R0
	ADD	R5,R0
	MOV	(SP)+,(R0)		; SET THE NEW VALUE IN WORD
	BR	12$

; STRING - F.FCV HAS THE LENGTH, CURRENT VALUE BEGINS AT F.FCV+1
35$:	PUSH$S	R3			; SAVE THE FEATURE BLOCK PTR
	INC	R3			; THIS DEPENDS ON F.FCV=1
	CMPB	R0,(R3)+		; CHECK THE LENGTH
	BEQ	37$			; OK
	POP$S	R3			; LENGTH ERROR - BACK PTR UP TO BEG OF BLOCK
36$:	CALLR	FERERR			; AND REPORT THE ERROR
37$:	PUSH$S	R1			; SAVE THE VARIABLES
	MOV	R0,R1			; R1/COUNT,R3/PTR TO BEG OF CURRENT VALUE
38$:	CALL	RD$BYT			; XFER THE NEW VALUE TO THE CURRENT VALUE
	MOVB	R0,(R3)+		; STUFF THE BYTE
	SOB	R1,38$			; AND COUNT IT
	POP$S	<R1,R3>
	CALLR	FTRSHO			; NOW READ THE FEATURE

40$:	CALL	SETSTD			; SET FEATURE TO STD
	BCC	45$
	CALLR	FERNST			; NO STD TO SET
45$:	CALLR	FTRCON			; DON'T READ IT
;+
; Set feature to its standard value
;
;	R3	Ptr to feature block in database
;	R5	Database address
;-
SETSTD:	BITB	#FB.STE,(R3)		; SEE IF IT HAS ONE
	BNE	10$			; YES
	SEC				; NO STD TO SET
	RETURN
10$:	BITB	#FB.WRD!FB.STR,(R3)	; CHECK MULTIPLE BYTE VALUES
	BNE	20$			; YES
	MOVB	F.FSV(R3),F.FCV(R3)	; SET CURRENT VALUE = STD VALUE
	BISB	#FB.SST,(R3)		; MARK FEATURE AS SET TO STD
	BR	50$

; MULTIPLE BYTE VALUE
20$:	BITB	#FB.STR,(R3)		; DELINEATE WORD AND LONGER STRINGS
	BNE	40$			; STRING
	PUSH$S	R5			; WORD VALUE
	CLR	-(SP)
	MOVB	F.FCV(R3),(SP)
	ADD	(SP)+,R5		; R5 /PTR TO CURRENT VALUE
	MOVB	F.FSV(R3),(R5)+		; XFER LO BYTE
	MOVB	F.FSV+1(R3),(R5)	; XFER HI BYTE
	POP$S	R5
	BR	50$

; STRING - F.FCV HAS THE LENGTH, CURRENT VALUE BEGINS AT F.FCV+1
; STD VALUE BEGINS AT F.FCV+1+(F.FCV)
40$:	PUSH$S	<R0,R1,R3>
	INC	R3			; DEPENDS ON F.FCV=1
	CLR	R0
	BISB	(R3)+,R0		; GET THE COUNT
	MOV	R0,R1
	ADD	R3,R1			; R0/COUNT
					; R1/SOURCE ADDRESS
					; R3/DESTINATION ADDRESS
45$:	MOVB	(R1)+,(R3)+		; XFER THE STD VALUE
	SOB	R0,45$			; COUNT IT
	POP$S	<R3,R1,R0>
50$:	CLC
	RETURN
;+
; FTRFND looks for a feature with fid in r2 and returns ptr
; to feature block in r3 if successful normal carry flag
; condition for success or failure
;-
FTRFND:	MOV	#C.CFET,R3		; TRY COMMON FEATURE 1ST
	PUSH$S	R2			; SAVE FID CAUSE IT WILL BE MANGLED
	TSTB	R2			; CHECK IF DEVICE SPECIFIC FTR
	BPL	10$			; COMMON, SKIP
	MOV	#C.DFET,R3		; DEVICE SPECIFIC, GET POINTER
	BIC	#177600,R2		; TRUNCATE TO JUST OFFSET ABOVE 128
10$:	BEQ	20$			; ZERO IS AN ILLEGAL FEATURE CODE
	ADD	R5,R3			; R3 -> FEATURE LIST IN DATABASE
	CMP	R2,(R3)			; CHECK ON ID RANGE
	BHIS	20$			; SKIP IF OUT OF RANGE
					; R3 IS POINTING TO THE BASE OF THE
					;   FEATURES LIST, WHICH HOLDS HIGHEST
					;   STORED FEATURE CODE
	ADD	R2,R3			; ADD THE FID TO POINT TO ADDRESS
					;   OF FEATURE BLOCK
	MOVB	(R3),R3			; ENTRY IS DB OFFSET TO FEATURE BLOCK
	BEQ	20$			; UNSUPPORTED FEATURE
	BIC	#^C377,R3		; FLUSH SIGN EXTENSION
	ADD	R5,R3			; R3 -> FEATURE BLOCK
	POP$S	R2
	CLC
	RETURN

20$:	POP$S	R2			; UNSUPPORTED FEATURE
	SEC
	RETURN
.sbttl	Support Routines
;+
; The following pages, up to start of interrupt handler code,
; contain the various support routines needed in CR.
;
; The routines are arranged in alphabetical order.
;-




;+
; Get a small data buffer
;-
BF$SDB:	PUSH$S	R1			; SAVE R1
	MOV	#60.,R1			; BUFFER SIZE WANTED
	CALL	$CBBGT			; GET A MESSAGE BUFFER
	BCS	10$			; LOSE
	MOV	R1,C.CNT(R4)		; SET SIZE
	CLR	C.PRM2(R4)		; IDENTIFY AS A MESSAGE BUFFER
	MOVB	#S$PEOM,C.PRM1+1(R4)	; INIT AS A WHOLE MSG
	MOVB	#FC.XME,C.FNC(R4)	; INIT FUNCTION AND MOD
	MOVB	#S$SND,C.MOD(R4)	; ...
	MOVB	C.NRD(R5),C.PIX(R4)	; SET NRD'S PIX
10$:	POP$S	R1			; RESTORE R1
	RETURN
;+
; NURD message builder
;
;	R0	Byte to put into buffer
;	R5	Database address
;-
MS$BYT:	DEC	C.MSGS(R5)		; COUNT IT
	BLT	20$			; BUFFER ALREADY FULL
	MAP$	C.MSGA(R5)		; MAP TO BUFFER
	MOVB	R0,@C.MSGA+2(R5)	; INSERT CHAR
	INC	C.MSGA+2(R5)		; ADVANCE PTR
	RETURN

20$:	CLR	C.MSGS(R5)		; RESET THE COUNT
25$:	PUSH$S	R4
	CALL	BF$SDB			; GET A BUFFER
	BCS	30$			; OOPS
	CLR	C.STS(R4)		; INIT THE STATUS
	MOV	C.BUF(R4),C.MSGA(R5)	; MAKE IT THE CURRENT BUFFER
	MOV	C.BUF+2(R4),C.MSGA+2(R5)
	MOV	C.CNT(R4),C.MSGS(R5)
	ENQ$	C.MSGQ			; ADD IT TO END OF LIST
	POP$S	R4
	BR	MS$BYT			; NOW STUFF THE CHAR

30$:	POP$S	R4
	CALL	ZZ$SLP			; ZZZzzz
	BR	25$
;+
; Purge the queues
;
;   Abort all from  <DDMQ>, <DLCQ>, and <MSGQ>   (in that order).
;-
Q$PURG:	PUSH$S	<R0,R1,R3,R4>		; SAVE CURRENT CCB, DATABASE ADDR, ETC
	CALL	$CRSTP			; Stop the reader

10$:	DEQ$	C.DDMQ,20$		; RETURN CURRENT READ BUFFERS TO POOL
	CALL	$RDBRT			; ...
	BR	10$

20$:	DEQ$	C.DLCQ,30$		; ABORT ANY MSGS WAITING TO BE DONE
	MOV	#S.EABO,C.STS(R4)
	MOVB	#FC.RCE,C.FNC(R4)
	CALL	$SCHED
	BR	20$

30$:	DEQ$	C.MSGQ,40$		; FLUSH ANYTHING GOING OUT
	MOV	#60.,R1
	CALL	$CBBRT			; ...
	BR	30$

40$:	CLRB	C.DTXL(R5)		; Clear any suspended processing
	CLR	C.PUBP+2(R5)		; NO CURRENT READ BUFFER
	CLR	C.DMAD+2(R5)		; NO CURRENT SOURCE BUFFER
	CLR	C.DMAD(R5)
	CLR	C.DMBS(R5)		; MAY HAVE BEEN A MESSAGE IN PROGRESS
	CLR	C.MSGS(R5)		; ...

	POP$S	<R4,R3,R1,R0>		; DONE
	RETURN
.sbttl	Get and Put Data Bytes
;+
; USED BY "DATA" AND BY "FTRSYN".
; A FTRSYN MSG IS SHUFFLED THRU DDM (AS AN EMPTY BUFFER) SO THAT
; IT IS RETURNED IN THE SAME SEQUENCE RELATIVE TO DATA THAT IT
; WAS RECEIVED.
;-
RD$BYT:	DEC	C.DMBS(R5)		; DECREMENT COUNTER
	BLT	5$			; BUFFER EMPTIED ALREADY
	MAP$	C.DMAD(R5)		; MAP IF NEED BE
	CLR	R0
	BISB	@C.DMAD+2(R5),R0	; GET BYTE FROM PHYSICAL BUFFER
	INC	C.DMAD+2(R5)		; INCREMENT POINTER
	RETURN

5$:	TST	C.DMAD+2(R5)		; IS A BUFFER IN PROCESS?
	BEQ	10$			; NO BUF IN USE
	MOV	C.DLCQ(R5),R4		; CHECK FOR MSG CONTINUITY
	BITB	#S$PEOM,C.PRM1+1(R4)
	BEQ	9$	
	BIS	#CA.ADN,C.ASTS(R5)	; PAUSE AND COMPLAIN
9$:	CALL	RD$DQX			; EMPTIED BUF ON TOP
	BIT	#S$PEOM,R0		; CHECK STATUS OF MSG DEQUEUED
	BEQ	10$
	MOV	C.DFSP(R5),SP		; GET THE STACK ORIGIN BACK SO CAN QUIT
	RETURN				; EXIT

10$:	MOV	C.DLCQ(R5),R4		; IS THERE A QUEUED ENTRY?
	BNE	15$			; YES, SET UP POINTER AND COUNTER
	CALL	ZZ$SLP			; Wait til next segment arrives
	BR	10$

15$:	MOV	C.BUF(R4),C.DMAD(R5)	; GET ADDRESS DOUBLEWORD AS PTR
	MOV	C.BUF+2(R4),C.DMAD+2(R5)
	MOV	C.CNT(R4),C.DMBS(R5)	; GET BUFFER COUNT AS SIZE COUNTER
	BR	RD$BYT			; TRY AGAIN


;+
; Dequeue and return a source buffer to NRD
;-
RD$DQX:	CLR	C.DMAD+2(R5)		; INDICATE "NO BUFFER IN USE"
	CLR	C.DMBS(R5)
	DEQ$	C.DLCQ			; DEQUEUE MSG JUST DONE
	PUSH$S	C.STS(R4)		; SAVE MSG STAUS
	CLR	C.STS(R4)		;  AND ACK IT
	MOVB	#FC.RCE,C.FNC(R4)	;   ...
	CALL	$SCHED
	POP$S	R0
	RETURN
;+
; Send alert message
;-
SN$ALR:	CALL	BF$SDB			; GET A MESSAGE BUFFER
	BCS	10$			; COULDN'T
	MOV	C.BUF+2(R4),R1		; GET BUFFER PTR
	MOVB	#NM.ALR,(R1)+		; BUILD ALERT MSG IN SINGLE WORD
	CLRB	(R1)+			; CLEAR NURD MSG FLGS
	MOV	#2,C.CNT(R4)
	MOVb	#S$SNI,C.MOD(R4)	; ALERT IS AN INTERRUPT MSG
	INC	C.IOCN(R5)
	CALLR	$SCHED
10$:	RETURN



;+
; Send attention message
;
; Allocate an sdb and build an attention message.
; Reason code is in C.RATT.
; 
;  format: <1><0><last seq #><rsn code><flags: 1-3 bytes><card count: 2 bytes>
;-
SN$ATT:	PUSH$S	<R0,R1,R2,R3,R4>	; PRESERVE THE REGISTERS
	MOVB	C.RATT(R5),R2		; GET EXCUSE CODE FOR ATTENTION MSG
	BEQ	110$			; NO ATTENTION IS PENDING, LEAVE

; BUILD AND SEND AN ATTENTION MESSAGE
	CALL	BF$SDB			; GET A MESSAGE BUFFER
	BCS	110$			; FAILED, EXIT - WE'LL BE BACK
	MOV	C.BUF+2(R4),R1		; GET POINTER TO START OF BUFFER
	MOVB	#NM.ATT,(R1)+		; LOAD MSG TYPE TO BUFFER
	CLRB	(R1)+			; CLEAR NURD MSG FLGS
	MOVB	C.CDCT(R5),(R1)+	; STORE LAST GOOD SEQ NUMBER IN ATTENTION
	MOVB	R2,(R1)+		; LOAD ATTENTION CODE
					; BUILD FLAGS FIELD
	CLR	R2			; CLEAR FLAGS ACCUMULATOR
	MOV	C.ASTS(R5),R0		; GET DEVICE STATUS
	BIT	#CA.ADN!CA.MOT!CA.TER,R0 ; FATAL ERRORS ?
	BEQ	10$
	BIS	#NA.FAT,R2		; SOME FATAL ERROR - USUALLY USER'S

10$:	BIT	#CA.OFL,R0		; READER OFFLINE ?
	BEQ	11$			; NO
	BIS	#NA.OFL,R2		; YES, NOTE OFFLINE

11$:	BIT	#CA.PAU,R0		; PAUSED?
	BEQ	12$			; NO
	BIS	#NA.PAU,R2		; YES

12$:	BIT	#CA.HOP,R0		; HOPPER EMPTY ?
	BEQ	20$
	BIS	#NA.OMD,R2		; MAYBE - SETS NA.OUF ALSO

20$:	BIS	#200,R2			; TENTATIVELY SET EXTEND FLAG
	MOVB	R2,(R1)+		; THAT'S IT FOR BYTE 1 FLAGS
	CLR	R2			; CLEAR FOR BYTE 2 FLAGS
	BIT	#CA.HOP,R0		; OUTPUT HOPPER FULL ?
	BEQ	21$
	BIS	#NA.OUF,R2		; MAYBE

21$:	BIT	#CA.TMO,R0		; HUNG?
	BEQ	22$			; NO
	BIS	#NA.DTO,R2		; YES

22$:	BIT	#CA.MOT,R0		; PICK FAILURE ?
	BEQ	30$
	BIS	#NA.PF,R2		; MAYBE

30$:	BIS	#200,R2			; TENTATIVELY SET EXTEND FLAG
	MOVB	R2,(R1)+		; THAT'S IT FOR BYTE 2
	CLR	R2			; CLEAR FOR BYTE 3 FLAGS
	BIT	#CA.IVP,R0		; INVALID PUNCH ?
	BEQ	40$
	BIS	#NA.IVP,R2		; YES

40$:	MOVB	R2,(R1)			; THAT'S IT FOR BYTE 3 FLAGS
	BNE	50$			; 3 BYTE FEILD
	BICB	#200,-(R1)		; NOT MORE THAN 2 BYTES
	BNE	50$			; 2 BYTER
	BICB	#200,-(R1)		; ONLY 1 BYTE

50$:	INC	R1			; ADVANCE R1 TO NEXT AVAILABLE BYTE
	MOV	C.CDCT(R5),R3		; GET CARD COUNT ACCUMULATOR
	MOVB	R3,(R1)+		; STORE LOW BYTE OF COUNT
	SWAB	R3			; GET HIGH BYTE
	MOVB	R3,(R1)+		; STORE IT TOO
	SUB	C.BUF+2(R4),R1		; COMPUTE MSG SIZE
	MOV	R1,C.CNT(R4)		; STUFF IT INTO CCB
	INC	C.IOCN(R5)
	CALL	$SCHED
	CLRB	C.RATT(R5)		; CLEAR PENDING REQUEST FOR ATTENTION
110$:	POP$S	<R4,R3,R2,R1,R0>	; RESTORE REGS
	RETURN				; EXIT SEND ATTENTION
;+
; Send capabilities message
;
; message format: <no. features><...fid's...>
;-
SN$CAP:	CALL	BF$SDB			; GET A MSG BUFFER
	BCS	100$
	MOV	C.BUF+2(R4),R3		; GET BUFFER ADDRESS
					; FORMAT A NURD MESSAGE
	MOVB	#NM.CAP,(R3)+		; NURD MSG TYPE = CAPABILITIES
	CLRB	(R3)+			; NO FLAGS
	PUSH$S	R3			; SAVE PTR TO CNT
	CLRB	(R3)+			; INIT CNT
	MOV	#3,C.CNT(R4)		; SET BUFFER CNT = BYTES INSERTED
	PUSH$S	R5			; SAVE THE DATABASE PTR
	ADD	#C.CFET,R5		; GET R5 -> COMMON FEATURES LIST
	CLR	R1			; INIT THE FID

10$:	CLR	R0			; GET LENGTH OF FEATURE LIST
	BISB	(R5)+,R0
	BEQ	31$			; NO FEATURES!!

20$:	INC	R1			; ADVANCE THE FID
	TSTB	(R5)+			; CHECK FOR SUPPORT OF IT
	BEQ	30$			; NOPE
	MOVB	R1,(R3)+		; YES - STORE FID IN MSG
	INC	C.CNT(R4)		; COUNT IT
30$:	SOB	R0,20$

31$:	TSTB	R1			; CHECK WHICH FEATURE LIST
	BMI	40$			; DEVICE SPECIFIC - DONE
	MOV	#200,R1			; INIT FID FOR DEVICE SPECIFIC FEATURES
	BR	10$			; PROCESS THAT LIST

40$:	POP$S	R5			; GET THE DATABASE PTR BACK
	MOV	C.CNT(R4),R0		; GET THE BUFFER CNT
	SUB	#3,R0			; CALC NO. FEATURES FOUND
	MOVB	R0,@(SP)+		; STORE CNT IN CNT BYTE OF MSG
	INC	C.IOCN(R5)
	CALL	$SCHED
	CLRB	C.CAPP(R5)		; FLUSH THE FLAG
100$:	RETURN
;+
; Data / Features hiber function
; Called by process to wait for a clock tick.
;-
ZZ$SLP:	MOV	R0,C.DTXT(R5)		; STORE THE REGISTERS 1ST
	MOV	#C.DTXT+2,R0
	ADD	R5,R0			; R0 -> R1 SLOT
	MOV	R1,(R0)+
	MOV	R2,(R0)+
	MOV	R3,(R0)+
	MOV	R4,(R0)+
	CLRB	C.DTXL(R5)		; INIT THE COUNT

10$:	CMPB	#DTXTSZ-5,C.DTXL(R5)	; CHECK INCIPIENT OVERFLOW
	BHI	20$
	BPT				; PROGRAM BUG
20$:	MOV	(SP)+,(R0)+		; XFER NEXT STACK WORD
	INCB	C.DTXL(R5)		; COUNT IT
	CMP	C.DFSP(R5),SP		; CHECK ORIGIN
	BNE	10$			; MORE
	RETURN				; DONE


;+
; Clock has ticked.
;-
ZZ$WAK:	MOV	SP,C.DFSP(R5)		; Set the new stack origin
	CLR	R0			; Get the number of 
	BISB	C.DTXL(R5),R0		;   words to return to stack
	ASL	R0			;   as word offset
	ADD	#C.DTXT+12,R0		; This is where they are at
	ADD	R5,R0			; R0 -> 1st word to return to stack

10$:	MOV	-(R0),-(SP)		; Xfer the stack contents
	DECB	C.DTXL(R5)		; Count them
	BNE	10$

	MOV	-(R0),R4		; Restore the registers
	MOV	-(R0),R3
	MOV	-(R0),R2
	MOV	-(R0),R1
	MOV	-(R0),R0
	RETURN				; RETURN TO ORIGINAL CALLER OF ZZ$SLP
.sbttl	Interrupt Service
;+
; Configuration stuff
;-
	CRHNG	=	5		; HUNG DEVICE TIME CONSTANT
	RDAHD	=	5		; C.IOCN LIMIT WHEN CA.XOF SET

;+
; CR11 Hardware device register definitions
;-
	CRS	= 0		; CARD READER STATUS REGISTER

	READ	=	1	; (W)  FEED A CARD TO THE READ STATION
	EJECT	=	2	; (R/W) INHIBIT COLUMN DONE & TIMERR
	INTENB	=	100	; (R/W) INTERRUPT ENABLE
				;      (ERROR,CARD DONE,COLUMN DONE,
				; 	 OFFLINE OR ONLINE TRANSITION)
	COLDN	=	200	; (R:I) COLUMN DONE - DATA READY
	RDROFL	=	400	; (R)   READER IS OFFLINE
	BUSY	=	1000	; (R)   READER BUSY
	RDRONL	=	2000	; (R:I)   READER TRANSITION TO ONLINE
	TIMERR	=	4000	; (R)   TIMING ERROR - SERVICE TOO SLOW
	MOCHK	=	10000	; (R)   MOTION CHECK -FORCES OFFLINE
				;       (FEED ERROR,MOTION ERROR,STACK FAIL)
	HOCHK	=	20000	; (R)   HOPPER CHECK - INPUT EMPTY/OUTPUT FULL
				; 	FORCES OFFLINE
	CARDN	=	40000	; (R:I) CARD DONE
	ERROR	=	100000	; (R:I) SUMMARY ERROR BIT
				; 	SET BY:	TRANSITION TO OFFLINE
				; 		TIMING ERROR AT CARD DONE TIME

	CRB1	=	CRS+2	; IMAGE DATA REGISTER
				;  BIT 11   = ZONE 12
				;  BIT 10   = ZONE 11
				;  BIT  9   = ZONE  0
				;  BITS 8-0 = ROWS 1-9

	CRB2	=	CRB1+2	; COMPRESSED DATA REGISTER
				;  BIT 7    = ZONE 12
				;  BIT 6    = ZONE 11
				;  BIT 5    = ZONE  0
				;  BIT 4    = ROW   9
				;  BIT 3    = ROW   8
				;  BITS 2-0 = ROWS 1-7 ENCODED(SINGLE PUNCH)
				; 	MULTIPUNCH IN 1-7 OR'S
;+
; Start reader
;-
$CRSTR:	BIT	#CA.PAU!CA.EOF,C.ASTS(R5) ; START THE CARD READER
	BNE	6$			; CAN'T YET
	CMP	#RDAHD,C.IOCN(R5)	; YES - CHECK READ AHEAD LIMIT
	BLE	6$			; STOP READING

5$:	TST	C.DDMQ(R5)		; CHECK FOR BUFFERS
	BNE	10$			; YES - CAN PROCEED
	CALL	$RDBGT			; GET A BUFFER
	BCC	7$			; OK
6$:	MOVB	#1,C.STRT(R5)		; SET PENDING READ
	RETURN				; WAIT TIL ANOTHER TIME

7$:	MOVB	#S$PEOM,C.PRM1+1(R4)
	MOV	#-1,C.PRM2(R4)		; Identify as an RDB
	MOV	.RDBSZ,C.CNT(R4)	; Set size
	MOVB	#FC.XME,C.FNC(R4)
	MOVB	#S$SND,C.MOD(R4)
	MOVB	C.NRD(R5),C.PIX(R4)
	ENQ$	C.DDMQ			; Queue it
10$:	CLR	C.PUBP+2(R5)		; INIT THINGS TO CAUSE COLD STARTUP
	CLRB	C.STRT(R5)		; FLUSH THE PENDING READ
	CLRB	C.COLC(R5)		; CLEAR COLUMN COUNT FOR CLEAN START
	BIC	#CA.ERR!CA.OFL!CA.TMO!CA.IVP,C.ASTS(R5)
					; FLUSH CONDITIONS OF LAST CARD


$CRGO:	MOVB	#CRHNG,C.HUNG(R5)	; SET THE HUNG TIMER
	MOV	#INTENB!READ,@C.CSR(R5)	; GOOSE IT
	RETURN

$CRSTP:	CLR	@C.CSR(R5)		; STOP THE INTERRUPTS
	CLRB	C.HUNG(R5)		; AND FLUSH THE HUNG TIMER
	CLRB	C.STRT(R5)		; FLUSH ANY PENDING READS
	RETURN
;+
; Interrupt service routine
;
;	R5	Database address
; 
; NURD msg format:  <0><msg flags><seq><data flags><seg cnt><...segs...>
; 
; Segment format:   <cnt><...cnt data items> or <200!cnt><data item>
; EOR segment:      <0>
;-
$CRINT::
	CLRB	C.HUNG(R5)		; Make sure hung timer is off
	SMAP$S
	PUSH$S	<R0,R1,R2,R3>
	MOV	C.CSR(R5),R3		; R3/CSR ADR
	MOV	(R3)+,C.RST(R5)		; READ THE STATUS
	BLE	25$
	BIT	#COLDN,C.RST(R5)	; CHECK COLUMN DONE
	BNE	20$
	CALLR	CRDEND			; MUST BE CARD DONE

; COLUMN DONE - READ DATA
20$:	DECB	C.COLC(R5)		; COUNT THE COLUMN
	BNE	30$
25$:	CALLR	INTERR			; READING BEYOND DEVICE WIDTH

30$:	MOV	(R3)+,R0		; R0/IMAGE DATA
	TSTB	CR.EOF+F.FCV(R5)	; CHECK EOF?
	BEQ	50$			; NOT INTERESTED
	BIT	#CA.EOF,C.ASTS(R5)	; EOF ALREADY ENCOUNTERED ?
	BEQ	40$			; NO
	JMP	190$			; YES, SKIP JUNK

40$:	CMP	#EOFASC,R0		; CHECK FOR EOF PUNCH
	BNE	50$
	TST	C.PUBP+2(R5)		; CHECK COLUMN 1
	BNE	50$
	BIS	#CA.EOF!CA.PAU,C.ASTS(R5) ; SET EOF AND PAUSE
	CLR	-(SP)			; PUT A FAKE DATA MODE INDICATOR ON
	BR	100$			;   STACK FOR LATER CODE TO CLEAN OFF

50$:	MOVB	CR.DAT+F.FCV(R5),R4	; CHECK DATA MODE
	SUB	#DM.CLI,R4		; CREATE 3 STATE FLAG FOR LATER USE
	MOV	R4,-(SP)		; TOP OF STACK = DATA MODE FLAG:
					; 	< 0 => ASCII MODE
					; 	= 0 => COLUMN IMAGE MODE
					; 	> 0 => AUGMENTED COLUMN IMAGE MODE
	BEQ	100$			; IMAGE
	MOV	(R3)+,R1		; R1/CODED DATA
	MOV	R1,R2
	BIC	#^C7,R2
	ASL	R2			; MAKE WORD OFFSET
	BIC	#7,R1
	ASR	R1
	ASR	R1			; R1/Z<12,11,0,9,8>*2, R2/<Z1-Z7 CODE>*2
	TST	(SP)			; CHECK DATA MODE
	BGE	80$			; AUGMENTED COLUMN IMAGE MODE
; ASCII MODE
	ROR	R0			; CHECK FOR MULTIPUNCH
	CLC
	RORB	R0			; LO R0/0+Z1-Z7
	BICB	ZTAB(R2),R0		; FLUSH SINGLE ZONE
	BNE	60$			; NONZERO => MULTIPUNCH
	MOV	ZONTAB(R1),R1		; GET TABLET
	BNE	70$			; ZONES OK
					; ILLEGAL ZONE COMBINATION
60$:	MOV	#ERRCHR,R0		; ILLEGAL CHARACTER
	BIS	#CA.IVP,C.ASTS(R5)	; SET INVALID PUNCH FLAG
	BR	100$

70$:	ASR	R2			; GET BYTE OFFSET BACK
	ADD	R2,R1			; R1/TABLET ENTRY
	CLR	R0			; MAKE FULL WORD DATA ITEM
	BISB	(R1),R0			; R0/TRANSLATED CHARACTER
	BR	100$

; AUGMENTED COLUMN IMAGE
80$:	MOV	R0,R4			; CHECK ERRORS
	ROR	R4
	CLC
	RORB	R4			; LO R4/0+Z1-Z7
	BICB	ZTAB(R2),R4		; FLUSH ZONE BIT
	BEQ	90$			; ZERO => SINGLE PUNCH
	BIS	#100000,R0		; MULTIPUNCH
	BIS	#CA.IVP,C.ASTS(R5)	; SET INVALID PUNCH FLAG
90$:	SWAB	R0			; STUFF Z1-Z7 CODE IN UPPER HALF
	BISB	ZTAB+1(R2),R0
	SWAB	R0

; WE NOW HAVE THE DECODED CHARACTER
; COLUMN IMAGE
100$:	MAP$	C.PUBP(R5)		; MAP THE BUFFER
	MOV	C.PUBP+2(R5),R3		; R3/CURRENT BUFFER PTR
	BEQ	110$			; 1ST COLUMN
	MOV	C.SGHD(R5),R2		; GET SEGMENT HEAD ADR
	BEQ	120$			; NONE IN PROGRESS
	TSTB	(R2)
	BPL	130$			; CHECK FOR COMPRESSION

; COMPRESSED SEGMENT
	CMP	R0,C.LDAT(R5)		; CHECK CONTINUITY
	BNE	120$			; END OF SEQUENCE
	INCB	(R2)			; COUNT ANOTHER - ASSUME 127 COL LIMIT HERE
	BR	180$
; NEW CARD, SO SET UP THE BUFFER
110$:	MOV	C.DDMQ(R5),R3		; 1ST COLUMN - SET UP PTRS
	MOV	C.BUF+2(R3),C.PUBP+2(R5); BUFFER ADDRESS
	MOV	C.BUF(R3),C.PUBP(R5)	; BUFFER  VIRTUAL  ADDRESS
	MAP$	C.BUF(R3)
	MOV	C.PUBP+2(R5),R3		; GET R3/BUFFER ADDRESS
	MOV	R3,C.NRDA(R5)		; SAVE BEG OF NURD MSG
	MOVB	CR.DWD+F.FCV(R5),C.COLC(R5) ; SET COLUMN COUNTER

; FORMAT A NURD MESSAGE
	MOVB	#NM.DAT,(R3)+		; MSGTYPE = DATA
	CLRB	(R3)+			; NO MSG FLAGS
	INCB	C.CDCT(R5)		; COUNT THE CARD
	MOVB	C.CDCT(R5),(R3)+	; INSERT SEQ NO. = LO CARD COUNT
	CLRB	(R3)+			; INIT DATA FLAGS
	CLRB	(R3)+			; INIT SEGMENT COUNT
	CLR	C.SGHD(R5)		; FLUSH C.SGHD
	CLR	C.LDAT(R5)		; FLUSH C.LDAT
	MOV	#N.DDAT,C.PUBS(R5)	; SET BUFFER COUNT TO MSG OVERHEAD

120$:	MOV	R3,R2			; BEGIN NEW SEGMENT
	CLRB	(R3)+			; INIT NEW SEGMENT HEADER
	INC	C.PUBS(R5)		; COUNT SEG HEAD IN BUFFER
	MOV	R2,C.SGHD(R5)		; SAVE NEW HDR ADR
	MOV	C.NRDA(R5),R4		; GET MSG BEG
	INCB	N.DSGC(R4)		; INCREMENT TOTAL SEGMENT COUNT
	BIT	#CA.EOF,C.ASTS(R5)	; DID WE GET EOF CHARACTER ?
	BEQ	160$			; NO
	BR	170$			; YES, LEAVE

130$:	CMP	R0,C.LDAT(R5)		; SEE IF NEW COMPR SEQ
	BNE	160$			; NO - JUST BUFFER THE COL
	TST	(SP)			; REMOVE DATA ITEM FROM BUFFER
	BLT	140$
	DEC	R3			; WORD
	DEC	C.PUBS(R5)		; REM FROM BUFFER
140$:	DEC	R3			; BYTE
	DEC	C.PUBS(R5)		; REM FROM BUFFER
	DECB	(R2)			; REMOVE PREVIOUS DATA ITEM FROM LAST SEG
	BEQ	150$			; REMOVED WHOLE SEGMENT
	MOV	R3,R2			; BEGIN NEW SEGMENT
	MOV	R2,C.SGHD(R5)		; SAVE NEW HDR ADR
	MOV	C.NRDA(R5),R4
	INCB	N.DSGC(R4)		; INCREMENT TOTAL SEGMENT COUNT
	INC	C.PUBS(R5)		; COUNT NEW SEG HEAD IN BUFFER

150$:	MOV	R2,R3			; SET BUFFER PTR BACK TO SEG HEAD
	MOVB	#ND.CMP+1,(R3)+		; NEW SEGMENT - INIT AS A COMPRESSED SEG
160$:	MOV	R0,C.LDAT(R5)		; SAVE NEW DATA ITEM IN C.LDAT
	MOVB	R0,(R3)+		; INSERT NEW DATA ITEM(LO) IN BUFFER
	INC	C.PUBS(R5)		; COUNT IT IN BUFFER
	INCB	(R2)			; COUNT IT IN SEGMENT
	TST	(SP)			; CHECK DATA MODE => DATA LENGTH
	BLT	170$			; ASCII - 1 BYTE
	SWAB	R0			; OTHERWISE - 2 BYTES
	MOVB	R0,(R3)+		; INSERT NEW DATA ITEM(HI) IN BUFFER
	INC	C.PUBS(R5)		; COUNT IT IN BUFFER
					; BUT NOT IN SEGMENT
170$:	MOV	R3,C.PUBP+2(R5)		; SAVE UPDATED BUFFER PTR
180$:	TST	(SP)+			; FLUSH DATA MODE FLAG
190$:	BIT	#CARDN,C.RST(R5)
	BNE	CRDEND			; CARD FINISHED ALSO
	MOVB	#CRHNG,C.HUNG(R5)	; EXIT - SET TIMER
	POP$S	<R3,R2,R1,R0>
	MAP$S
	RETURN
INTERR:	MOV	C.RST(R5),R1		; ERROR-INTERRUPT ENTRY POINT
	MOV	#CA.PAU,R0		; DETECT INDIVIDUAL ERRORS
	BIT	#HOCHK,R1
	BEQ	11$
	BIS	#CA.HOP,R0		; HOPPER CHECK

11$:	BIT	#MOCHK,R1
	BEQ	12$
	BIS	#CA.MOT,R0		; MOTION CHECK

12$:	BIT	#TIMERR,R1
	BEQ	13$
	BIS	#CA.TER,R0		; TIMING ERROR

13$:	BIT	#RDROFL,R1
	BEQ	14$
	BIS	#CA.OFL,R0		; READER OFFLINE

14$:	BIS	R0,C.ASTS(R5)		; MARK ERROR => PAUSE ALSO
	TST	C.PUBP+2(R5)		; CHECK FOR EXISTING BUFFER
	BEQ	CRDEND			; NO CARD TO MARK ERRORS
	MAP$	C.PUBP(R5)		; MAP TO THE BUFFER
	MOV	C.NRDA(R5),R1		;  AND GET PTR TO PLACE IN BUFFER
	BISB	#ND.IER,N.DFLG(R1)	; SET ERROR STATUS FLAG IN NURD MSG
;	CALLR	CRDEND				; FALL INTO CRDEND
; CARD DONE
CRDEND:
	CLR	@C.CSR(R5)		; STOP THE READER
	POP$S	<R3,R2,R1,R0>		; RESTORE INTERRUPT LEVEL REGISTERS
	MAP$S
	bit	#ca.syn,c.asts(r5)	; synch requested in progress
	beq	3$			; no so do synch request
	return
3$:	bis	#ca.syn,c.asts(r5)	; set requesting synch
	MOV	R5,R4			; Calculate synch block
	ADD	#C.SYN,R4		;   address
	MOV	#5$,S.DSP(R4)		; Set resumption address
	CALLR	$SYNCH			; Go

; Here at synch level
5$:
	bic	#ca.syn,c.asts(r5)	; clear synch request flag
	MAP$	C.PUBP(R5)
	MOV	C.PUBP+2(R5),R3		; GET THE BUFFER PTR
	BEQ	20$			; NULL CARD
	CLRB	(R3)			; INSERT EOR = NULL SGMENT
	MOV	C.PUBS(R5),R1		; GET THE BUFFER CNT
	INC	R1			; COUNT THE EOR IN THE BUFFER
	MOV	C.NRDA(R5),R4		; GET THE MSG PTR
	INCB	N.DSGC(R4)		; COUNT THE EOR IN MSG SEGMENT CNT
	DEQ$	C.DDMQ
	MOV	R1,C.CNT(R4)		; STUFF FINAL BUFFER CNT IN CCB
	BIT	#CA.EOF,C.ASTS(R5)	; CHECK FOR EOF
	BEQ	10$
	MOV	C.NRDA(R5),R1		; INSERT EOF IN MSG
	BISB	#ND.EOF,N.DFLG(R1)

10$:	BIT	#CA.ABE,C.ASTS(R5)	; CHECK ABORTS
	BEQ	14$
	MAP$	C.BUF(R4)		; ABORTING
	MOV	C.BUF+2(R4),R0
	BITB	#ND.EOF,N.DFLG(R0)	; CHECK THIS CARD FOR EOF
	BNE	12$			; YES!
	CALL	$RDBRT			; No, return the buffer
	BR	20$

12$:	BIC	#CA.ABE,C.ASTS(R5)	; ABORT CONDITION FULFILLED
14$:	INC	C.IOCN(R5)		; COUNT THE BUFFER
	MOVB	#FC.XME,C.FNC(R4)
	CALL	$SCHED
20$:	BIT	#CA.PAU!CA.ADN!CA.EOF,C.ASTS(R5) ; DO WE NEED TO PAUSE ?
	BEQ	30$				 ; NO
	TSTB	C.RATT(R5)		; PAUSING, ATTN ALREADY PENDING ?
	BNE	25$			; YES, DON'T OVERWRITE CODE
	MOVB	#N.ASTC,C.RATT(R5)	; SET THE CODE
	CALL	SN$ATT			; INSTIGATE THE ATTN MESSAGE
25$:	RETURN				; GET ON OUT

30$:	CALLR	$CRSTR			; START NEXT CARD
.sbttl	Hollerith - ASCII Conversion Tables

; CONVERSION IS ZONE ORIENTED
; ZONE LABELS ARE CONSTRUCTED BY APPENDING TO "ZON"
; THE OCTAL CODE FOR CARD ZONES(ROWS)
;	ZONE 12	=	20
;	ZONE 11	=	10
;	ZONE  0	=	 4
;	ZONE  9	=	 2
;	ZONE  8	=	 1

ZON0:			;NO ZONE PUNCHES
	.byte	40	;NO ROW PUNCHES
	.byte	'1	;ROW 1
	.byte	'2	;ROW 2
	.byte	'3	;ROW 3
	.byte	'4	;ROW 4
	.byte	'5	;ROW 5
	.byte	'6	;ROW 6
	.byte	'7	;ROW 7

ZON20:			;ZONE 12
	.byte	'&	;NO ROW PUNCHES
	.byte	'A	;ROW 1
	.byte	'B	;ROW 2
	.byte	'C	;ROW 3
	.byte	'D	;ROW 4
	.byte	'E	;ROW 5
	.byte	'F	;ROW 6
	.byte	'G	;ROW 7

ZON10:			;ZONE 11
	.byte	'-	;NO ROW PUNCHES
	.byte	'J	;ROW 1
	.byte	'K	;ROW 2
	.byte	'L	;ROW 3
	.byte	'M	;ROW 4
	.byte	'N	;ROW 5
	.byte	'O	;ROW 6
	.byte	'P	;ROW 7

ZON4:			;ZONE 0
	.byte	'0	;NO ROW PUNCHES
	.byte	'/	;ROW 1
	.byte	'S	;ROW 2
	.byte	'T	;ROW 3
	.byte	'U	;ROW 4
	.byte	'V	;ROW 5
	.byte	'W	;ROW 6
	.byte	'X	;ROW 7

ZON1:			;ZONE 8
	.byte	'8	;NO ROW PUNCHES
	.byte	140	;ROW 1
	.byte	':	;ROW 2
	.byte	'#	;ROW 3
	.byte	'@	;ROW 4
	.byte	''	;ROW 5
	.byte	'=	;ROW 6
	.byte	'"	;ROW 7

ZON2:			;ZONE 9
	.byte	'9	;NO ROW PUNCHES
	.byte	ERRCHR	;ROW 1
	.byte	SYN	;ROW 2
	.byte	ERRCHR	;ROW 3
	.byte	ERRCHR	;ROW 4
	.byte	ERRCHR	;ROW 5
	.byte	ERRCHR	;ROW 6
	.byte	EOT	;ROW 7

ZON30:			;ZONE 12-11
	.byte	174	;NO ROW PUNCHES
	.byte	'J	;ROW 1
	.byte	'K	;ROW 2
	.byte	'L	;ROW 3
	.byte	'M	;ROW 4
	.byte	'N	;ROW 5
	.byte	'O	;ROW 6
	.byte	'P	;ROW 7

ZON24:			;ZONE 12-0
	.byte	173	;NO ROW PUNCHES
	.byte	'A	;ROW 1
	.byte	'B	;ROW 2
	.byte	'C	;ROW 3
	.byte	'D	;ROW 4
	.byte	'E	;ROW 5
	.byte	'F	;ROW 6
	.byte	'G	;ROW 7

ZON21:			;ZONE 12-8
	.byte	'H	;NO ROW PUNCHES
	.byte	ERRCHR	;ROW 1
	.byte	'[	;ROW 2
	.byte	'.	;ROW 3
	.byte	'<	;ROW 4
	.byte	'(	;ROW 5
	.byte	'+	;ROW 6
	.byte	'!	;ROW 7

ZON22:			;ZONE 12-9
	.byte	'I	;NO ROW PUNCHES
	.byte	SOH	;ROW 1
	.byte	STX	;ROW 2
	.byte	ETX	;ROW 3
	.byte	ERRCHR	;ROW 4
	.byte	HT	;ROW 5
	.byte	ERRCHR	;ROW 6
	.byte	DEL	;ROW 7

ZON14:			;ZONE 11-0
	.byte	175	;NO ROW PUNCHES
	.byte	176	;ROW 1
	.byte	'S	;ROW 2
	.byte	'T	;ROW 3
	.byte	'U	;ROW 4
	.byte	'V	;ROW 5
	.byte	'W	;ROW 6
	.byte	'X	;ROW 7

ZON11:			;ZONE 11-8
	.byte	'Q	;NO ROW PUNCHES
	.byte	ERRCHR	;ROW 1
	.byte	']	;ROW 2
	.byte	'$	;ROW 3
	.byte	'*	;ROW 4
	.byte	')	;ROW 5
	.byte	';	;ROW 6
	.byte	'^	;ROW 7

ZON12:			;ZONE 11-9
	.byte	'R	;NO ROW PUNCHES
	.byte	DC1	;ROW 1
	.byte	DC2	;ROW 2
	.byte	DC3	;ROW 3
	.byte	ERRCHR	;ROW 4
	.byte	ERRCHR	;ROW 5
	.byte	BS	;ROW 6
	.byte	ERRCHR	;ROW 7

ZON5:			;ZONE 0-8
	.byte	'Y	;NO ROW PUNCHES
	.byte	ERRCHR	;ROW 1
	.byte	'\	;ROW 2
	.byte	',	;ROW 3
	.byte	'%	;ROW 4
	.byte	'_	;ROW 5
	.byte	'>	;ROW 6
	.byte	'?	;ROW 7
ZON6:			;ZONE 0-9
	.byte	'Z	;NO ROW PUNCHES
	.byte	ERRCHR	;ROW 1
	.byte	ERRCHR	;ROW 2
	.byte	ERRCHR	;ROW 3
	.byte	ERRCHR	;ROW 4
	.byte	LF	;ROW 5
	.byte	ETB	;ROW 6
	.byte	ESC	;ROW 7

ZON3:			;ZONE 9-8
	.byte	ERRCHR	;NO ROW PUNCHES
	.byte	ERRCHR	;ROW 1
	.byte	ERRCHR	;ROW 2
	.byte	ERRCHR	;ROW 3
	.byte	DC4	;ROW 4
	.byte	NAK	;ROW 5
	.byte	ERRCHR	;ROW 6
	.byte	SUB	;ROW 7

ZON23:			;ZONE 12-9-8
	.byte	ERRCHR	;NO ROW PUNCHES
	.byte	ERRCHR	;ROW 1
	.byte	ERRCHR	;ROW 2
	.byte	VT	;ROW 3
	.byte	FF	;ROW 4
	.byte	CR	;ROW 5
	.byte	SO	;ROW 6
	.byte	SI	;ROW 7

ZON13:			;ZONE 11-9-8
	.byte	CAN	;NO ROW PUNCHES
	.byte	EM	;ROW 1
	.byte	ERRCHR	;ROW 2
	.byte	ERRCHR	;ROW 3
	.byte	FS	;ROW 4
	.byte	GS	;ROW 5
	.byte	RS	;ROW 6
	.byte	US	;ROW 7

ZON7:			;ZONE 0-9-8
	.byte	ERRCHR	;NO ROW PUNCHES
	.byte	ERRCHR	;ROW 1
	.byte	ERRCHR	;ROW 2
	.byte	ERRCHR	;ROW 3
	.byte	ERRCHR	;ROW 4
	.byte	ENQ	;ROW 5
	.byte	ACK	;ROW 6
	.byte	BEL	;ROW 7

ZON25:			;ZONE 12-0-8
	.byte	'H	;NO ROW PUNCHES
	.byte	ERRCHR	;ROW 1
	.byte	ERRCHR	;ROW 2
	.byte	ERRCHR	;ROW 3
	.byte	ERRCHR	;ROW 4
	.byte	ERRCHR	;ROW 5
	.byte	ERRCHR	;ROW 6
	.byte	ERRCHR	;ROW 7

ZON26:			;ZONE 12-0-9
	.byte	'I	;NO ROW PUNCHES
	.byte	ERRCHR	;ROW 1
	.byte	ERRCHR	;ROW 2
	.byte	ERRCHR	;ROW 3
	.byte	ERRCHR	;ROW 4
	.byte	ERRCHR	;ROW 5
	.byte	ERRCHR	;ROW 6
	.byte	ERRCHR	;ROW 7

ZON31:			;ZONE 12-11-8
	.byte	'Q	;NO ROW PUNCHES
	.byte	ERRCHR	;ROW 1
	.byte	ERRCHR	;ROW 2
	.byte	ERRCHR	;ROW 3
	.byte	ERRCHR	;ROW 4
	.byte	ERRCHR	;ROW 5
	.byte	ERRCHR	;ROW 6
	.byte	ERRCHR	;ROW 7

ZON32:			;ZONE 12-11-9
	.byte	'R	;NO ROW PUNCHES
	.byte	ERRCHR	;ROW 1
	.byte	ERRCHR	;ROW 2
	.byte	ERRCHR	;ROW 3
	.byte	ERRCHR	;ROW 4
	.byte	ERRCHR	;ROW 5
	.byte	ERRCHR	;ROW 6
	.byte	ERRCHR	;ROW 7

ZON15:			;ZONE 11-0-8
	.byte	'Y	;NO ROW PUNCHES
	.byte	ERRCHR	;ROW 1
	.byte	ERRCHR	;ROW 2
	.byte	ERRCHR	;ROW 3
	.byte	ERRCHR	;ROW 4
	.byte	ERRCHR	;ROW 5
	.byte	ERRCHR	;ROW 6
	.byte	ERRCHR	;ROW 7

ZON16:			;ZONE 11-0-9
	.byte	'Z	;NO ROW PUNCHES
	.byte	ERRCHR	;ROW 1
	.byte	ERRCHR	;ROW 2
	.byte	ERRCHR	;ROW 3
	.byte	ERRCHR	;ROW 4
	.byte	ERRCHR	;ROW 5
	.byte	ERRCHR	;ROW 6
	.byte	ERRCHR	;ROW 7

ZON33:			;ZONE 12-11-9-8
	.byte	ERRCHR	;NO ROW PUNCHES
	.byte	DLE	;ROW 1
	.byte	ERRCHR	;ROW 2
	.byte	ERRCHR	;ROW 3
	.byte	ERRCHR	;ROW 4
	.byte	ERRCHR	;ROW 5
	.byte	ERRCHR	;ROW 6
	.byte	ERRCHR	;ROW 7

ZON27:			;ZONE 12-0-9-8
	.byte	ERRCHR	;NO ROW PUNCHES
	.byte	NUL	;ROW 1
	.byte	ERRCHR	;ROW 2
	.byte	ERRCHR	;ROW 3
	.byte	ERRCHR	;ROW 4
	.byte	ERRCHR	;ROW 5
	.byte	ERRCHR	;ROW 6
	.byte	ERRCHR	;ROW 7
; ZONE INDEX TABLE
	.even
ZONTAB:
	$$=0
	.rept 32.
	.irp	Z,<$$>
	.if df,	ZON'Z
		.word	ZON'Z
	.iff
		.word	0
	.endc
	.endm
	$$=$$+1
	.endr



; ZTAB IS INDEXED BY Z1-Z7 CODE FOR PUNCH BIT.
;   LO BYTE IS ZONE PUNCH MASK,  HI BYTE IS ZONE CODE SHIFTED FOR AUG COL
ZTAB:	.byte	0,0			; NO PUNCHES
	.byte	100,1*20		; ZONE 1
	.byte	40,2*20			; ZONE 2
	.byte	20,3*20			; ZONE 3
	.byte	10,4*20			; ZONE 4
	.byte	4,5*20			; ZONE 5
	.byte	2,6*20			; ZONE 6
	.byte	1,7*20			; ZONE 7
	.even

	.end