Trailing-Edge
-
PDP-10 Archives
-
BB-P363B-SM_1985
-
mcb/drivers/le.m11
There are no other files named le.m11 in the archive.
.enabl lc
.title LE LP11 LLC and Driver
.ident "X02090"
; 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.
;
;
; Originally written by TOM POWERS
; December, 1976
;
; X01010 thru X01100 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 16-June-81 Buren Hoffman
; Added NOOP dispatches for Power-Failure and Initialization
; entries in the Timer Dispatch table.
;
; X02030 16-June-81 Buren Hoffman
; Minor bug fixes (i.e., typos, ...)
;
; X02040 18-June-81 Buren Hoffman
; Call TIMPIN routine for process initialization
;
; X02050 9-July-81 Buren Hoffman
; Simplified interrupt handling routine by removing
; compression-pair handling, and restructuring routine.
;
; X02060 12-July-81 Buren Hoffman
; Fixed bug in interrupt handler - device was being force fed
; whether it wanted it or not.
;
; X02070 15-July-81 Buren Hoffman
; Corrected problem in context switch. Register was being
; clobbered.
;
; X02080 5-August-81 Buren Hoffman
; Reduced number of LPT physical buffers from 12 to 6.
;
; x02090 14-sept-81 Ron Platukis
; Fix routine SN$ATT to not set overprint error if there
; -was a page limit overflow.
;
.sbttl Macro Calls & Dispatch Entries
.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 LE
PHD$D LEMCB
PHD$E
.psect $PLIT$,D,RO
;+
; Dispatch tables
;-
LEMCB: DSP$B
DSP$ ; Asynchronous trap
DSP$ FC.XME ; Xmit enable
DSP$ FC.RCE ; Receive enable
DSP$ FC.KIL ; Kill
DSP$ FC.CTL,LECTL ; Control (start / stop)
DSP$ FC.TIM,LETIM ; Timeout (clock)
DSP$ FC.XCP,LEDONE ; Xmit complete (resource ret)
DSP$ FC.RCP,LEDSP ; Receive complete (arriving data)
DSP$ FC.KCP ; Kill complete
DSP$ FC.CCP ; Control complete
DSP$E
;+
; Control dispatch
;-
LECTL: CALLR @1$(R3)
1$: DSP$B
DSP$
DSP$ FM.STR,DVOPEN ;Start device
DSP$ FM.STP,DVCLOS ;Stop device
DSP$E
;+
; Timer dispatch
;-
LETIM: 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 LE Data Base Offsets
; NURD LP11 Device driver Data Base
DAT$ DSR
L.TIMC: .blkb 1 ; Timer byte
L.NRD: .blkb 1 ; NRD's PIX
L.CSR: CSR$W ; One word CSR pointer
DTXTSZ= 16. ; Data/features process context allocated
L.DTXT: .blkw DTXTSZ ; Context block for data/features process
L.DTXL: .blkb 1 ; Number of words on stack at sleep time
.even
L.DFSP: .blkw 1 ; Stack origin
L.SYN: SYN$X ; Synch block
L.ASTS: .blkw 1 ; Action status bit map
LA.CLO== 1 ; Closed
LA.PAU = 2 ; Paused
LA.ADN = 4 ; Abort when done with DDM queue
LA.ABO = 10 ; Aborting
LA.ABE = 20 ; Clear abort on EOF (else on clear)
LA.CPC = 40 ; Clear page counter enable
LA.AKR = 100 ; Acknowledge requested
LA.ERR = 200 ; An error has been noted
LA.EOF = 400 ; Current message has EOF
LA.EOR = 1000 ; End of record (EOR) encountered
LA.CTL = 2000 ; Control message to return
LA.TMO = 4000 ; Device time out
LA.PLE = 10000 ; Page limit error occurred
LA.INT = 20000 ; Interrupt in progress
L.PUBP: .blkw 2 ; Buffer pointer doubleword
L.PUBS: .blkw 1 ; Buffer size down counter
L.DDMQ: LST$D ; CCBs queued for output
L.PGCT: .blkw 1 ; Physical page count
L.PGLM::.blkw 1 ; Page limit register
L.PLNC: .blkb 1 ; Physical line count
L.HUNG: .blkb 1 ; Device hung timer
; Miscellaneous items
L.IOCN: .blkw 1 ; Outstanding I/O count
L.IOWT: .blkw 1 ; Close wait flag (close CCB address)
; Control message processing data base
L.CMSQ: LST$D ; Control message queue
L.RECO: .blkb 1 ; Control message resource error flag
L.RATT: .blkb 1 ; Pending attention message flag - reason code
L.CAPP: .blkb 1 ; Pending capabilities message flag
; Data & features processing data base
L.LNCT: .blkb 1 ; Line counter
L.COCT: .blkb 1 ; Column counter
.even
L.DLCQ: LST$D ; CCBs queued into DLC
L.MFLG: .blkb 1 ; Current NURD message flags
L.DMSQ: .blkb 1 ; Last good/current data sequence numbers
L.DMSG: .blkb 1 ; Data message segment count byte
L.DMSS: .blkb 1 ; Data message current segment size
L.CAPN: .blkb 1 ; Current index into capabilities list
L.NFSP: .blkb 1 ; No. feature specs in current message
.even
L.DMCR: .blkw 1 ; Char repeat count, 2 bytes used as word
L.DMAD: .blkw 2 ; Data message address doubleword
L.DMBS: .blkw 1 ; Data message buffer size
L.PLBQ: LST$D ; Free physical buffer queue
L.PLBP: .blkw 2 ; Character pointer doubleword
L.PLBS: .blkw 1 ; Buffer size down counter
L.PBRC: .blkb 1 ; Temp byte
.even
L.MSGQ: LST$D ; Outgoing message queue
L.MSGA: .blkw 2 ; Current message buffer address doubleword
L.MSGS: .blkw 1 ; Current message buffer size
; LP11 common feature list
L.CFET: .blkb 1 ; Number of common features
.blkb 1 ; FE.DAT supported
.blkb 1 ; FE.SER not supported
.blkb 1 ; FE.LCR supported
.blkb 1 ; FE.FWD supported
.blkb 1 ; FE.EOF not supported
.blkb 1 ; FE.DVT not supported
.blkb 1 ; FE.TRN supported
.blkb 1 ; FE.FNM not supported
.blkb 1 ; FE.DWD supported
LCFETL== .-L.CFET-1
L.DFET: .blkb 1 ; Number of device specific features
.blkb 1 ; 129 not defined
.blkb 1 ; LP.HT supported
.blkb 1 ; LP.SFC supported
.blkb 1 ; LP.OVP not supported
.blkb 1 ; LP.CVF not supported
.blkb 1 ; LP.FCC supported
.blkb 1 ; LP.VFR not supported
.blkb 1 ; LP.CHS supported
.blkb 1 ; LP.PLE supported
.blkb 1 ; LP.OPV not supported
LDFETL== .-L.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 offset to it
FB.STR = 4 ; Feature is a string and F.FCV is the length
; current value begins at F.FCV+1
; Std 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
LE.DAT::.blkb 1 ; Data mode
LEDATS== FB.CL1!FB.SST!FB.STE!FB.CA ; Initial status of LE.DOF & LE.DAT
.blkb 1 ; ASCII
.blkb 1 ; ASCII is standard
LE.LCR::.blkb 1 ; Lower case raise
LELCRS== FB.CL0!FB.CA ; Initial status of LE.LCR
.blkb 1 ; Off
LE.FWD::.blkb 1 ; Form width
.blkb 1
LE.TRN::.blkb 1 ; Record truncation
LETRNS== FB.CL0!FB.CA!FB.SST!FB.STE ; Initial status of LE.TRN
.blkb 1 ; Off
.blkb 1 ; Off is standard
LE.DWD::.blkb 1 ; Device width
.blkb 1 ; Standard width
; Device specific features
LE.HT:: .blkb 1 ; Horizontal tab
LEHTS== FB.CL1!FB.SST!FB.STE ; Initial status of LE.HT
.blkb 1 ; Standard tabs on
.blkb 1
LE.SFC::.blkb 1 ; Standard VFU
LESFCS== FB.CL0!FB.SST!FB.STE ; Initial status of LE.SFC
.blkb 1 ; On
.blkb 1
LE.FCC::.blkb 1 ; Fortran carriage control
LEFCCS== FB.CL0!FB.CA!FB.SST!FB.STE ; Initial status of LE.FCC
.blkb 1 ; Off
.blkb 1
LE.CHS::.blkb 1 ; Character set
.blkb 1 ; 96 Character ASCII
LE.PLE::.blkb 1 ; Page limit enforcement
LEPLES== FB.CL1!FB.WRD!FB.CA!FB.SST!FB.STE ; Initial status of LE.PLE
.blkb 1 ; Page counter
.blkb 2 ; Standard = 0 => Off
END$
.psect $CODE$,I,RO
.sbttl Start / Stop Device (FC.CTL)
;+
; Initialize unit
; R4 CCB
; R5 Database
;-
DVOPEN: PUSH$S R4 ; Save incoming CCB pointer
BIT #LA.CLO,L.ASTS(R5) ; Closed?
BEQ 101$ ; No, then how can it be opened?
MOVB C.PIX(R4),L.NRD(R5) ; Remember NRD's PIX
CLR @L.CSR(R5) ; Be very pessimistic
CLR L.ASTS(R5) ; Zap the action status word
CLR L.PUBS(R5) ; Flush current print buffer
CLRB L.DTXL(R5) ; Nothing waiting
CLR L.DMAD(R5) ; Flush source buffer in use
CLR L.DMAD+2(R5)
CLR L.DMBS(R5)
CLRB L.DMSQ(R5) ; The data message sequence number
CLR L.MSGA(R5) ; Current out message
CLR L.MSGA+2(R5)
CLR L.MSGS(R5)
CLR L.IOCN(R5) ; OUTSTANDING IO COUNT
CLR L.IOWT(R5) ; CLOSE-WAIT FLAG
CLRB L.HUNG(R5) ; HUNG DEVICE TIMER
CLRB L.RECO(R5) ; CONTROL RESOURCE ERROR FLAG
CLRB L.RATT(R5) ; PENDING ATTENTION MESSAGE FLAG
CLRB L.CAPP(R5) ; PENDING CAPABILITIES MESSAGE FLAG
CLR L.PGCT(R5) ; THE PAGE COUNTER
MOV #DEL,R0 ; INITIALIZE LP11 XLATION TABLE
CLRB LEASC(R0) ; SUPPRESS DEL
DEC R0
10$: MOVB R0,LEASC(R0) ; MOST OF TABLE IS ASCII TO ASCII
SOB R0,10$
MOV #US,R0 ; SUPPRESS CONTROL CODES
12$: CLRB LEASC(R0)
SOB R0,12$
CLRB LEASC ; SUPPRESS NUL
MOV #FCXTB,R0 ; GET ADDRESS OF EXCEPTION TABLE
15$: MOVB (R0)+,R1 ; GET LEASC OFFSET
BMI 20$ ; DONE IF BYTE NEGATIVE
MOVB (R0)+,LEASC(R1) ; STUFF IN REPLACEMENT DATA
BR 15$
; Set all features with standards to standard
20$: MOV #L.CFET,R0 ; COMMON FEATURES
CALL 200$ ; DO ALL
MOV #L.DFET,R0 ; DEVICE SPECIFIC FEATURES
CALL 200$ ; ALL OF THEM TOO
; INIT PHYSICAL BUFFER POOL
MOV #LEPBFS,R0 ; RESERVE SEVERAL
55$: CALL $RDBGT ; GET A PHYSICAL BUFFER
BCS 70$ ; LOSE
CLR C.STS(R4) ; INIT STATUS
CLR C.STK(R4) ; CLEAR CCB POINTER
CLRB C.PIX(R4) ; MARK THE CCB AS STRICTLY LOCAL
ENQ$ L.PLBQ ; QUEUE THE BUFFER TO THE FREE LIST
SOB R0,55$
70$: TST L.PLBQ(R5) ; FAIL TO OPEN IF NONE AVAILABLE
BEQ 100$
CLR L.PLBP(R5) ; INIT PHYSICAL BUFFER IN USE
CLR L.PLBP+2(R5)
CLR L.PLBS(R5)
MOV #S.SSUC,R3 ; RETURN SUCCESS
INCB (R5) ; START THE TIMER
CALL $LTMEN ; ...
BR 104$
100$: BIS #LA.CLO,L.ASTS(R5) ; NOTE REMAINING CLOSED
101$: MOV #S.ERBO,R3 ; FAILED TO OPEN
104$: POP$S R4 ; RESTORE POINTER FOR COMMAND CCB
BR CTLXIT ; Leave
; Feature setting routine
200$: ADD R5,R0 ; R0/FEATURE LIST OFFSET
CLR R1
BISB (R0)+,R1 ; GET NO. OF FEATURES
210$: CLR R3
BISB (R0)+,R3 ; GET ADDRESS OF FEATURE BLOCK
BEQ 220$ ; EMPTY SLOT
ADD R5,R3
CALL SETSTD ; SET IT TO STANDARD
220$: SOB R1,210$
NOOP: RETURN
;+
; Stop unit
;
; R4 CCB
; R5 Database
;-
DVCLOS: PUSH$S R4 ; SAVE COMMAND CCB POINTER
CALL $LESTP ; CLEAR PRINTER (SCREECHING HALT)
CLRB (R5) ; Stop timer
BIS #LA.CLO,L.ASTS(R5) ; MARK AS CLOSED
CALL Q$PURG ; FLUSH THE QUEUES
10$: DEQ$ L.PLBQ,20$ ; Get next physical buffer from queue
CALL $RDBRT ; Return it to system
BR 10$ ; Continue
20$: DEQ$ L.CMSQ,30$ ; FLUSH ANY OUTSTANDING CONTROL MESSAGES
MOVB #FC.RCE,C.FNC(R4) ; and return them
CALL $SCHED ; ...
BR 20$
30$: POP$S R4 ; RESTORE COMMAND CCB POINTER
MOV R4,L.IOWT(R5) ; SET CLOSE WAIT FLAG
; Complete close only when all CCB'S returned
STOPWT: TST L.IOCN(R5)
BEQ 10$
INCB (R5) ; Restart timer
RETURN
10$: CLRB (R5) ; STOP THE CLOCK
MOV L.IOWT(R5),R4 ; GET THE CLOSE CCB BACK
CLR L.IOWT(R5) ; COVER OUR TRACKS
MOV #S.SSUC,R3
CTLXIT: MOV R3,C.STS(R4) ; Set status
MOVB #FC.CCP,C.FNC(R4) ; and function for completion
CALLR $SCHED ; Leave
.sbttl Timer handler (FC.TIM)
;+
; R5 Database address
;-
CLOCK: TST L.IOWT(R5) ; CHECK FOR CLOSE WAIT
BNE STOPWT ; PREEMPTS ALL ELSE
TSTB L.RECO(R5) ; CHECK CONTROL RESOURCE ERROR
BEQ 10$
CLRB L.RECO(R5) ; YES - FLUSH THE FLAG
CALL MSGFIN ; RESTART THE PROCESS
10$: TSTB L.DTXL(R5) ; Anything waiting for tick?
BEQ 20$ ; Appears not
CALL ZZ$WAK ; Yes, resume it - it will return here
20$: BIT #LA.ERR,L.ASTS(R5) ; CHECK DEVICE PROBLEMS
BEQ 30$ ; NOT BECAUSE OF DEVICE ERROR
TST @L.CSR(R5) ; DEVICE ERROR WAS REASON
BMI 30$ ; STILL HAVING PROBLEMS
CALL SN$ALR ; CURE HAS BEEN EFFECTED, NOTIFY USER
BCS 30$ ; COULDN'T SEND ONE, COME BACK LATER
BIC #LA.ERR,L.ASTS(R5) ; FLUSH ERROR BUT LEAVE LA.PAU
; USER MUST RESUME
30$: TSTB L.HUNG(R5) ; CHECK HUNG TIMER
BEQ 50$ ; NOT SET
DECB L.HUNG(R5) ; COUNT THE TOCK
BNE 50$ ; STILL OK
BIS #LA.TMO,L.ASTS(R5) ; DEVICE IS COMATOSE
TSTB L.RATT(R5) ; CHECK PENDING ATTENTION MESSAGE
BNE 55$ ; YES - DON'T GENERATE ANOTHER
MOVB #N.ASTC,L.RATT(R5) ; SET PENDING ATTENTION FLAG
; (REASON CODE)
50$: TSTB L.RATT(R5) ; CHECK PENDING ATTENTION MESSAGE
BEQ 60$
55$: CALL SN$ATT ; TRY TO SEND ONE
60$: TSTB L.CAPP(R5) ; CHECK PENDING CAPABILITIES MESSAGE
BEQ 70$
CALL SN$CAP ; TRY TO SEND ONE
70$: INCB (R5) ; RESET THE CLOCK FOR NEXT TOCK
RETURN
.sbttl Resource Return
;+
; Resource return
;-
LEDONE: DEC L.IOCN(R5) ; COUNT THE RETURNED RESOURCE
BGE 10$
CLR L.IOCN(R5) ; BLEW IT SOMEWHERE
10$: MOV #80.,R1 ; SET SIZE OF RETURNING BUFFER
CALLR $CBBRT ; SEND IT BACK, AND CCB TOO
.sbttl Message Received (FC.RCP)
;+
; Dispatch according to the type of NURD message connected to the
; current CCB chain, which consists of but a single message.
;
; Inputs: R4 - Points to first (or only) CCB in chain
; R5 - Points to LE data base
;
; 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.
;
; Message Format: <msgtype><msgflgs><msgtxt.....>
;-
LEDSP: BIT #LA.CLO,L.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
; AND RETURN
40$: DSP$B
DSP$ ,MSGDAT ; 0 - DATA MESSAGE
DSP$ ,50$ ; 1 - ATTENTION (ILLEGAL IF REC'D)
DSP$ ,MSGDAT ; 2 - FEATURES MESSAGE
DSP$ ,MSGCTL ; 3 - CONTROL MESSAGE
NRDOTR = <.-40$>/2
DSP$ ,50$ ; N - ILLEGAL NURD TYPE SPEC
DSP$E
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 Data, Control and Features Messages
;
; R4 CCB to queue
; R5 Database address
;-
MSGDAT: BIT #LA.ABO,L.ASTS(R5) ; Check for abort state
BEQ MSGQUE ; Ok to proceed
MOV #S.EABO,C.STS(R4) ; Aborting
MSGX: MOVB #FC.RCE,C.FNC(R4) ; ...
CALLR $SCHED
MSGQUE: ENQ$ L.DLCQ
CMP R4,L.DLCQ(R5) ; Was queue empty ?
BNE 35$ ; No, routines of interest are working
TSTB L.DTXL(R5) ; Maybe waiting for packet ?
BNE 35$ ; Could be
30$: MOV SP,L.DFSP(R5) ; Set the stack origin for this process
CALL RD$BYT ; Extract message type
MOV R0,R1
CALL RD$BYT ; Extract message flags
MOV R0,L.MFLG(R5)
ASL R1 ; Make a dispatch
CALL @40$(R1) ; and go
MOV L.DLCQ(R5),R4 ; Get next message
BNE 30$ ; ...
35$: RETURN ; Done
40$: DSP$B
DSP$ ,DATSYN ; 0 - Data message
DSP$ ,MSGX ; 1 - Attention (illegal if rec'd)
DSP$ ,FTRSYN ; 2 - Features message
DSP$ ,CTLSYN ; 3 - Control message
DSP$E
;+
; NURD Data Message Handler
;
; Virtual (NURD) to physical (LP11) data translation.
; Unload NURD buffer into physical buffer and ship physical buffers
; down to driver as they are filled, saving virtual machine buffer
; address in C.PRM5 of the physical buffer until the corresponding
; physical data has been output or aborted.
;
; This routine is called from MSGDAT at the main entry, and entered at
; various recovery points, depending upon the nature of the failure
; which caused the suspension.
;
; Source buffer has been set up and NURD type and message flag bytes
; (1st & 2nd bytes) have been extracted.
;
; R4 CCB
; R5 Database address
;
; Data message format: <seq no.><flgs><seg cnt><segs......>
; Data seg format: <cnt><txt....> or <sign+cnt><repeat char>
;-
DATSYN: CALL RD$BYT ; GET SEQUENCE NUMBER
MOVB R0,L.DMSQ(R5) ; SAVE THE CURRENT SEQ NUMBER
CALL RD$BYT ; GET THE FLAGS BYTE
BIT #ND.ACK,R0 ; CHECK FOR ACK REQ'D
BEQ 14$ ; NO ACK REQUEST
BIS #LA.AKR,L.ASTS(R5) ; NOTE ACK REQUESTED
14$: BIT #ND.EOF,R0 ; CHECK FOR EOF
BEQ 16$
BIS #LA.EOF,L.ASTS(R5) ; SET THIS MESSAGE HAS EOF
16$: CALL RD$BYT ; GET SEGMENT COUNT BYTE
MOVB R0,L.DMSG(R5) ; SAVE THE SEGMENT COUNT OF MESSAGE
BEQ 60$ ; EMPTY MESSAGE, SKIP DATA PROCESSING
; Process message data
20$: CALL RD$BYT ; GET NEXT SEGMENT HDR BYTE
TSTB R0 ; DISPATCH BASED ON SEGMENT TYPE
BMI 28$ ; THIS IS A COMPRESSED SEGMENT
BGT 32$ ; THIS IS A NORMAL DATA SEGMENT
; This is an EOR
TSTB LE.FCC+F.FCV(R5) ; IS FORTRAN CARRIAGE CONTROL SET?
BEQ 24$ ; NO, EOR IS THEN NOT SIGNIFICANT
BIS #LA.EOR,L.ASTS(R5) ; YES, SET EOR TO FLAG NEXT CHAR AS CC
24$: BR 56$ ; PROCESS END OF SEGMENT
; Process compressed data segment
28$: MOVB #1,L.DMSS(R5) ; Set segment size to one character
BIC #177600,R0 ; Isolate repeat count
MOV R0,L.DMCR(R5) ; and save it
BR 36$ ; Process data in normal way
; Process normal data segment
32$: MOVB R0,L.DMSS(R5) ; Set segment size
CLR L.DMCR(R5) ; No repeat count
;Normal segment processor
36$: CALL RD$BYT ; Get character data byte from message
BIC #^C177,R0 ; SAVE ONLY LOW SEVEN BITS OF CHARACTER
MOVB LEASC(R0),R0 ; GET MAPPED VALUE OF THE CHARACTER
MOVB R0,L.PBRC(R5) ; SAVE A COPY OF THE CHARACTER
40$: CLR R0 ; GET THE BYTE BACK
BISB L.PBRC(R5),R0
BIT #LA.EOR,L.ASTS(R5) ; END OF RECORD ON?
BEQ 42$ ; NO, PROCEED WITHOUT INCIDENT
; YES, BYTE IS FORTRAN CARRIAGE CHAR
CALL FORM$F ; HANDLE FORTRAN CARRIAGE CONTROL
BIC #LA.EOR,L.ASTS(R5) ; FLUSH END OF RECORD
BR 52$ ; OKAY, DO NEXT BYTE
42$: TSTB R0 ; CHECK FOR CHARACTER TYPE
BEQ 54$ ; IGNORE CHARACTERS MAPPED TO ZERO
BPL 44$ ; DIRECTLY TRANSMIT PRINTING CHARACTERS
; MANIPULATIONS REQ'D ON FORMS CONTROL CHARS
CALL FORM$X ; DO FORMS CONTROL TRANSLATION
BR 52$ ; SUCCEEDED, CONTINUE CURRENT PROCESSING
44$: CALL PR$CHR ; STUFF RESULTING CHAR TO OUTPUT
52$: DEC L.DMCR(R5) ; DECREMENT CHAR REPEAT COUNT
BGT 40$ ; MORE TO DO ON CURRENT CHAR
54$: CLR L.DMCR(R5) ; KEEP REPEAT CNT = 0
DECB L.DMSS(R5) ; DECREMENT SEGMENT SIZE
BNE 36$ ; SEGMENT NOT EMPTY, GET NEXT CHAR
; SEGMENT FINISHED, CLEAN UP
56$: DECB L.DMSG(R5) ; DECREMENT COUNT OF SEGMENTS
BNE 20$ ; NOT DONE, GO BACK FOR NEXT SEGMENT
; Message complete, drop into end-of-message code
60$: MOV L.PLBQ(R5),R4 ; GET PHYSICAL BUFFER CCB ADDRESS
BEQ 86$
MOV L.ASTS(R5),C.STS(R4) ; MARK PHYSICAL BUF
86$: CALL RD$DQX ; PURGE
BIC #LA.EOF!LA.AKR,L.ASTS(R5) ; CLEAR SAVED DATA FLAGS
RETURN
;+
; NURD Control Message Handler
;
; Called from LEDSP
;
; R0 Buffer pointer to NURD message type
; R4 CCB
; R5 Database
; KISAR6 Mapped to buffer
;
; Control message format: <seq no.><command><result>
;-
.enabl lsb
MSGCTL: CMPB #S$INT,C.MOD(R4) ; CHECK FOR INTERRUPT MESSAGE
BEQ 10$ ; Tis
CALLR MSGQUE ; OTHERS ARE SYNCH'D WITH DATA STREAM
10$: ENQ$ L.CMSQ ; QUEUE THE CCB FOR LATER
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 #CTLLEN,R1 ; CHECK RANGE OF COMMAND
BLOS 100$ ; OUT OF RANGE, SKIP OUT
ASL R1 ; IN RANGE, GET AS WORD OFFSET
CLR R0 ; INIT COMPLETION CODE
CALL @CTLDSP(R1) ; EXECUTE DETAILED CONTROL ROUTINE
CMP R0,#-1 ; MORE TO DO ?
BEQ 15$ ; NO
;+
; Here when finished, or when performing a retry to get a resource
;-
MSGFIN: CALL BF$SDB ; GET A RESPONSE BUFFER
BCC 20$ ; OK
INCB L.RECO(R5)
15$: RETURN
20$: PUSH$S R4 ; SAVE THE RESPONSE CCB
DEQ$ L.CMSQ ; GET THE MESSAGE 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 MESSAGE
MOVB #FC.RCE,C.FNC(R4) ; ACK the control message
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 MESSAGE
CLRB (R3)+ ; NO MESSAGE 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 L.IOCN(R5) ; Count the I/O
CALLR $SCHED ; Queue it and return
; Illegal function
100$: DEQ$ L.CMSQ ; Get the CCB back
MOV #S.ERBO,C.STS(R4) ; Reject this one
MOVB #FC.RCE,C.FNC(R4) ; ...
CALL $SCHED ; ...
CALL $LESTP ; Turn off the device
BIS #LA.ADN,L.ASTS(R5) ; Abort when print queue done
MOV #-1,R0 ; Say we were here
CALLR $LESTR ; Turn on printer so it can die
;+
; The NURD control 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
CTLLEN = <. - CTLDSP>/2 ;* = special action taken
.dsabl lsb
;+
; Non-interrupt control message processing
;-
CTLSYN: CALL RD$BYT ; GET SEQ NO.
MOVB R0,L.DMSQ(R5) ; SAVE IT FOR RESPONSE
CALL RD$BYT ; GET COMMAND
MOV R0,R1
CMP #CTLLEN,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 ; INIT COMPLETION FLAG
CALL @CTLDSP(R1) ; Do it - return with R0 = result code
POP$S R1 ; GET COMMAND CODE BACK
CMP R0,#-1 ; MORE TO DO ?
BEQ 60$ ; NO
PUSH$S R0 ; SAVE RESPONSE CODE
CALL RD$DQX ; Complete the message
MOV #NM.CTL,R0 ; CONSTRUCT A CONTROL MESSAGE
CALL MS$BYT ; CONTROL CODE
CLR R0
CALL MS$BYT ; NULL MESSAGE FLAGS
MOVB L.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$ L.MSGQ ; GET BUFFER OFF QUEUE
SUB L.MSGS(R5),C.CNT(R4) ; SET MESSAGE SIZE
INC L.IOCN(R5) ; ONE MORE OUTSTANDING I/O
CALL $SCHED ; SEND IT
CLR L.MSGS(R5) ; CLEAN UP TRACKS
TSTB L.RATT(R5) ; CHECK FOR OTHER MESSAGES TO GO OUT
BEQ 20$ ; NO ATTENTION MESSAGES
CALL SN$ATT ; PENDING ATTENTION MESSAGE
20$: TSTB L.CAPP(R5) ; TRY CAPABILITIES MESSAGE
BNE 30$
RETURN
30$: CALLR SN$CAP ; PENDING CAPABILITIES MESSAGE
50$: CALL $LESTP ; FLUSH THE DEVICE INTERRUPTS
BIS #LA.ADN,L.ASTS(R5) ; ABORT LINK WHEN PRINT QUEUE DONE
CALL RD$DQX ; Ack the message
60$: RETURN
;+
; Clear ABORT status
;-
CTLABC: BIT #LA.ABE!LA.ABO,L.ASTS(R5) ; ABORT SET?
BEQ 10$ ; NO
BIC #LA.ABE!LA.ABO,L.ASTS(R5) ; CLEAR ABORTING
RETURN
10$: MOV #NR.ACN,R0 ; NOT IN ABORT STATE
RETURN
;+
; Abort to end of file
;-
CTLABE: MOV #NR.NOE,R0 ; EOF NOT DEFINED FOR OUTPUT DEVICE
RETURN
;+
; Abort until clear received
;-
CTLABT: CALL $LESTP ; FLUSH THE DEVICE
BIS #LA.ABO,L.ASTS(R5) ; NOTE ABORTING
MOV L.DDMQ(R5),R1 ; CHECK ALL QUEUES
BIS L.PLBP+2(R5),R1
BIS L.DLCQ(R5),R1
BIS L.MSGQ(R5),R1
BISB L.DTXL(R5),R1 ; CHECK SUSPENDED PROCESSES
BNE 10$ ; NR.ABS (ABORT STARTED)
MOV #NR.NAB,R0 ; NOTHING TO ABORT
10$: CALLR Q$PURG ; CLEAR THE WORLD
;+
; Request capabilities
;-
CTLCPB: INCB L.CAPP(R5) ; SET CAPABILITIES PENDING FLAG
RETURN
;+
; Dump output buffers
;-
CTLDMP: TSTB L.RATT(R5) ; PENDING ATTENTION?
BNE 10$ ; YES
TSTB L.CAPP(R5) ; PENDING CAPABILITIES?
BNE 10$ ; YES
TST L.DDMQ(R5) ; NOT TO DLC, CHECK ON PHYSICAL QUEUE
BNE 10$ ; IT'S THERE, OKAY
MOV #NR.NOB,R0 ; NOTHING TO DUMP
10$: RETURN
;+
; Pause output
;-
CTLPAU: BIT #LA.PAU!LA.ADN,L.ASTS(R5) ; ALREADY PAUSED/PAUSING?
BNE 20$ ; YES
TST L.DLCQ(R5) ; ANYTHING QUEUED?
BNE 10$ ; YES, OKAY TO PAUSE
TST L.DDMQ(R5) ; NONE IN VIRTUAL, CHECK PHYSICAL
BNE 10$
MOV #NR.NDP,R0 ; NOTHING TO PAUSE
10$: CALL $LESTP ; ENSURE ONLY ONE ATTENTION MESSAGE
BIS #LA.PAU,L.ASTS(R5) ; PAUSE NO MATTER WHAT
CALLR $LEGO ; MAKE SURE IT HAPPENS
20$: MOV #NR.PAU,R0 ; ALREADY PAUSED
RETURN
;+
; RESUME OUTPUT
;-
CTLRSM: BIT #LA.PAU!LA.ADN,L.ASTS(R5) ; CHECK PAUSEDNESS
BEQ 20$ ; NOTHING HUNG
BIC #LA.PAU!LA.ADN!LA.ERR!LA.PLE,L.ASTS(R5) ; No pause conditions
TST L.DDMQ(R5) ; CHECK FOR ANY CURRENT DATA XFER'S
BEQ 10$ ; SOMETHING IN PRINT Q
CALL $LEGO ; TURN ON DEVICE AGAIN
BR 30$
10$: TST L.DLCQ(R5) ; RESUME NON-INTERRUPT MESSAGE PROCESSING
BNE 30$ ; SOMETHING WAITING TO PROCESSED
MOV #NR.NDR,R0 ; NOTHING TO RESUME
BR 30$
20$: MOV #NR.NPS,R0 ; NOT PAUSED
30$: RETURN
;+
; Status request
;-
CTLSTS: TSTB L.RATT(R5) ; CHECK ATTENTION ALREADY PENDING
BNE 10$ ; DON'T MUNG ORIGNAL REASON
MOVB #N.ARQS,L.RATT(R5) ; SET ATTENTION PENDING FLAG
10$: RETURN
;+
; NURD Features message handlers
;
; Feature message 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 L.DLCQ
; R5 Database address
; KISAR6 Mapped to data buffer
;-
.enabl lsb
FTRSYN: MOV #NM.FTR,R0 ; BEGIN A REPLY
CALL MS$BYT ; INSERT NURD MESSAGE TYPE
CLR R0
CALL MS$BYT ; INSERT MESSAGE FLAGS
CALL RD$BYT ; GET SEQ NO.
MOVB R0,L.DMSQ(R5)
CALL MS$BYT ; SEND IT BACK
CALL RD$BYT ; GET NO. FEATURE SPECS
CLR R0 ; BUT IGNORE IT
MOVB R0,L.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 L.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 L.CFET(R5),L.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 STANDARD
BNE FERERR ; NO - ILLEGAL COMBINATION
10$: INCB R2 ; ADVANCE THRU LIST
BMI 15$ ; GRUBBLING THRU DEVICE FEATURES
CMPB R2,L.CAPN(R5) ; CHECK RANGE
BLOS 20$ ; OK
MOVB L.DFET(R5),L.CAPN(R5) ; INIT FOR DEVICE SPECFIC FEATURES
MOV #201,R2
15$: MOV R2,R0 ; DEVICE FEATURE - HACK OFF SIGN
BIC #^C177,R0
CMPB R0,L.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 message-flags(hi) + class(lo)
; R2 Feature id (byte)
; R3 Points to feature status byte
; R5 Database address
; L.DMSQ Sequence no.
; L.NFSP Number of 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 L.CAPN(R5) ; CHECK FOR ALLNESS
BNE 10$ ; YES
70$: MOV L.MSGQ(R5),R4 ; GET BEG OF MESSAGE
MAP$ C.BUF(R4)
MOV C.BUF+2(R4),R0 ; GET THE PTR
MOVB L.NFSP(R5),N.NSPC(R0) ; SET IN THE FINAL SPEC COUNT
CALL RD$DQX ; RETURN ORGINAL MESSAGE TO NRD
DEQ$ L.MSGQ ; GET BUFFER OFF QUEUE
SUB L.MSGS(R5),C.CNT(R4) ; SET MESSAGE SIZE
INC L.IOCN(R5) ; ONE MORE OUTSTANDING I/O
CLR L.MSGS(R5) ; CLEAN UP TRACKS
CALLR $SCHED ; SEND IT
FERUSF: PUSH$S #FR.USF
BR 100$
FERBCL: PUSH$S #FR.BCL
BR 100$
FERNST: PUSH$S #FR.NST
BR 100$
FERERR: PUSH$S #FR.ERR
BR 100$
FERCPN: PUSH$S #FR.CPN
BR 100$
FERNEB: PUSH$S #FR.NEB
BR 100$
FERDNP: PUSH$S #FR.DNP
100$: MOV R2,R0 ; Insert <fid><flags><class><error>
CALL MS$BYT ; FID
MOV R1,R0
SWAB R0
CALL MS$BYT ; Flags
MOV R1,R0
CALL MS$BYT ; Class
POP$S R0
CALL MS$BYT ; Error
INCB L.NFSP(R5) ; Count the spec
BR FTRCON
.dsabl lsb
;+
; Features Read
;
; R1 Feature message-flags(hi) + class(lo)
; R2 Feature id (byte)
; R3 Points to feature status byte
; R5 Database address
;
; Reply has been formatted thru <no. specs>
; Insert in message:
; <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 STANDARD?
BEQ 10$ ; NO
BIS #NF.STD,R0 ; SET TO STANDARD
10$: CALL MS$BYT ; INSERT FLAGS
CLRB R1 ; SET FEATURE CLASS IN R1(LO)
BITB #FB.CL1,(R3) ; CHECK CLASS 1
BEQ 15$ ; CLASS 0
BISB #FC.CL1,R1 ; SET CLASS 1
15$: 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 20$ ; 1 BIT
; FEATURE DEPENDENT
BITB #FB.WRD!FB.STR,(R3) ; LOOK FOR THOSE MORE THAN 1 BYTE
BNE 50$ ; YES
; NOT MULTIPLE BYTE DATA, SO 1 BYTE
INC R0 ; COUNT = 1
CALL MS$BYT ; INSERT COUNT
20$: MOVB 1(R3),R0 ; COPY FEATURE TABLE DATA ENTRY
30$: CALL MS$BYT ; INSERT DATA
40$: INCB L.NFSP(R5) ; COUNT THE SPEC
BR FTRCON ; CONTINUE FEATURE HACKING
50$: BITB #FB.STR,(R3) ; WORD OR STRING ?
BNE 60$ ; STRING
; WORD, F.FCV = DB 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 30$ ; INSERT HI BYTE
; STRING - F.FCV HAS THE LENGTH, CURRENT VALUE BEGINS AT F.FCV+1
60$: INC R3 ; THIS DEPENDS ON F.FCV=1
MOVB (R3)+,R0 ; GET THE LENGTH
CALL MS$BYT ; INSERT IN MESSAGE
MOV R0,R4 ; SAVE IT
70$: MOVB (R3)+,R0 ; INSERT THE STRING IN MESSAGE
CALL MS$BYT
DECB R4 ; COUNT THE BYTE
BNE 70$
BR 40$
;+
; Features set
;
; R1 Feature message-flags(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 STANDARDNESS
BEQ 80$ ; STANDARD 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
; CLASS 1 - FEATURE DEPENDENT
CALL RD$BYT ; FEATURE DEPENDENT DATA - GET LENGTH
BITB #FB.WRD!FB.STR,(R3) ; CHECK MULTIPLE BYTE DATA
BNE 30$ ; GO DO IT
CMPB #1,R0 ; ONE BYTER ?
BNE FERERR ; NO
10$: CALL RD$BYT ; CLASS 0 OR CLASS 1(1 BYTE), GET BYTE
MOVB R0,1(R3) ; SET FEATURE CURRENT VALUE
20$: BICB #FB.SST,(R3) ; NOT SET TO STANDARD
BR FTRSHO ; NOW READ THE FEATURE
30$: BITB #FB.STR,(R3) ; Word or string ?
BNE 40$ ; String
CMPB #2,R0 ; 2 BYTE LENGTH
BNE 50$ ; SCREWED IT UP
CALL RD$BYT ; GET THE PAGE LIMIT
MOVB R0,L.PBRC(R5) ; HIDE THE LO PART
CALL RD$BYT ; GET THE HI PART
SWAB R0
BISB L.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 20$
; STRING - F.FCV HAS THE LENGTH, CURRENT VALUE BEGINS AT F.FCV+1
40$: PUSH$S R3 ; SAVE THE FEATURE BLOCK PTR
INC R3 ; THIS DEPENDS ON F.FCV=1
CMPB R0,(R3)+ ; CHECK THE LENGTH
BEQ 60$ ; OK
POP$S R3 ; LENGTH ERROR - TO BEGINNING OF BLOCK
50$: CALLR FERERR ; AND REPORT THE ERROR
60$: PUSH$S R1 ; SAVE THE VARIABLES
MOV R0,R1 ; R1/COUNT, R3/PTR TO CURRENT VALUE
70$: CALL RD$BYT ; XFER NEW TO CURRENT VALUE
MOVB R0,(R3)+ ; STUFF THE BYTE
SOB R1,70$ ; AND COUNT IT
POP$S <R1,R3>
CALLR FTRSHO ; NOW READ THE FEATURE
80$: CALL SETSTD ; SET FEATURE TO STANDARD
BCC 90$
CALLR FERNST ; NO STANDARD TO SET
90$: CALLR FTRCON ; DON'T READ IT
;+
; Set feature to its standard value
;
; R3 Pointer to feature block in database
; R5 Database address
;-
SETSTD: BITB #FB.STE,(R3) ; SEE IF IT HAS ONE
BNE 10$ ; YES
SEC ; NO STANDARD TO SET
RETURN
10$: BITB #FB.WRD!FB.STR,(R3) ; CHECK MULTIPLE BYTE VALUES
BNE 20$ ; YES
; SINGLE BYTE VALUE
MOVB F.FSV(R3),F.FCV(R3) ; SET CURRENT VALUE = STANDARD VALUE
BISB #FB.SST,(R3) ; MARK FEATURE AS SET TO STANDARD
BR 30$
; MULTIPLE BYTE VALUE
20$: BITB #FB.STR,(R3) ; Word or string ?
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
30$: CLC
RETURN
40$: PUSH$S <R0,R1,R3> ; STRING - F.FCV HAS THE LENGTH
; CURRENT VALUE BEGINS AT F.FCV+1
; STANDARD VALUE BEGINS AT F.FCV+1+(F.FCV)
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 ADDR,R3/DEST ADDR
45$: MOVB (R1)+,(R3)+ ; XFER THE STANDARD VALUE
SOB R0,45$ ; COUNT IT
POP$S <R3,R1,R0>
CLC
RETURN
;+
; Feature finder
;
; FTRFND looks for a feature with FID in R2 and returns a pointer
; to the feature block in R3, if successful. Normal carry flag
; condition for success or failure.
FTRFND: MOV #L.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 #L.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 POINTS TO BASE OF FEATURES LIST,
; WHICH HOLDS HIGHEST STORED CODE
ADD R2,R3 ; ADD FID TO POINT TO ADDR OF 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 LE.
;
; The routines are arranged in alphabetical order.
;-
;+
; Physical Buffer Management
;-
BF$SDB: PUSH$S R1 ; Save R1
MOV #80.,R1 ; Size of buffer wanted
CALL $CBBGT ; Get a message buffer
BCS 10$ ; Oops
MOV R1,C.CNT(R4) ; Set size
MOVB #S$PEOM,C.PRM1+1(R4) ; Init as a whole message
MOVB #FC.XME,C.FNC(R4) ; Set function code
MOVB #S$SND,C.MOD(R4) ; Assume normal data
MOVB L.NRD(R5),C.PIX(R4) ; Set PIX
10$: POP$S R1 ; Restore R1
RETURN ; and leave
;+
; Fortran carriage control processing
;
; Maps the first character of each "record" into one or more
; forms control characters:
; ** INPUT ** ** OUTPUT **
; Space LF
; * DC3
; + CR
; - DC3,DC3,LF
; / DC4
; 0 DC3,LF
; 1 FF
; 2 DLE
; 3 VT
; Other Space
;-
FORM$F: MOV #FCCTBL,R1 ; SCAN TABLE FOR 1 CHARACTER XLATIONS
MOV #FCCTBN,R2
10$: CMPB (R1)+,R0
BEQ 100$ ; FOUND IT
INC R1 ; SKIP EQUIVALENT
SOB R2,10$
CMPB #'-,R0 ; CHECK MULTIPLE CHARACTER XLATIONS
BEQ 20$
CMPB #'0,R0
BEQ 30$
MOV #SPA,R0 ; NOT AN FCC CHARACTER
CALLR PR$CHR ; JUST PRINT A SPACE
20$: MOV #3,R0 ; CHECK FOR ROOM IN CURRENT BUFFER
CALL PR$SPC ; GET RID OF THIS BUFFER IF NOT ENOUGH
25$: MOV #DC3,R0 ; DO DC3,DC3,LF
CALL 110$ ; PRINT THW DC3
30$: MOV #2,R0 ; CHECK FOR ROOM IN CURRENT BUFFER
CALL PR$SPC ; GET RID OF THIS BUFFER IF NOT ENOUGH
35$: MOV #DC3,R0 ; DO DC3,LF
CALL 110$ ; PRINT THE DC3
CALLR PR$LF ; DO A LINE FEED
100$: MOVB (R1),R0 ; GET SINGLE CHARACTER EQUIVALENT
110$: MOVB LEASC(R0),R0 ; Map the character to our set
CALLR FORM$X
FCCTBL: .byte ' ,LF
.byte '*,DC3
.byte '+,CR
.byte '/,DC4
.byte '1,FF
.byte '2,DLE
.byte '3,VT
FCCTBN=<.-FCCTBL>/2
;+
; Horizontal forms control translation
;
; R0/2*<translated character>
;-
FORM$H: ASLB R0 ; TEST FOR TAB OR CR
BPL 10$ ; HT
CLRB L.COCT(R5) ; CLEAR COLUMN COUNT, A CARRIAGE RETURN
MOVB #CR,R0 ; SET CR CODE
CALLR PR$BYT ; PUT IT DOWN
10$: MOVB L.COCT(R5),R0 ; GET CURRENT COLUMN COUNT
MOVB R0,R3 ; NO REAL LIST EXISTS, TAB BY 8'S
BISB #7,R3 ; SET COUNT TO NEXT TAB STOP
INCB R3 ; PLUS 1
CMPB R3,LE.FWD+F.FCV(R5) ; STOP PAST FORM WIDTH?
BLOS 30$ ; NO (BYTES ARE ALL UNSIGNED NUMBERS)
TSTB LE.TRN+F.FCV(R5) ; YES, LINE TRUNCATION ON?
BNE 20$ ; YES, TAB TO JUST EOL
CALL PR$LF ; NO, WRAP TO NEXT LINE
BR 10$ ; NOW DO TAB
20$: MOVB LE.FWD+F.FCV(R5),R3 ; SET TAB STOP JUST AT FORMS WIDTH
30$: MOVB R3,R2 ; SAVE NEW COLUMN COUNT
SUB R0,R2 ; COMPUTE NUMBER OF
MOVB #SPA,R0 ; SPACES TO DO
BIC #177400,R2 ; PREVENT SIGN PROBLEMS
BEQ 60$ ; NONE! (CAN HAPPEN IF TRUNC ON AT EOL)
40$: CALL PR$BYT ; Output a space
SOB R2,40$ ; DO UNTIL DONE
50$: MOVB R3,L.COCT(R5) ; TAB STOP VALUE IS NEW COLUMN COUNT
60$: RETURN
;+
; Vertical forms control translation
;
; R0/2*<translated character>
; FCXTB has translation image of all forms control characters.
;-
FORM$V: CLRB L.COCT(R5) ; CLEAR COL COUNTER ON ALL VFE'S
BIC #177400,R0 ; CLEAR HIGH BYTE OF R0
BNE 10$ ; NOT AN EXPLICIT FORM FEED
CALLR PR$FF ; EXPLICIT FORM FEED
10$: MOV CHMAP(R0),R0 ; GET CHANNEL MAP
MOVB L.LNCT(R5),R1 ; GET THE LINE COUNTER
ASL R1 ; AS A WORD OFFSET
ADD #VFU66,R1 ; Calculate position in VFU
CLR R2 ; Clear a line spacing counter
20$: TST (R1)+ ; UP LINE POINTER, CHECK FOR END OF VFU
BPL 30$ ; NOT AT END OF VFU (MARKED AS MINUS)
CALLR PR$FF ; JUST DO AN FF AND QUIT
30$: INC R2 ; UP THE LINE SPACING COUNTER
BIT R0,(R1) ; STOP MATCH AT CURRENT LINE LOCATION?
BEQ 20$ ; NOT YET
MOVB #LF,R0 ; SET LF CODE IN TO R0
40$: CALL PR$BYT ; Print a line feed
INCB L.LNCT(R5) ; Count lines as we go
SOB R2,40$
RETURN
;+
; Forms control translation
;-
FORM$X: ASLB R0 ; Horizontal or vertical ?
BPL FORM$V ; Vertical
BR FORM$H ; Horizontal
;+
; NURD message builder
;
; R0 Byte to put into buffer
; R5 Database address
;-
MS$BYT: DEC L.MSGS(R5) ; COUNT IT
BLT 20$ ; BUFFER ALREADY FULL
MAP$ L.MSGA(R5) ; MAP TO BUFFER
MOVB R0,@L.MSGA+2(R5) ; INSERT CHAR
INC L.MSGA+2(R5) ; ADVANCE PTR
RETURN
20$: CLR L.MSGS(R5) ; RESET THE COUNT
25$: PUSH$S R4
CALL BF$SDB ; GET A BUFFER
BCS 30$ ; Oops
MOV C.BUF(R4),L.MSGA(R5) ; MAKE IT THE CURRENT BUFFER
MOV C.BUF+2(R4),L.MSGA+2(R5)
MOV C.CNT(R4),L.MSGS(R5)
ENQ$ L.MSGQ ; ADD IT TO END OF LIST
POP$S R4
BR MS$BYT ; NOW STUFF THE CHAR
30$: POP$S R4
CALL ZZ$SLP ; WAIT A SEC AND TRY AGAIN
BR 25$
;+
; Put a byte into the physical output buffer
;-
PR$BYT: DEC L.PLBS(R5) ; Decrement the counter
BLT 10$ ; Buffer, if any, is full already
MAP$ L.PLBP(R5) ; Map it
MOVB R0,@L.PLBP+2(R5) ; Stash byte in physical buffer
INC L.PLBP+2(R5) ; Advance the pointer
RETURN
10$: CLR L.PLBS(R5) ; CORRECT DECREMENT OVERRUN
TST L.PLBP+2(R5) ; PHYSICAL BUFFER IN PROCESS?
BEQ 20$ ; NO, GO GET ONE
CALL PR$DQX ; STUFF THE LP
20$: CALL PR$NEW ; SET UP A NEW BUFFER
BR PR$BYT ; TRY AGAIN
;+
; Put a printing character into the physical output buffer.
;-
PR$CHR: CMPB L.COCT(R5),LE.FWD+F.FCV(R5) ; COL CT LE FORM WIDTH?
BLO 28$ ; YES, OKAY (UNSIGNED COMPARISON)
TSTB LE.TRN+F.FCV(R5) ; LINE TRUNCATION ON?
BNE 35$ ; IF SO, EAT CHARACTERS TIL FC COMES
CALL PR$LF ; FORCE WRAP TO NEXT LINE
28$: TSTB LE.LCR+F.FCV(R5) ; UPPER CASE ONLY ON?
BEQ 30$ ; NO
CMPB R0,#140 ; YES, IS CHAR LOWER CASE?
BLO 30$ ; NO
BIC #40,R0 ; YES, CLEAR UNSHIFT BIT
30$: CALL PR$BYT ; PUT CURRENT CHARACTER TO BUFFER
INCB L.COCT(R5) ; COUNT THE COLUMN
35$: RETURN
;+
; Dequeue and transmit physical buffer.
;-
PR$DQX: DEQ$ L.PLBQ ; Dequeue the physical buffer
MOV .RDBSZ,C.CNT(R4) ; Buffer is originally this big
SUB L.PLBS(R5),C.CNT(R4) ; Subtract unused count
CLR L.PLBP+2(R5) ; Clear current buffer
CLR L.PLBS(R5) ; and size
CALLR $LEQUE ; Send it
;+
; Print a form feed, and adjust counters
;-
PR$FF: PUSH$S R0
MOVB #FF,R0 ; SET FF CHAR
CALL PR$BYT ; TRY TO PUT IT
POP$S R0
CLRB L.LNCT(R5) ; CLEAR LINE COUNT
CLRB L.COCT(R5) ; CLEAR COLUMN COUNTER
RETURN
;+
; Called to return a data CCB.
; R4 CCB
; R5 Database address
;
; The CCB may be a physical buffer that has been printed and may
; have a source buffer pointer in C.STK (to be returned). The
; CCB may be other than a physical buffer, being circulated through
; the driver queue to synchronize its transmission with the printed
; data.
;-
PR$FIN: TSTB C.PIX(R4) ; Physical buffer?
BNE 100$ ; No, a source buffer in disguise
PUSH$S R4 ; Yes, save its address
MOV C.STK(R4),R4 ; Get source buffer address
BEQ 26$ ; None here
CALL 100$ ; Return source buf to owner
26$: POP$S R4 ; Restore physical buffer address
CLR C.STK(R4) ; Assure no pointer left here
BIT #LA.EOF,C.STS(R4) ; Check this buffer for eof condition
BEQ 30$
BIS #LA.CPC,L.ASTS(R5) ; IF SO, SET FLAG TO CLEAR PAGE COUNT
; BEFORE NEXT OUTPUT
30$: BIT #LA.AKR,C.STS(R4) ; MARKED TO ACKNOWLEDGE DATA MESSAGE?
BEQ 45$ ; NO
TSTB L.RATT(R5) ; ATTENTION PENDING?
BEQ 40$ ; NO, SET UP TO ACK
CMPB L.RATT(R5),#N.AACK ; IS CURRENT REASON MORE IMPORTANT?
BHI 45$ ; YES, LET THE PENDING REASON STAND
40$: MOVB #N.AACK,L.RATT(R5) ; NO, USE ACKNOWLEDGE AS THE REASON
CALL SN$ATT ; TRY TO SEND AN ATTENTION
45$: ENQ$ L.PLBQ ; Queue physical buffer for future use
RETURN
; Return a source buffer to NRD
100$: MOV #S.SSUC,C.STS(R4) ; ACK A NRD message
BIT #LA.ABO,L.ASTS(R5) ; Abort in progress ?
BEQ 101$ ; No, serenity
MOV #S.EABO,C.STS(R4) ; Indicate purge
101$: MOVB #FC.RCE,C.FNC(R4) ; Return it
CALLR $SCHED ; ...
;+
; Print a line feed
;-
PR$LF: CLR R1
BISB L.LNCT(R5),R1 ; GET LINE COUNTER BYTE
ASL R1 ; AS WORD OFFSET
ADD #VFU66,R1 ; POINT INTO CURRENT VFU
TST (R1)+ ; UP LINE POINTER
BMI PR$FF ; SKIP IF PAST END OF TABLE
BIT #VFBP8,(R1) ; ***TABLE SPECIFIC HACK
BEQ PR$FF ; APPARENTLY IN MARGIN, GO DO TOF
PUSH$S R0
MOVB #LF,R0 ; SET CHAR
CALL PR$BYT ; DO IT
POP$S R0
INCB L.LNCT(R5) ; UP LINE COUNTER
CLRB L.COCT(R5) ; CLEAR COL CTR WHEN GOING TO NEW LINE
RETURN ; CARRY STAYS AS SET BY LAST CALL
;+
; Set up new physical output buffer
;-
PR$NEW: MOV L.PLBQ(R5),R4 ; IS ONE ENQUEUED?
BEQ 10$ ; NO
CLR C.STS(R4) ; INIT STATUS
MOV C.BUF(R4),L.PLBP(R5) ; SET NEW ADDRESS DOUBLEWORD
MOV C.BUF+2(R4),L.PLBP+2(R5)
MOV .RDBSZ,L.PLBS(R5) ; SET BUFFER MAX SIZE
RETURN
10$: CALL ZZ$SLP ; SLEEP TIL A BUFFER MEANDERS BACK
BR PR$NEW
;+
; Check for room in current buffer. Start new buffer if needed.
; R0 Number of bytes of space needed
;-
PR$SPC: TST L.PLBP+2(R5) ; BUFFER IN USE ?
BEQ 20$ ; NOPE - GO GET ONE
CMP R0,L.PLBS(R5) ; ROOM ?
BGT 10$
RETURN
10$: CALL PR$DQX ; NO ROOM - FLUSH CURRENT
20$: CALLR PR$NEW ; AND GET NEW ONE
;+
; Purge the queues
;
; Abort all from <DDMQ>, <DLCQ>, and <MSGQ> (in that order).
; Assumes that printer is stopped.
;-
Q$PURG: PUSH$S <R0,R1,R3,R4> ; SAVE CURRENT CCB, DATABASE ADDR, ETC.
10$: DEQ$ L.DDMQ,20$ ; RETURN PHYSICAL BUFFERS TO THE PB Q
CALL PR$FIN ; RETURN ANY MESSAGE BUFS
BR 10$ ; AND RESTORE PHYSICAL BUFS TO LIST
20$: DEQ$ L.DLCQ,30$ ; Abort messages waiting to be done
MOV #S.EABO,C.STS(R4) ; ...
MOVB #FC.RCE,C.FNC(R4) ; ...
CALL $SCHED ; ...
BR 20$ ; Look on
30$: DEQ$ L.MSGQ,40$ ; FLUSH ANYTHING GOING OUT
MOV #80.,R1 ; SET SIZE OF BUFFER
CALL $CBBRT ; RETURN IT
BR 30$
40$: CLRB L.DTXL(R5) ; FLUSH ANY SUSPENDED PROCESSING
CLR L.PUBS(R5) ; NO CURRENT PRINT BUFFER
CLR L.PLBP+2(R5) ; NO PHYSICAL BUFFER IN USE
CLR L.PLBS(R5)
CLR L.DMAD+2(R5) ; NO CURRENT SOURCE BUFFER
CLR L.DMAD(R5)
CLR L.DMBS(R5) ; MAY HAVE BEEN A BUFFER IN PROGRESS
CLR L.MSGS(R5) ; ...
POP$S <R4,R3,R1,R0>
RETURN
;+
; Read a data byte.
;-
RD$BYT: DEC L.DMBS(R5) ; Decrement counter
BLT 5$ ; Buffer, if any, is drained
MAP$ L.DMAD(R5) ; Map it
CLR R0 ; Get a byte from buffer
BISB @L.DMAD+2(R5),R0 ; ...
INC L.DMAD+2(R5) ; Advance source pointer
RETURN ; Done
5$: TST L.DMAD+2(R5) ; IS A BUFFER IN PROCESS?
BEQ 10$ ; NO BUFFER IN USE
MOV L.DLCQ(R5),R4 ; CHECK FOR MESSAGE CONTINUITY
BITB #S$PEOM,C.PRM1+1(R4)
BEQ 9$
; A message fragment, can't continue
CALL $LESTP ; Stop printer interrupts
BIS #LA.ADN,L.ASTS(R5) ; ABORT LINK WHEN DDM QUEUE IS EMPTY
9$: CALL RD$DQX ; EMPTIED BUFFER ON TOP
BIT #S$PEOM,R0 ; CHECK IF EOM
BEQ 10$ ; NO
MOV L.DFSP(R5),SP ; YES, RECOVER STACK POINTER
RETURN ; SO WE CAN QUIT
; No buffer, so set one up
10$: MOV L.DLCQ(R5),R4 ; IS THERE A QUEUED ENTRY?
BNE 15$ ; YES
CALL ZZ$SLP ; WAIT TIL NEXT SEGMENT ARRIVES
BR 10$
15$: MOV C.BUF(R4),L.DMAD(R5) ; GET ADDRESS DOUBLEWORD AS PTR
MOV C.BUF+2(R4),L.DMAD+2(R5); ...
MOV C.CNT(R4),L.DMBS(R5) ; GET BUFFER COUNT AS SIZE COUNTER
BR RD$BYT ; TRY AGAIN
;+
; Dequeue and return a source buffer to NRD
;-
RD$DQX: CLR L.DMAD+2(R5) ; Indicate "no buffer in use"
CLR L.DMBS(R5) ; ...
DEQ$ L.DLCQ ; Dequeue message just don
MOVB C.PRM1+1(R4),R0 ; Remember EOM status
TST L.PLBP+2(R5) ; Physical buffer in use ?
BEQ 10$ ; No, have to make do without one
PUSH$S R4 ; Yes, save the buffer address
MOV L.PLBQ(R5),R4 ; Get current physical buffer address
TST C.STK(R4) ; Already have a stacked message?
BEQ 20$ ; No, so go ahead and stack on it
POP$S R4 ; Yes, so just stick it directly on que
10$: CALLR $LEQUE ; ...
20$: POP$S C.STK(R4) ; Ok, tie it to physical buffer
CALLR PR$DQX ; Queue physical buffer to printer
;+
; Send alert message
;-
SN$ALR: CALL BF$SDB ; Get a message buffer
BCS 10$ ; Oops
MOV C.BUF+2(R4),R1 ; Get buffer pointer
MOVB #NM.ALR,(R1)+ ; Complete alert message (1 word)
CLRB (R1)+ ; Clear NURD message flags
MOV #2,C.CNT(R4) ; Set message length
MOVB #S$SNI,C.MOD(R4) ; Alert is an interrupt message
INC L.IOCN(R5) ; Count the I/O
CALL $SCHED ; and send it
10$: RETURN
;+
; Send attention message
;
; Allocate an SDB and build attention message.
; Reason code is in L.RATT.
;-
SN$ATT: PUSH$S <R0,R1,R2,R3,R4> ; Preserve registers
MOVB L.RATT(R5),R2 ; GET EXCUSE CODE FOR ATTENTION MESSAGE
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 MESSAGE TYPE TO BUFFER
CLRB (R1)+ ; CLEAR NURD MESSAGE FLGS
MOVB L.DMSQ(R5),(R1)+ ; STORE LAST GOOD SEQUENCE NUMBER
MOVB R2,(R1)+ ; LOAD ATTENTION CODE
CLR R2 ; CLEAR FLAGS ACCUMULATOR
MOV L.ASTS(R5),R0 ; GET DEVICE STATUS
BIT #LA.ADN,R0 ; ABORT WHEN DONE ?
BEQ 10$
BIS #NA.FAT,R2 ; SOME FATAL ERROR - USUALLY USER'S
10$: BIT #LA.ERR,R0 ; PRINTER ERROR ON?
BEQ 11$ ; NO
BIS #NA.OFL,R2 ; YES, NOTE OFFLINE
11$: BIT #LA.PAU,R0 ; PAUSED?
BEQ 20$ ; NO
BIS #NA.PAU,R2 ; YES
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 #LA.TMO,R0 ; HUNG?
BEQ 30$ ; NO
BIS #NA.DTO,R2 ; YES
30$: BIS #200,R2 ; TENTATIVELY SET EXTEND FLAG
MOVB R2,(R1)+ ; THAT'S IT FOR BYTE 2
CLR R2 ; CLEAR FOR BYTE 3 FLAGS
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 L.PGCT(R5),R3 ; GET PAGE 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 MESSAGE SIZE
MOV R1,C.CNT(R4) ; STUFF IT INTO CCB
INC L.IOCN(R5) ; Count the I/O
CALL $SCHED ; and send it
CLRB L.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 MESSAGE BUFFER
BCS 100$
MOV C.BUF+2(R4),R3 ; GET BUFFER ADDRESS
; FORMAT A NURD MESSAGE
MOVB #NM.CAP,(R3)+ ; NURD MESSAGE 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 #L.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 MESSAGE
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 MESSAGE
INC L.IOCN(R5) ; Count the I/O
CALL $SCHED ; and send it
CLRB L.CAPP(R5) ; FLUSH THE FLAG
100$: RETURN
;+
; Data / Features HIBER Function
;
; Called by process to wait for a clock tick.
;-
ZZ$SLP: MOV R0,L.DTXT(R5) ; STORE THE REGISTERS 1ST
MOV #L.DTXT+2,R0
ADD R5,R0 ; R0 -> R1 SLOT
MOV R1,(R0)+
MOV R2,(R0)+
MOV R3,(R0)+
MOV R4,(R0)+
CLRB L.DTXL(R5) ; INIT THE COUNT
10$: CMPB #DTXTSZ-5,L.DTXL(R5) ; CHECK INCIPIENT OVERFLOW
BHI 20$
BPT ; PROGRAM BUG
20$: MOV (SP)+,(R0)+ ; XFER NEXT STACK WORD
INCB L.DTXL(R5) ; COUNT IT
CMP L.DFSP(R5),SP ; CHECK ORIGIN
BNE 10$ ; MORE
RETURN
;+
; Clock has ticked.
;-
ZZ$WAK: MOV SP,L.DFSP(R5) ; SET THE NEW STACK ORIGIN
CLR R0
BISB L.DTXL(R5),R0 ; GET NO. OF WORDS TO RETURN TO STACK
ASL R0
ADD #L.DTXT+12,R0
ADD R5,R0 ; R0 -> 1ST WORD TO RETURN TO STACK
10$: MOV -(R0),-(SP) ; XFER THE STACK CONTENTS
DECB L.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
;-
LEPBFS = 6 ; NUMBER OF PHYSICAL BUFFERS TO HOARD
LEHNG = 5 ; HUNG DEVICE TIME CONSTANT
;+
; LP11 hardware device register definitions
;-
LPS = 0 ; LINE PRINTER STATUS REGISTER
ERROR = 100000 ; GENERAL (ONLY KNOWN) ERROR BIT
DONE = 200 ; CHARACTER DONE/PRINTER READY
INTENB = 100 ; INTERRUPT ENABLE
LPB = 2 ; LINE PRINTER DATA BUFFER REGISTER
; SEVEN BIT ASCII, WRITE ONLY
LESZ = 4 ; SIZE OF LP11 REGISTER BLOCK IN BYTES
;+
; Queue output image buffers to be sent to LPT.
; Start interrupt processing if appropriate
;
; R4 CCB
; R5 Database address
;-
.enabl LSB
$LEQUE: CALL $LESTP ; FLUSH INTERRUPTS WHILE WE DIDDLE Q'S
ENQ$ L.DDMQ ; QUEUE UP INCOMING OUTPUT BUFFER
; START THE DEVICE IF IT SHOULD BE
$LESTR: BIT #LA.PAU,L.ASTS(R5) ; ARE WE PAUSED?
BNE 10$ ; EVERYTHING IS QUIESENT
$LEGO: MOVB #LEHNG,L.HUNG(R5) ; SET THE HUNG TIMER
MOV #INTENB,@L.CSR(R5) ; GOOSE IT
10$: RETURN
.DSABL LSB
$LESTP: CLR @L.CSR(R5) ; STOP THE INTERRUPTS
CLRB L.HUNG(R5) ; AND FLUSH THE HUNG TIMER
RETURN
;+
; Field interrupts:
; Dispatch to error if error set,
; alternate error if pause set,
; else output next character.
; If last character of buffer sent, go to interrupt on done
; to return used buffer and start next one.
;
; R5 Database address
;-
$LEINT::BIT #LA.INT,L.ASTS(R5) ; Already handling interrupt ?
BEQ 1$ ; No, so go to it
RETURN ; Yes, so dismiss this one
1$: BIS #LA.INT,L.ASTS(R5) ; Say we are processing an interrupt
CLRB L.HUNG(R5) ; Reset the hung timer
MOV R5,R4 ; Build pointer to SYNCH block
ADD #L.SYN,R4 ; ...
MOV #5$,S.DSP(R4) ; Set return address
CALLR $SYNCH ; Go
; Here at synch level
5$: BIC #LA.INT,L.ASTS(R5) ; Clear interrupt level stuff
MOV L.CSR(R5),R3 ; Get CSR address
CLR (R3) ; Turn off printer
BIT #LA.PAU,L.ASTS(R5) ; Ought we pause?
BNE INXPAU ; Yes
MAP$ L.PUBP(R5) ; Set mapping for present output
MOV L.PUBP+2(R5),R2 ; R2 / Buffer pointer
MOV L.PUBS(R5),R1 ; R1 / Buffer count
BEQ 40$ ; An empty one !
10$: TST (R3) ; Check the printer status
BGT 15$ ; All is ok
MOV R1,L.PUBS(R5) ; Some problem, we will wait a bit
MOV R2,L.PUBP+2(R5) ; Save the state
MOV #LA.ERR,R0 ; Assume an error
TST (R3) ; Ehh ?
BMI INTPAU ; An error
MOVB #LEHNG,L.HUNG(R5) ; Full up, so reset the hung timer
MOV #INTENB,(R3) ; Restart the device
BR LEXIT ; Wait till later
15$: MOVB (R2)+,R0 ; Printer is ready, get next character
CALL LEPRT ; and print it
BCC 20$ ; Jump ahead if it went ok
DEC R1 ; Page-limit exceeded. FF went out
MOV R1,L.PUBS(R5) ; Save state information
MOV R2,L.PUBP+2(R5) ; ...
MOVB #N.APLE,L.RATT(R5) ; Request attention message
MOV #LA.PLE,R0 ; Set status correctly
BR INTPAU ; Get on out
20$: SOB R1,10$ ; Keep going while printer is hungry
30$: DEQ$ L.DDMQ ; Take just-finished buffer off queue
CALL PR$FIN ; Return buffer
40$: MOV L.DDMQ(R5),R4 ; Get address of top element of queue
BEQ 60$ ; None there, exit
TSTB C.PIX(R4) ; Check for circulating source buffer
BNE 30$ ; If so, just return it
BIT #LA.CPC,L.ASTS(R5) ; Check if last buffer had EOF
BEQ 50$ ; No
CLR L.PGCT(R5) ; Yes - clear page counter
BIC #LA.CPC,L.ASTS(R5) ; Reset the flag
50$: MAP$ C.BUF(R4) ; Set mapping
MOV C.BUF(R4),L.PUBP(R5) ; and save it too
MOV C.BUF+2(R4),R2 ; Buffer pointer
MOV C.CNT(R4),R1 ; Buffer length
BEQ 30$ ; Empty buffers are empty
MOV L.CSR(R5),R3 ; Get CSR address again
BR 10$ ; Continue where we left off
60$: CLR L.PUBS(R5) ; Clear L.PUBS for next startup
BIT #LA.ADN,L.ASTS(R5) ; Check for auto death
BEQ LEXIT ; Nope
CLR R0 ; Yes - pause now
; BR INTPAU ; ...
INTPAU: BIS #LA.PAU,R0 ; Always have this bit lit
BIS R0,L.ASTS(R5) ; Put whole thing into database
INXPAU: TSTB L.RATT(R5) ; Attention already pending?
BNE LEXIT ; Yes, don't overwrite reason code
MOVB #N.ASTC,L.RATT(R5) ; No, note status change
CALL SN$ATT ; Build and send attention message
LEXIT: RETURN ; Leave
; Printer output routine
LEPRT: CMPB #LF,R0 ; CHECK FOR LF'S AND FF'S
BNE 30$
MOVB R0,2(R3) ; LF - PRINT IT
INCB L.PLNC(R5) ; COUNT IT
CMPB #PGLEN,L.PLNC(R5) ; CHECK FOR PAGE BOUNDARY
BGT 50$ ; OK
20$: CLRB L.PLNC(R5) ; PAGE - CLEAR LINE COUNT
INC L.PGCT(R5) ; COUNT THE PAGE
TST L.PGLM(R5) ; CHECK PAGE LIMIT
BEQ 50$ ; NOT SET
CMP L.PGCT(R5),L.PGLM(R5) ; COMPARE COUNT TO LIM
BLE 50$ ; STILL OK
SEC ; TOO BAD
RETURN
30$: CMPB #FF,R0 ; CHECK FF
BNE 40$ ; NOT A CRITICAL CHARACTER
CMPB #PGLEN-1,L.PLNC(R5) ; CHECK FOR LAST LINE
BLE 35$ ; BOF - FF IS SPECIAL CASE
MOVB R0,2(R3) ; Not BOF - Print FF and count page
BR 20$
35$: MOVB #LF,2(R3) ; LINE FEED AT BOF TO AVOID BLANK PAGE
BR 20$ ; COUNT THE PAGE
40$: MOVB R0,2(R3) ; JUST PRINT THE CHARACTER
50$: CLC
RETURN
.sbttl Miscellaneous Tables
;+
; The following table defines the default contents for the LP11
; vertical format unit (VFU) for a 66 line page.
; NOTE: All VFU actions also perform carriage return action.
;
; Channel 1 - (top of form) line 1 only
; Channel 2 - half of form stop
; Channel 3 - every other line from line 1 up to and including line 59
; Channel 4 - every third line from line 1 up to and including line 58
; Channel 5 - every line
; Channel 6 - every tenth line from line 1 up to and including line 51
; Channel 7 - every 20th line from line 1 up to and including line 41
; Channel 8 - next line in form
;
; Standard extension to standard vertical format unit
; Channel 9 - every fifth line from line 1 up to and including line 56
; Channel 10 - quarter of form stops
; Channel 11 - alternate top of form
; Channel 12 - (bottom of form) line 60 at 6 lpi
;-
; Set forms info
PGLEN = 66. ; 66 lines per page
FRMLEN = 60. ; 60 lines of form
; Name the FCXTB flags
FCCHR = 200 ; CHARACTER IS A FORMS CONTROL CHARACTER
HFCHR = 100 ; CHARACTER IS A HORIZONTAL CONTROL CHARACTER
HFCCR = 40 ; CHARACTER IS A CARRIAGE RETURN CHARACTER
; Support Macros
.macro .ZER $$
.P'$$=0
.endm .ZER
.macro SETCHN A,B ; 'OR' new channel setting into map word
.P'B = .P'B ! A
.endm SETCHN
.macro WRDVFU X
.word .P'X
.endm WRDVFU
.macro DEFPAR CHN,LINC,LAST
$$ = 1
.rept LAST/LINC ; Number of stops in channel
SETCHN CHN,\$$ ; 'OR' in next stop
$$ = $$ + LINC
.endr
.endm DEFPAR
.if ndf VCH1 ; DEFINE VFU CHANNEL TAGS
$$ = 1
.irp X,<1,2,3,4,5,6,7,8,9,10,11,12>
VCH'X = X'. - 1 ; NUMERIC CHANNEL TAGS
VFBP'X = $$ ; POSITIONAL CHANNEL TAGS
$$ = $$*2
.endr
.endc
.if ndf .P1
$$=0 ; INIT THE LINE SYMBOLS .P<NN>
.rept PGLEN
$$=$$+1
.ZER \$$
.endr
; SET THE MASK BITS IN THE PARTITION WORDS ACCORDING TO
; THE FORMULA DEFINED IN THE ARGUMENTS TO DEFPAR
DEFPAR VFBP1,1,1 ; TOP OF FORM
DEFPAR VFBP2,<<FRMLEN/2>>,FRMLEN ; HALF OF FORM STOPS
DEFPAR VFBP3,2.,FRMLEN ; EVERY OTHER LINE
DEFPAR VFBP4,3.,FRMLEN ; EVERY THIRD LINE
DEFPAR VFBP5,1.,PGLEN ; EVERY LINE TO END
DEFPAR VFBP6,10.,FRMLEN ; EVERY TENTH LINE
DEFPAR VFBP7,20.,FRMLEN ; EVERY TWENTIETH LINE
DEFPAR VFBP8,1.,FRMLEN ; SINGLE SPACE IN FORM
DEFPAR VFBP9,5.,FRMLEN ; EVERY FIFTH LINE
DEFPAR VFBP10,<<FRMLEN/4>>,FRMLEN ; QUARTER OF FORM STOPS
DEFPAR VFBP11,1,1 ; ALTERNATE TOP OF FORM
.P75 = .P75 ! VFBP12 ; SPECIAL CASE FOR BOF
.P102 = .P102 ! 100000 ; FLAG LAST WORD OF VFU IMAGE TABLE
.endc
.sbttl Tables
;*******
; USE THE DEFINED PARTITION WORDS TO BUILD A COMPLETE VFU IMAGE, AND
; ASSIGN PARTITIONS FOR THE WHOLD PAGE.
VFU66: $$ = 0
.rept PGLEN
$$=$$+1
WRDVFU \$$
.endr ; R PGLEN
VFU66S = .-VFU66 ; SIZE OF VFU IMAGE
; $LERAM FCT INDEX TO CHANNEL MAP
CHMAP: .word VFBP1 ; 0
.word VFBP2 ; 1
.word VFBP3 ; 2
.word VFBP4 ; 3
.word VFBP5 ; 4
.word VFBP6 ; 5
.word VFBP7 ; 6
.word VFBP8 ; 7
.word VFBP9 ; 8
.word VFBP10 ; 9
.word VFBP11 ; 10
.word VFBP12 ; 11
.word 0 ; 12 UNUSED ENTRY (NO STOPS SET)
.word 0 ; 13 UNUSED ENTRY (NO STOPS SET)
.word 0 ; 14 UNUSED ENTRY (NO STOPS SET)
.word 0 ; 15 UNUSED ENTRY (NO STOPS SET)
; LP11 CHARACTER TRANSLATION MAP, INITIALIZED TO NURD ASCII BY OPEN.
; POTENTIALLY CHANGED BY FEATURES SETS. ROOM FOR A FULL SEVEN-BIT CODE.
LEASC: .blkb 128.
.even
; FORMS CONTROL EXCEPTION TABLE
; FORMAT: .byte <LEASC OFFSET>,<TRANS CONTENTS>
FCXTB: .byte HT,FCCHR!HFCHR
.byte LF,FCCHR!VCH8
.byte VT,FCCHR!VCH7
.byte FF,FCCHR!0
.byte CR,FCCHR!HFCHR!HFCCR
.byte DLE,FCCHR!VCH2
.byte DC1,FCCHR!VCH3
.byte DC2,FCCHR!VCH4
.byte DC3,FCCHR!VCH5
.byte DC4,FCCHR!VCH6
; .byte FS,FCCHR!VCH9 ; Channels 9-12 not supported
; .byte GS,FCCHR!VCH10
; .byte RS,FCCHR!VCH11
; .byte US,FCCHR!VCH12
.word -1 ; Table ends on negative offset byte
.end