.MCALL .MODULE .MODULE LP,VERSION=14,COMMENT=,AUDIT=YES ; Copyright (c) 1998 by Mentec, Inc., Nashua, NH. ; All rights reserved ; ; This software is furnished under a license for use only on a ; single computer system and may be copied only 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 except for use on such ; system and to one who agrees to these license terms. Title ; to and ownership of the software shall at all times remain ; in Mentec, Inc. ; ; The information in this document is subject to change without ; notice and should not be construed as a commitment by Digital ; Equipment Corporation, or Mentec, Inc. ; ; Digital and Mentec assume no responsibility for the use or ; reliability of its software on equipment which is not supplied ; by Digital or Mentec, and listed in the Software Product ; Description. ;++ ; Facility: RT-11 Line Printer Handler ; ; Author: ; ; Abstract: With conditionals appropriately defined, this module ; can be assembled and linked to produce the LP handler ; for unmapped or mapped versions of RT-11. The hardware ; interfaces supported are: ; ; PDP-11 LP11 (M7258) ; LSI-11 LPV11 (M8027) ; ; Edit Who Date Description of modification ; ---- --- ---- --------------------------- ; 001 WLD 08-FEB-90 Eliminate unwanted LF or FF ; (depending upon SET LP ENDPAG=?) ; at recovery CPU recovery from ; power fail or abort while printer ; is powered down/off-line. ; This problem has been reported only ; on an LA-180 with a UNIBUS (LP11) ; interface. It has been reproduced ; on an LP05. ; Upon INIT, the LP11 directs the LP ; to print the character last stuffed ; into its buffer. Typically, this ; character is a LF (SET LP ENDPAG=0) ; or a FF (SET LP ENDPAG=x, x>0) from ; completion of the last page printed. ; Hence, there is the unwanted paper ; motion described above. ; The solution implemented below is to ; stuff a CR into the LP data buffer ; upon as the final step in processing ; a .CLOSE or ABORT. ;-- .SBTTL CONDITIONAL ASSEMBLY SUMMARY ;+ ;COND ; OFORM (1) Support form operations ; 0 no ; 1 yes ; ; LS.CSZ (132.) default width ; LS.PGZ (66.) default length ; ; LS$CSR (177514) CSR ; LS$VEC (200) Vector ; ; MMG$T std conditional ; TIM$IT std conditional (no code effects) ; ERL$G std conditional (no code effects) ;- .SBTTL MACROS AND DEFINITIONS .MCALL .DRDEF .ASSUME .DRDEF LP,3,WONLY$!SPECL$,0,177514,200,DMA=NO .DRPTR .DREST CLASS=DVC.LP .IIF NDF LP.CSZ, LP.CSZ =: 132. ;DEFAULT COLUMN SIZE (WIDTH) .IIF NDF LP.PSZ, LP.PSZ =: 66. ;DEFAULT PAGE SIZE .IIF NDF OFORM, OFORM =: 1 ;INCLUDE FORM PROCESSING CODE ; PRINTER STATUS REGISTER BITS IE =: 100 ;INTERRUPT ENABLE RDY =: 200 ;PRINTER READY ERR =: 100000 ;PRINTER ERROR ; ASCII CONSTANTS HT =: 11 LF =: 12 FF =: 14 CR =: 15 CTRLQ =: 'Q-100 CTRLS =: 'S-100 SPACE =: 40 COLSIZ == LP.CSZ PAGSIZ == LP.PSZ CLOS.. =: 1 ;.CLOSE request code LOOK.. =: 3 ;.LOOKUP request code ENTE.. =: 4 ;.ENTER request code .SBTTL INSTALLATION CODE and SET CODE overflow .DRINS LP .=300 ; Space for more SET code I.CSR: MOV R0,INSCSR ;SET NEW CSR FOR CHECKING MOV R0,LPS ;SET NEW CSR FOR HANDLER MOV R0,DISCSR ;SET DISPLAY CSR ADD #2,R0 ;POINT TO BUFFER REGISTER MOV R0,LPB ;SET POINTER TO THAT BR CMPR03 ;GIVE ERROR IF LESS THAN 160000 .ASSUME . LE 400 .SBTTL SET OPTION PARAMETER TABLE .DRSET WIDTH,30.,O.WIDTH,NUM ;MINIMUM WIDTH 30, NUMBER REQUIRED .DRSET CR,NOP,O.CR,NO ;'NOP' INSTR, ALLOW 'NOCR' .DRSET FORM0,NOP,O.FORM0,NO ;'NOP' INSTR, ALLOW 'NOFORM0' .DRSET HANG,,O.HANG,NO ;INSTR, ALLOW 'NOHANG' .DRSET LC,40,O.LC,NO ;MASK FOR LC->UC CONVERT, ALLOW 'NOLC' .DRSET CTRL,,O.CTRL,NO ;INSTR, ALLOW 'NOCTRL' .DRSET TAB,,O.TAB,NO ;INSTR, ALLOW 'NOTAB' .DRSET CSR,160000,O.CSR,OCT ;MINIMUM VALUE FOR CSR .DRSET VECTOR,477,O.VEC,OCT ;MAXIMUM VALUE FOR VECTOR .DRSET ENDPAG,-1,O.ENDP,NUM ;Number of FFs at EOF .DRSET BIT8,<^c177>,O.BIT8,NO ;Pass eighth bit thru, don't if NOBIT8 .IF NE OFORM .DRSET FORM,100000!LF,O.FF,NO ;Simulate FF with LFs .DRSET LENGTH,1,O.LENG,NUM ;Length of simulated page .DRSET SKIP,O$LENG,O.SKIP,NUM ;Skip perforations .ENDC ;NE OFORM .SBTTL SET OPTION PROCESSING ROUTINES .ENABL LSB O.CSR: BR I.CSR ;Code moved to install area O.WIDT: MOV R0,COLCNT ;MOVE NEW WIDTH TO COUNTER MOV R0,O$WIDT ; AND TO RESET VALUE CMPR03: CMP R0,R3 ;ERROR IF < 30. RETURN ;Note: SET CTRL overrides SET NOCR O.CR: MOV (PC)+,R3 ;'NO' NOT GIVEN, SO SET TO SEND CR BEQ RSTC-O$CR+. ;WHEN CR IS FOUND, GO SEND IT N.CR: MOV R3,O$CR ;SET CR OPTION .ASSUME O.CR+4 EQ N.CR RETURN O.FORM0:MOV (PC)+,R3 ;SET TO DO FORMFEEDS ON BLOCK 0 BEQ BLK0-O$FORM+. N.FORM0:MOV R3,O$FORM .ASSUME O.FORM0+4 EQ N.FORM0 RETURN O.HANG: MOV (PC)+,R3 ;SET TO HANG BMI RET-O$HANG+. N.HANG: MOV R3,O$HANG .ASSUME O.HANG+4 EQ N.HANG RETURN O.LC: CLR R3 ;FOR 'LC', LEAVE LOWER CASE STUFF ALONE NOP N.LC: MOV R3,O$LC .ASSUME O.LC+4 EQ N.LC RETURN O.CTRL: MOV (PC)+,R3 ;SET TO PASS ALL NON-PRINTING CHAR BNE PRINTC-O$CTRL+. N.CTRL: MOV R3,O$CTRL .ASSUME O.CTRL+4 EQ N.CTRL RETURN O.TAB: MOV (PC)+,R3 ;PASS TAB DIRECTLY TO LP BEQ HDWTAB-O$TAB+. N.TAB: MOV R3,O$TAB .ASSUME O.TAB+4 EQ N.TAB RETURN O.VEC: MOV R0,LPSTRT ;SET NEW VECTOR CMPR30: CMP R3,R0 ;SET CARRY IF ABOVE 500 RETURN O.ENDP: MOV R0,O$ENDP ;Set number of FFs at EOF RETURN O.BIT8: MOV #^c377,R3 ;Don't mask high bit N.BIT8: MOV R3,O$BIT8 ;Move proper mask .ASSUME O.BIT8+4 EQ N.BIT8 RETURN .IF NE OFORM O.FF: MOV (PC)+,R3 ;HARDWARE FORM FEED PRESENT .WORD FF ;SEND FORM FEED N.FF: MOV R3,O$FF ;SET IT .ASSUME O.FF+4 EQ N.FF RETURN O.LENG: MOV R0,O$LENG ;SET FORM LENGTH VALUE MOV R0,LINCTR ;SET LINE COUNTER BR CMPR03 ;ERROR IF LESS THAN 1 O.SKIP: TST R0 ;IS HE TURNING IT OFF? BEQ 2$ ;YES, GO SET AND CHECK A 0 INC R0 ;INCREMENT VALUE TO PLANT 2$: MOV R0,O$SKIP ;PLANT THE SKIP VALUE BR CMPR30 ;Range check it .ENDC ;NE OFORM .ASSUME . LE 1000 .DSABL LSB .SBTTL DRIVER ENTRY .DRBEG LP MOV LPCQE,R4 ;POINT TO CURRENT QUEUE ELEMENT TSTB Q$FUNC(R4) ;File operation? BMI LPDONE ;SPFUN? None are supported. CMPB Q$FUNC(R4),#CLOS.. ; What kind of operation? BEQ RET ; If CLOSE, cause interrupt BGT NOTHNG ; If LOOKUP, ENTER or PURGE, do NOTHING. ASL Q$WCNT(R4) ;CONVERT WORD COUNT TO BYTE COUNT BEQ LPDONE ;SEEKS COMPLETE IMMEDIATELY BCC LPERR ;A READ REQUEST IS ILLEGAL RET: BIS #IE,@LPS ;CAUSE AN INTERRUPT, STARTING TRANSFER RETURN .SBTTL OPERATION COMPLETE ; .CLOSE invoked after at least one .WRITE or ABORT executed: force last ; character printed to be a CR to prevent further paper motion. LPCLOS: CLR @LPS ;Disable interrupts. MOVB #CR,@LPB ;Stuff CR -- should be ok since READY ;flag in LP status register is up. BR LPEXIT ;Otherwise, exit normally, without error. ; ; If (Q$WCNT * 2) = # characters to print is non-negative (.READ invoked ; for LP), exit with error. LPERR: BIS #HDERR$,@-(R4) ;SET HARD ERROR BIT ; ; If Q$FUNC is not .WRITE or .CLOSE, exit with no I/O action at LPDONE. ; LPEXIT is defined for LPCLOS (see above). ; Also, if a .CLOSE is invoked without at least one .WRITE, exit at LPDONE. LPDONE: CLR @LPS ;TURN OFF INTERRUPT LPEXIT: CLR LPFBLK+2 ;DON'T DISPATCH TO FORK ROUTINE IF ABORTING .DRFIN LP .SBTTL INTERRUPT SERVICE .ENABL LSB .DRAST LP,4,LPCLOS MOV LPCQE,R4 ;SET UP R4 IN CASE 'NOHANG' BRANCH TAKES BIT #ERR!RDY,@(PC)+ ;READY OR ERROR CONDITION ON PRINTER? LPS=:. ;Set by SET CSR=n .WORD LP$CSR ;LINE PRINTER STATUS REGISTER .Assume . LE LPSTRT+1000,MESSAGE=<;Set code object not in block1> O$HANG=:. ;Set by SET [NO]HANG BMI RET ;IF ERROR, HANG TILL CORRECTED (SET HANG) ; BMI LPERR ;IF ERROR, JUST GIVE AN ERROR (SET NOHANG) .Assume . LE LPSTRT+1000,MESSAGE=<;Set code object not in block1> BEQ RET ;IF NOT READY, RETURN FROM INTERRUPT CLR @LPS ;YES, DISABLE INTERRUPTS .FORK LPFBLK ;GO TO FORK LEVEL MOV LPCQE,R4 ;POINT TO CURRENT QUEUE ELEMENT MOVB Q$FUNC(R4),R5 ;Load request code BEQ WRITE ;0 means .READ/.WRITE BMI LPDONE ;- means .SPFUN (none to do) CMPB #CLOS..,R5 ;.CLOSE? BEQ 25$ ;Yes CMPB #100!CLOS..,R5 ;Trailing FF function? BEQ DOFF ;Yes CMPB #ENTE..,R5 ;.ENTER? BEQ 24$ ;Yes CMPB #LOOK..,R5 ;.LOOKUP? BNE LPDONE ;No, ignore all others 24$: NOTHNG: CLR DIDIO ;Indicate no I/O yet BR LPDONE ;Finished for this file operation 25$: DIDIO=:.+2 TST #0 ;Any I/O done before this close? BEQ LPDONE ;No, then done O$ENDP=:.+2 ;Number of FFs at EOF from SET ENDPAG=n MOV #0,Q$WCNT(R4) ;Put in WCNT entry .Assume . LE LPSTRT+1000,MESSAGE=<;Set code object not in block1> BEQ LPCLOS ;Finished, no trailing FFs MOVB #100!CLOS..,Q$FUNC(R4) ;Change request code to strange value BR DOFF ;Do the first one WRITE: MOV SP,DIDIO ;Indicate a write was done TST @R4 ;IS THIS BLOCK 0? O$FORM0=:. ;Set by SET [NO]FORM0 BEQ BLK0 ;IF SO, PRINT INITIAL FORM FEED (SET FORM0) ; NOP ;IF SO, SO WHAT (SET NOFORM0) .Assume . LE LPSTRT+1000,MESSAGE=<;Set code object not in block1> LPNEXT: TSTB @LPS ;READY FOR ANOTHER CHARACTER YET? BPL RET ;NOPE, RETURN FROM INTERRUPT ASLB (PC)+ ;IS TAB EXPANSION IN PROGRESS? TABFLG: .WORD 0 BNE TAB ;IF SO, GO SEND A SPACE DOFF: .IF NE OFORM MOV (PC)+,R5 ;ARE WE EXPANDING A FORM FEED? FFFLAG: .WORD 0 ;POSITIVE IF NOT, 100012 IF YES BMI DOFORM ;YES, SEND THIS LINE FEED .ENDC ;NE OFORM CMPB #100!CLOS..,Q$FUNC(R4) ;Trailing FF function? BNE IGNORE ;No DEC Q$WCNT(R4) ;Reduce count by 1 BGE BLKZ ;Send another FF BR LPCLOS ;Or done IGNORE: TST Q$WCNT(R4) ;ANOTHER CHARACTER TO TRANSFER? BEQ LPDONE ;NO, THIS REQUEST IS DONE .IF EQ MMG$T MOVB @Q$BUFF(R4),R5 ;GET NEXT CHARACTER (IF ANY) INC Q$BUFF(R4) ;BUMP THE BUFFER POINTER .IFF CALL @$GTBYT ;GET A BYTE FROM USER BUFFER MOV (SP)+,R5 ; INTO R5 .ENDC ;EQ MMG$T INC Q$WCNT(R4) ;BUMP CHARACTER COUNT O$BIT8=:.+2 ;Set by SET [NO]BIT8 (^c377 for BIT8) BIC #^C<177>,R5 ;MAKE IT 7-BIT ASCII .Assume . LE LPSTRT+1000,MESSAGE=<;Set code object not in block1> BEQ IGNORE ;IGNORE NULLS .IF NE OFORM ASRB (PC)+ ;DID WE JUST DO A FORM FEED FOR PERF. SKIP? SKPFLG: .WORD 0 BCC 1$ ;NO CMPB #FF,R5 ;YES, IS THIS A FORM FEED ON ITS HEELS? BEQ IGNORE ;YES, IGNORE IT 1$: .ENDC ;NE OFORM CMPB R5,#SPACE ;PRINTABLE CHARACTER? BLO CHRTST ;NO, GO TEST FOR SPECIAL CHARACTERS CMPB R5,#'a!40 ;LOWER CASE? BLO PCHAR ;NOPE... CMPB R5,#'z!40 ;MAYBE, IN RANGE A-Z (LOWER CASE)? BHI PCHAR ;NO... SUB (PC)+,R5 ;YES, CONVERT IF 'LC' OPTION CHOSEN O$LC=:. .WORD 40 ; (40 FOR 'NOLC', 0 FOR 'LC') .Assume . LE LPSTRT+1000,MESSAGE=<;Set code object not in block1> PCHAR: DEC COLCNT ;ANY ROOM LEFT ON LINE? BLT IGNORE ;NO MORE ROOM ON LINE, SO DON'T PRINT IT ASLB (PC)+ ;UPDATE TAB COUNT TABCNT: .WORD 1 BEQ RSTTAB ;NEXT TAB STOP, RESET TAB COUNTER PRINTC: MOVB R5,@(PC)+ ;PRINT THE CHARACTER LPB=:. .WORD LP$CSR+2 ;LINE PRINTER BUFFER REGISTER .Assume . LE LPSTRT+1000,MESSAGE=<;Set code object not in block1> BR LPNEXT ;TRY TO SEND ANOTHER ; SPECIAL CHARACTERS CHRTST: CMPB #HT,R5 ;IS THIS A TAB? O$TAB=:. ;Set by SET [NO]TAB BEQ TABSET ;YES, SEND SPACES (SET NOTAB) ; BEQ HDWTAB ;YES, SEND (SET TAB) .Assume . LE LPSTRT+1000,MESSAGE=<;Set code object not in block1> CMPB #FF,R5 ;IS THIS A FF? BEQ SENDFF ;YES, RESET LINE COUNT AND SEND OR EXPAND CMPB #CR,R5 ;IS THIS A CR? O$CR=:. ;Set by SET [NO]CR NOP ;YES, IGNORE IT (SET NOCR) ; BEQ RSTC ;YES, SEND IT (SET CR) .Assume . LE LPSTRT+1000,MESSAGE=<;Set code object not in block1> CMPB #LF,R5 ;IS THIS A LF? O$CTRL=:. ;Set by SET [NO]CRLF BNE IGNORE ;NO, NON-PRINTING. IGNORE (SET NOCTRL) ; BNE PRINTC ;NO, NON-PRINTING. SEND (SET CRLF) .Assume . LE LPSTRT+1000,MESSAGE=<;Set code object not in block1> .IF NE OFORM CMP LINCTR,(PC)+ ;SHOULD WE SKIP PERFORATIONS? O$SKIP=:. ;Set by SET SKIP=n .WORD 0 ;SIZE OF MARGIN TO SKIP AT PERFORATION .Assume . LE LPSTRT+1000,MESSAGE=<;Set code object not in block1> BLE SKIPFF ;YES, SEND FORM FEED RATHER THAN LINE FEED DOFORM: DEC LINCTR ;COUNT DOWN LINES LEFT ON THIS PAGE BGT RSTC ;NOT AT END OF PAGE YET NEWPAG: MOV (PC)+,(PC)+ ;RESET NUMBER OF LINES LEFT O$LENG=:. ;Set by SET LENGTH=n .WORD LP.PSZ ;NUMBER OF LINES PER PAGE .Assume . LE LPSTRT+1000,MESSAGE=<;Set code object not in block1> LINCTR: .WORD LP.PSZ ;NUMBER OF LINES LEFT ON THIS PAGE .Assume . LE LPSTRT+1000,MESSAGE=<;Set code object not in block1> CLR FFFLAG ;NOT DOING FORM FEEDS ANY MORE .ENDC ;NE OFORM RSTC: MOV (PC)+,(PC)+ ;RESET COLUMN COUNTER O$WIDT=:. ;Set by SET WIDTH=n .WORD LP.CSZ ;LINE PRINTER WIDTH .Assume . LE LPSTRT+1000,MESSAGE=<;Set code object not in block1> COLCNT: .WORD LP.CSZ ;# OF PRINTER COLUMNS LEFT .Assume . LE LPSTRT+1000,MESSAGE=<;Set code object not in block1> RSTTAB: MOV #1,TABCNT ;RESET TAB COUNTER BR PRINTC ;PRINT THE CHAR TABSET: MOV TABCNT,TABFLG ;SET UP TAB COUNT TO EXPAND TAB: MOV #SPACE,R5 ;PRINT SPACE AS TAB EXPANSION BR PCHAR HDWTAB: ASLB TABCNT ;DEVICE HAS HARDWARE TABS, ADJUST TAB COUNT BEQ RSTTAB ;COUNT COLUMNS FOR EACH EXTRA SPACE DEC COLCNT ;ADJUST COLUMN COUNT BR HDWTAB ;CONTINUE UNTIL NEXT TAB POSITION .IF NE OFORM SKIPFF: INC SKPFLG ;INDICATE THAT WE'RE GIVING A FORM FEED BR SENDFF ; AND DO IT .ENDC ;NE OFORM BLKZ: BLK0: INC @R4 ;BLOCK 0. MAKE SURE WE ONLY COME HERE ONCE SENDFF: MOV (PC)+,R5 ;GET A FORM FEED (OR A LINE FEED FILLER) O$FF=:. ;Set by SET [NO]FF .WORD FF ;FORM FEED (OR 100000!LF IF FILLING) .Assume . LE LPSTRT+1000,MESSAGE=<;Set code object not in block1> .IF NE OFORM MOV R5,FFFLAG ;SET FLAG ON IF FILLING BPL NEWPAG ;IF NO FILL, RESET PAGE COUNT BR DOFORM ;IF FILLING, COUNT DOWN THIS LINE FEED .IFF BR RSTC ;SEND THE FORM FEED .ENDC ;NE OFORM .DSABL LSB LPFBLK: .WORD 0,0,0,0 ;FORK BLOCK .DREND LP .END