tITLE PTC6 - Interface for PTC-6 Plotter Controller SUBTTL Joe Smith, 1983 at CSM SEARCH MACTEN SALL SPINCH:: ^D500 ;Steps per inch, 400 or 500 (decimal) ; Commands sent to the PTC-6 are formatted as follows: ; !----------------------!-------------------!------------------! ; ! HEADER ! PLOT DATA ! PROMPT ! ; !----------------------!-------------------!------------------! ; ! # ! Record/Record ! Prompt/Inquire ! ; !----------------------!-------------------!------------------! ; ! 2 1 2 Bytes ! Max 510 Bytes ! 1 Byte for ASCIZ ! ; !----------------------!-------------------!------------------! ; ! 1 Word ! 102 Words ! 1 Word ! ; !----------------------!-------------------!------------------! ; ;CPS/6 - Command Structure Protocol for PTC-6 ; @-H Vector Magnitude Descriptor, see table at VCCLEN ; I Symbol Command, with Height, Aspect, T/S/O, Rotation, Count, Text ; J Symbol Command, using only Count and Text ; K Circle Command, 3 bytes of Radius follow ; L Arc Command, 3 bytes of Radius, 2 of Beginning Angle, 2 of End Angle ; M Hatch Command, Pattern, 2 bytes Spacing, Width, Height ; N Rectangle Command, 1 to 3 bytes Width, 1 to 3 bytes Height ; P Pen Up Command ; Q Pen Down Command ; R Select Pen, next byte is a digit from 1 to 4 ; X Download Command, 1 byte Register Number, 2 bytes Data ; Y Negative Y Inhibit Command ; Z Set Maximum Speed Command, 1 byte Speed ;O,S,T,U,V,W are not used by controller, this routine uses @-H and P-R only ;By Joe Smith, with assistance from Todd Frauenhoff ; Table of Contents for PTC-6 Plotter Controller ; ; ; Section Page ; ; 1. Definitions . . . . . . . . . . . . . . . . . . . . . 2 ; 2. PTCINI - Initialize plotter controller . . . . . . . . 3 ; 3. PTCOUT - Output rest of buffer . . . . . . . . . . . . 4 ; 4. PTCMOR - Finish a move longer than 40 inches . . . . . 5 ; 5. PTCMOV - Convert moves to character string . . . . . . 5 ; 6. PTCFIN - Finish up and deselect plotter controller . . 8 ; 7. PTCPEN - Change to different pen . . . . . . . . . . . 8 ; 8. Data Section . . . . . . . . . . . . . . . . . . . . . 9 SUBTTL Definitions ;AC Definitions TF=0 ;TRUE/FALSE S1=1 ;Temp S2=2 ;(not preserved) T1=3 ;X coordinate T2=4 ;Y coordinate T3=5 ;Pen up/down code T4=6 ;Unused J=14 ;Base of job parameters P=17 ;Stack Pointer ;Limit Definitions MAXDAT==^D510 ;Absolute max number of data bytes ;Max X is 128K=16K*8; 8 long moves are 6 bytes each, +1 for VMD and +1 up/down ND BUFFUL,MAXDAT-<8*6>-2;Buffer is considerred full after 460 bytes %16K=^D<16*1024> ;Max offset in 15-1 bits %512=^D512 ;Max offset in 10-1 bits %16=^D16 ;Max offset in 5-1 bits ;Miscellaneous Definitions LF=12 ;Linefeed - Command if "#" follows CR=15 ;Carriage Return XON=21 ;DC1 - Prompt code for PTC6 $100=100 ;64 decimal $40=40 ;Offset to printing characters SUBTTL PTCINI - Initialize plotter controller ;Routine to initialize plotter controller ;Call: MOVEI T1,^D<+45> ;Number of step-times to raise/lower pen ; PUSHJ P,PTCINI## ;(T1 negative to use XON/XOFF flow control) ; OUTSTR (T1) ;Output init string to TTY ; PUSHJ WAIT ;Wait for controller to send a CR PTCINI::MOVMM T1,UPINCS(J) ;Time to raise/lower pen SKIPGE T1 ;Positive means use CR SKIPA T1,[INIXON] ;Negative means use XON MOVEI T1,INICR ;Use CR as handshake JRST UNBUF1 ;Reset variables and return T1 nonzero ;"P" raises pens, "R1" selects #1, controller sends "0C" when ready INICR: BYTE (7) CR,LF,";","#",CR,LF,"#",$40+0,$40+3,"P","R","1",XON,0 ;"X501" sets register 5 to 01 to use XON/XOFF handshaking INIXON: BYTE (7) CR,LF,";","#",CR,LF,"#",$40+0,$40+7,"P" BYTE (7) "R","1","X","5","0","1",XON,0 SUBTTL PTCOUT - Output rest of buffer ;Routine to dump internal buffer ;Call: PUSHJ P,PTCOUT## ; OUTSTR (T1) ;Output last buffer to TTY ; PUSHJ WAIT ;Wait for controller to send a CR (time in S1) PTCOUT::MOVEI S1,"P" ;Get pen-up command PUSHJ P,OUTBYT ;Send it and fall into UNBUF ;Return pointer to completed string (called if zero in T3 or buffer full) UNBUF: SKIPN T1,BYTCNT(J) ;Get byte count if any POPJ P, ;Nothing there, return zero in T1 MOVE S1,[BYTE(7)CR,LF,"#",$40,$40] MOVEM S1,HEADER(J) ;Start of block, number of bytes IDIVI T1,$100 ;Separate into 6-bit fields ADDI T1,$40 ;High order DPB T1,[POINT 7,HEADER(J),27] ADDI T2,$40 ;Low order DPB T2,[POINT 7,HEADER(J),34] MOVEI S1,XON ;Prompt code IDPB S1,PNTR(J) MOVEI S1,0 ;End with null byte IDPB S1,PNTR(J) ; for ASCIZ MOVEI T1,HEADER(J) ;Return T1 nonzero as signal to dump buffer UNBUF1: SETZM OLDVMD(J) ;Make no assumptions about previous commands SETZM OLDPEN(J) ; ... MOVE S1,[POINT 7,PLDATA(J)] MOVEM S1,PNTR(J) ;Reset to start SETZB S1,BYTCNT(J) ; ... EXCH S1,INCS+0(J) ;Clear counter EXCH S1,INCS+1(J) ;Move previous count down EXCH S1,INCS+2(J) EXCH S1,INCS+3(J) ADD S1,INCS+3(J) ;Accumulate total incs for last 4 buffers ADD S1,INCS+2(J) ADD S1,INCS+1(J) ;Return count in S1 POPJ P, ; and addr in T1 SUBTTL PTCMOV -- Check pen up/down status before moving ;Call: MOVE T1, ; MOVE T2, ; MOVEI T3,2 (or MOVEI T3,3) ; PUSHJ P,PTCMOV## ;Convert to PTC-6 commands ; JUMPE T1,CPOPJ ;Done unless PTCMOV returned data ; OUTSTR (T1) ;Send output to terminal ; PUSHJ P,WAIT ;Wait for controller to send a CR (time in S1) ;Set pen up/down condition -- 1 = Same, 2 = Lower, 3 = Raise pen PTCMOV::SKIPLE T3 ;Reset if zero or negative, CAILE T3,3 ; or out of range positive, PJRST PTCOUT ; raise pen and dump buffer (ignoring T1 & T2) SKIPN S1,PENCOM-1(T3) ;Get pen up or pen down command MOVE S1,OLDPEN(J) ;Use previous status if T3 contains 1 CAMN S1,OLDPEN(J) ;If new position is the same, JRST MOVE1 ; don't change it MOVEM S1,OLDPEN(J) ;Set new status PUSHJ P,OUTBYT ;Tell the controller MOVE S1,UPINCS(J) ;Time required to raise/lower pen ADDM S1,INCS+0(J) MOVE1: SKIPN T1 ;If no delta movement, JUMPE T2,MOVE9 ; just return ;Convert movements from 400 steps per inch to 500 steps per inch ;T1 has X, T2 has Y, T3 is scratch ;Note that if SPINCH contains ^D400, this routine is a long no-op (trashes T3) KLUDGE::PUSH P,T2 ;Save because of IDIVI IMUL T1,SPINCH ;Multiply by 500 ADD T1,REM.X(J) ;(with rounding) IDIVI T1,^D400 ;Divide by 400 (ratio of 500/400) MOVEM T2,REM.X(J) ;Save remainder for rounding next time POP P,T2 IMUL T2,SPINCH ;Actual Steps Per INCH ADD T2,REM.Y(J) IDIVI T2,^D400 MOVEM T3,REM.Y(J) ;Continue at BIGCHK SUBTTL PTCMOV -- Split giant moves into smaller ones ;Check to see if X and Y increments within limits BIGCHK: DMOVEM T1,OLD.X(J) ;Save original X and Y MOVM S1,T1 ;Get abs(X) MOVM S2,T2 ;Get abs(Y) CAMGE S1,S2 ;Put larger in S1 MOVE S1,S2 MOVE T3,S1 ;Number of increments that will be in buffer ;Any vector more than 40 (32) inches long has to be split into 2 or more moves. IDIVI S1,%16K ;Determine how many times over JUMPE S1,MOVEOK ;Go plot if T1 and T2 now within range ;If S1=2 then vector must be split into 3 pieces ADDI S1,1 ;Round up (assume 3 in S1 in the comments below) IDIV T1,S1 ;Split X (wipes out T2) MOVE T2,OLD.Y(J) ;Restore Y IDIV T2,S1 ;Split Y (wipes out T3) MOVN S1,T1 ;Get negative 1/3 of distance MOVN S2,T2 ADDM S1,OLD.X(J) ;2/3 left to go ADDM S2,OLD.Y(J) ;Update INCS so that SPROUT can be told to expect a long delay MOVM S1,T1 ;Get abs of the new X and Y MOVM S2,T2 CAMGE S1,S2 ;Put larger in T3 MOVE S1,S2 ADDM S1,INCS(J) ;For elapsed time calculations PUSHJ P,XY2STG ;Do part of vector from T1 & T2 DMOVE T1,OLD.X(J) JRST BIGCHK ;Do rest of movement ;T1 and T2 have delta-X and delta-Y, which are less than 16K increments each MOVEOK: ADDM T3,INCS(J) ;Predict elapsed time PUSHJ P,XY2STG ;Convert to character string ;Check if it is time to dump the buffer MOVE9: MOVE S1,BYTCNT(J) ;Get the byte count CAIL S1,BUFFUL ;Almost full? PJRST UNBUF ;Yes, return addr of string in T1 SETZB S1,T1 ;No, return with zero in T1 POPJ P, SUBTTL PTCMOV -- Convert (X,Y) to ASCII string ;Find out number of bytes needed to represent X increments XY2STG: MOVEI S1,"@" ;Assume 1 byte needed to output X CAXL T1,-%16 ;If more than 4 bits CAXLE T1,%16-1 MOVEI S1,"C" ;Use 2 bytes CAXL T1,-%512 ;If more than 9 bits CAXLE T1,%512-1 MOVEI S1,"F" ;Use 3 bytes ;Find out number of bytes needed to represent Y increments CAXL T2,-%16 ;If more than 4 bits CAXLE T2,%16-1 ADDI S1,1 ;Use 2 bytes CAXL T2,-%512 ;If more than 9 bits CAXLE T2,%512-1 ADDI S1,1 ;Use 3 bytes CAME S1,OLDVMD(J) ;Same as previous? PUSHJ P,OUTBYT ;No, send VMD (Vector Magnitude Descriptor) MOVEM S1,OLDVMD(J) ;Change X and Y increments into ASCII characters for plot controller. HLRZ T3,VCCLEN-"@"(S1);Number of characters to make X incs into vector string PUSHJ P,OUTCOR ;Make X coordinate MOVE T1,T2 ;Get Y coordinate MOVE S1,OLDVMD(J) ;Reset S1 HRRZ T3,VCCLEN-"@"(S1);Number of characters to make Y incs into vector string ;Fall into OUTCOR and return ;Routine to translate coordinate change to characters ;Call with bit pattern in T1, number of bytes in T3 OUTCOR: MOVE S2,[POINT 5,T1,30 ;1 byte POINT 5,T1,25 ;2 bytes POINT 5,T1,20]-1(T3);3 bytes OUTC1: ILDB S1,S2 ;Get 5 bits ADDI S1,$40 ;Make printing PUSHJ P,OUTBYT ;Send it SOJG T3,OUTC1 ;Do all POPJ P, ;Routine to output a byte in S1 and count it OUTBYT: IDPB S1,PNTR(J) ;Save the character AOS BYTCNT(J) ;Count it POPJ P, ;Return SUBTTL PTCFIN - Finish up and deselect plotter controller ;Routine to release plotter controller ;Call: PUSHJ P,PTCFIN## ; OUTSTR (T1) ;Output end string to TTY PTCFIN::MOVEI T1,DESLCT ;Deselect plotter controller POPJ P, ;Return with addr in T1 DESLCT: ASCIZ / # (PTC-6 deselected) / ;LF,"#",$40,$40 does it, rest is comment SUBTTL PTCPEN - Change to different pen ;Routine to change pens, call with number from 1 to 3 in S2 PTCPEN::MOVEI S1,"R" ;Change pens PUSHJ P,OUTBYT MOVEI S1,"0"(S2) ;Pen number PJRST OUTBYT ;This assumes that a call to PLTMOV will be made before the 510 byte limit SUBTTL Data Section ;Constants PENCOM: EXP 0,"Q","P" ;1 = No change, 2 = lower pen, 3 = raise pen ;Bytes needed for X and Y ranges VCCLEN: XWD 1,1 ; -16 <= X <= 15 15 => Y => -16 XWD 1,2 ; -16 <= X <= 15 511 => Y => -512 XWD 1,3 ; -16 <= X <= 15 16383 => Y => -16384 XWD 2,1 ; -512 <= X <= 511 15 => Y => -16 XWD 2,2 ; -512 <= X <= 511 511 => Y => -512 XWD 2,3 ; -512 <= X <= 511 16383 => Y => -16384 XWD 3,1 ; -16384 <= X <= 16383 15 => Y => -16 XWD 3,2 ; -16384 <= X <= 16383 511 => Y => -512 XWD 3,3 ; -16384 <= X <= 16383 16383 => Y => -16384 ;Variables IFG , DATSIZ==+1 EXTERN PTC6BF,J$$END ;For .ASSIGN DEFINE LP(NAME,SIZE),< NAME=PTC6BF+<..=J$$.> J$$.==J$$.+SIZE ..==..> J$$.==0 LP PNTR, 1 ;Byte pointer into PLDATA LP BYTCNT, 1 ;Count of characters in array LP OLDPEN, 1 ;Last pen up/down command sent LP OLDVMD, 1 ;Last Vector Magnitude Descriptor LP OLD.X, 2 ;For doing long vectors OLD.Y=OLD.X+1 LP UPINCS, 1 ;Number of increments to raise/lower pen LP INCS, 4 ;Accumulated increments for 4 buffers LP REM.X, 1 ;Kludge for 500 vs 400 increments LP REM.Y, 1 LP HEADER, 1 ;Header block, must be at PLDATA-1 LP PLDATA, DATSIZ ;Buffer where data is stored ..==J$$. .ASSIGN PTC6BF,J$$END,J$$. ;PTC6BF=J$$END, J$$END=J$$END+J$$. PTC6..=:J$$END-1 ;Last data loc used by this routine PURGE .. LITS: END