.title PL - Simple HP Plotter RS232C interface handler .enabl lc .nlist bex,toc .sbttl V4 9-May-84 ; .rem \ Author: Dr R N Caffin, Description: Current HP plotters interface to a computer via a serial interface. Most operating systems permit one to "attach" another terminal port to the job, but RT-11 has only one terminal fully supported. Multi-terminal support is possible, but requires the use of special monitor calls rather than a simple OPEN or .LOOKUP. The HP plotters are quite intelligent, and can both read and write, and should be treated as another terminal port or device. Unfortunately, DEC RT/Fortran does not easily support both input and output on a single channel, owing to the form of the internal buffering. It is possible to overcome this by repeated use of "REWIND n"; this flushes the internal buffer and permits a direction change. This handler is bidirectional, supporting conventional block-oriented output and line (or record) oriented input. That is, on input it returns a record at a time. It does not have a ring buffer (yet), so the buffer must be emptied before a new record can START to be accepted. This is reasonable for a device such as a plotter, which will reply with a single record to any prompt. The XON/XOFF protocol is the currently accepted standard handshake technique. In the event that there is no complete record in the input buffer a null record will be returned. This will consist of ..... \ ; .iif ndf reg, REG = 176500 .iif ndf vec, VEC = 300 .iif ndf mmg$t, MMG$T = 0 .iif ndf tsx$p, TSX$P = 0 .iif ne tsx$p, MMG$T = 1 ; prcsr = reg ;Receiver CSR prbuf = reg+2 ;Receiver Buffer plcsr = reg+4 ;Transmitter CSR plbuf = reg+6 ;Transmitter Buffer ; prvec = vec ;Receiver Vector plvec = vec+4 ;Transmitter Vector ; CR = 15 LF = 12 ctrlS = 23 ctrlQ = 21 RxbfSz = 80. ; .mcall .drdef .drdef pl,377,abtio$!spfun$,0,prcsr,prvec ; ; Do not use the pl$csr forms, as they are crossed! This permits me to ; call the device PL, use PL for output, and have it auto-install. ; .sbttl SET Options ; .drset csr,2,o.csr,OCT .drset vector,4,o.vec,OCT ; o.csr: mov R0,176 ;Install CSR check location mov R0,pr.csr ;Modify pr.csr add R3,R0 mov R0,pr.buf ; and pr.buf add R3,R0 mov R0,pl.csr ; and pl.csr add R3,R0 mov R0,pl.buf ; and pl.buf cmp R0,#170000 ;Check that new CSR is legal return ; o.vec: mov R0,pl$vtb ;Modify pr.vec add R3,R0 mov R0,pl$vtb+6 ; and pl.vec cmp #477,R0 ;Check new vector legal return ; ; The handler proper starts here. ; .drbeg pl ; start: mov plcqe,R4 asl q$wcnt(R4) ;Convert word count to byte count bcs writer ;Was it a write? bne read ;Was it a read? ; movb q$func(R4),R5 ;Get spfun code cmpb R5,#200 ;Kill the interrupts? bne plfin br plabt ; ; Input is called for: see if there is any ; read: tst Rxflag ;Processor Priority 0, System State bne 5$ ;See if we have a CR ; ; No complete record, so fake a null return ; .if eq mmg$t movb #15,@q$buff(R4) ;No record, so return a inc q$buff(R4) clrb @q$buff(R4) ; and a null inc q$buff(R4) .iff movb #15,-(SP) call @$ptbyt movb #0,-(SP) call @$ptbyt .endc sub #2,q$wcnt(R4) br 20$ ; ; There is a line in the buffer: transfer it to caller ; 5$: mov PC,R5 add #RxBuf-.,R5 ;PIC buffer address as pointer, in R5 10$: .if eq mmg$t movb (R5)+,@q$buff(R4) ;Transfer a byte & jog pointer .iff movb (R5),-(SP) ; or do it the hard way with mmgt: call @$ptbyt ; transfer and then tstb (R5)+ ; set condition codes. .endc beq 20$ ;Ignore last byte if null(=EOF) .if eq mmg$t inc q$buff(R4) ;OK, jog pointer if no mmgt .endc dec q$wcnt(R4) ; and downcount the bytes left br 10$ ; until EOF (>CR) ; 20$: .if eq mmg$t clrb @q$buff(R4) ;Zap rest of buffer inc q$buff(R4) .iff movb #0,-(SP) call @$ptbyt .endc dec q$wcnt(R4) bne 20$ ; clr RxFlag ;Init buffer again clr Point br plfin ; ; Set up the Vector tables ; .drvtb pl,prvec,print .drvtb ,plvec,plint ; ; ; Output Section, for writing to plotter ; writer: dec active ;Set active flag tstb @pr.csr ;Has anything come in? (200=Int. flag) bpl 10$ ; movb @pr.buf,R5 ;Yes, so see what it is. bic #177600,R5 ;Kill the parity cmp R5,#ctrlQ ;Is it a ^Q? beq 20$ cmp R5,#ctrlS ;Is it a ^S? bne 30$ ;If not, ignore it: unwanted! dec csflg ;If it is ^S, set the flag br 40$ ; and only enable the receiver ; 10$: tst csflg ;Receiver clear, how about ^S flag? bne 40$ ;If set, don't enable output 20$: clr csflg ;It is a ^Q 30$: bis #100,@pl.csr ;Set it all going (100=Int. Enable) 40$: bis #100,@pr.csr return ; ; ; .drast pl,4,plabt bic #100,@pl.csr ;Lock out further interrupts plout: mov plcqe,R4 ;Start of Queue beq plexit ;Oops-bain't none! tst csflg ;Do we speak to world? bne ploutr ;Not yet, until ^Q ; .if eq mmg$t movb @q$buff(R4),@(PC)+ ;Output next char. from user buffer .iff call @$gtbyt ; either by direct addressing or by mmgt, movb (SP)+,@(PC)+ ; but set the condition code! .endc pl.buf: .word plbuf bne 10$ ;Was it EOF? clr q$wcnt(R4) ;Yes, so short cut the rest of the buffer br plexit ; and get out of here. 10$: .if eq mmg$t inc q$buff(R4) ;No, so jog pointer (if no mmgt) .endc inc q$wcnt(R4) ; and decrement byte count. beq plexit ;If buffer empty, be done with it. tst csflg ;Check for ^S flag bne ploutr ; and skip enable if set bis #100,@pl.csr ;Otherwise reenable interrupt and continue ploutr: return ; plabt: clr @(PC)+ ;Kill the input - rather final pr.csr: .word prcsr plexit: clr active ;(used elsewhere) clr @(PC)+ ;Write is done: turn off output pl.csr: .word plcsr plfin: .drfin pl ;All done ; ; ; Input Section, for handling ^S and ^Q and getting replies into buffer ; .drast pr,4,plabt bic #100,@pr.csr ;Lock out interrupts movb @(PC)+,R5 ;Get character pr.buf: .word prbuf bic #177600,R5 ;Kill parity beq 10$ ;Ignore null bytes ; cmp R5,#ctrlQ ;Is it ^Q? bne 20$ clr csflg ;Yes, so flag it tst active ;Now, were we doing anything? beq 10$ bis #100,@pl.csr ;Only for ^Q, and only when output active 10$: bis #100,@pr.csr ;Reenable receiver interrupts return ; and return (from .inten in .drast) ; 20$: cmp R5,#ctrlS ;Is it ^S ? bne 30$ ;If not, ignore but listen dec csflg ;Yes, so set the ^S flag br 10$ ; 30$: tst RxFlag ;Neither ^S nor ^Q bne 50$ ;Check for existing buffer full! ; mov PC,R4 add #RxBuf-.,R4 ;PIC address of buffer add Point,R4 ;Point to next empty byte in buffer movb R5,(R4)+ ;Put character in buffer inc Point ;Jog pointer ; cmp R5,#CR ;Was character a CR? bne 40$ dec RxFlag ;Yes, so set flag for line end clrb @R4 ; and put terminating null byte in after inc Point ; the CR ; 40$: cmp point,#RxbfSz ;Check for buffer length blt 10$ ;OK? ; ; Hard error exit ; 50$: mov plcqe,R4 bis #hderr$,@-(R4) ;Set hard error status bit in channel br plabt ; ; active: .word 0 RxBuf: .blkb RxbfSz point: .word 0 RxFlag: .word 0 csflg: .word 0 ; .drend pl ; .end