! EDWIN driver for the PRINTRONIX 300 Matrix printer
! JGH November 1980

%external %integer %spec VIEWING
%external %integer %spec DEVICE
%external %routine %spec TTPUT (%integer I)
%external %string(255) %spec BUFF
%external %routine %spec FLUSH OUTPUT
%external %routine %spec CHARACTER (%integer CH)
%external %routine %spec SET CHAR QUALITY (%integer Q)
%external %integer %fn %spec OUT STREAM
%external %routine %spec OPEN OUTPUT (%integer I, %string (127) ST)

%const %integer PRINTER = 6; ! EDWIN Device number.
%const %integer TRUE = 0,   FALSE = 1
%const %integer PLOT    = 5; ! To indicate that the line is a plot line.
! Nb - for Moray House  664 x 780,  for VAX and EMAS 774 x 780
%const %integer DOTS WIDE = 774 , DOTS HIGH = 779
%const %integer LINE MAX = DOTS WIDE//8 - 1

%record %format LINEF (%byte %integer %array X (0:LINE MAX))
!%record %format PAGEF (%record(LINEF) %array A (0:DOTS HIGH-1)); ! NOT EMAS
!%own %record (PAGEF) PAGE                                      ; ! VAX
!%own %record (PAGEF) %name PAGE                               ; ! MOUSES
!%record %format PARM FM (%short DSNO, DACT, SSNO, SACT, %integer P1, P2, P3, P4, P5, P6)
%own %record (LINEF) %array PAGE (0:DOTS HIGH-1)              ; ! EMAS

%own %byte %integer DRAWN = TRUE
%own %integer SX = 0, SY = 0

! MOUSES-specific claim and release segment operations
!%record(*)%map  ANY SEGMENT(%integer  bytes)
!   %record(parmfm) p
!   p_p2 = bytes;  p_p3 = -1;   ! <0 => any seg will do
!   svc(8,p)
!   %signal 14,300,p_p1 %if p_p1 < 0
!   %result == record(p_p1<<16);   ! seg. no => addr
!%end
!
!%routine RELEASE SEGMENT(%record(*)%name  r)
!   %record(parmfm)  p
!   p_p1 = addr(r)>>16;  svc(9,p)
!%end

%routine DRAW PAGE
   %const %integer LINE BUFF LEN = (DOTS WIDE+5)//6 + 4 + 2
   %const %integer LIM = LINE MAX // 3 * 3
   %string (LINE BUFF LEN) LINE BUFF
   %integer P, J, LB, I
   %byte %integer %array %name L
   ! 3-bit inversion table
   %constbyteintegerarray  inverse(0:7) =
      2_000, 2_100, 2_010, 2_110,  2_001, 2_101, 2_011, 2_111
   !    0      1      2      3       4      5      6      7

   %for I = DOTS HIGH-1, -1, 0  %cycle
        L == PAGE(I)_X;   ! EMAS
!       L == PAGE_A(I)_X; ! NOT EMAS
        LB = ADDR (LINE BUFF)
        %for J = ADDR(L(0)), 3, ADDR(L(LIM)) %cycle
             P = (((BYTE INTEGER(J)<< 8) ! BYTE INTEGER(J+1)) << 8) ! BYTE INTEGER (J+2)
             BYTE INTEGER (LB + 4) = P&63;   P = P >> 6
             BYTE INTEGER (LB + 3) = P&63;   P = P >> 6
             BYTE INTEGER (LB + 2) = P&63;   P = P >> 6
             BYTE INTEGER (LB + 1) = P&63
             LB = LB + 4
        %repeat
        LENGTH(LINE BUFF) = LB - ADDR(LINE BUFF)
        %if LIM#LINE MAX %start
            P = 0
            %for J = LIM+1, 1, LINE MAX %cycle
                 P = (P<<8)!L(J)
            %repeat
            P = P << (8*(3-(LINE MAX - LIM)))
            %for J = 18, -6, 0 %cycle
                 LINE BUFF = LINE BUFF.TO STRING((P>>J)&63)
            %repeat
        %finish
        ! Strip trailing spaces (represented by binary zero)
        J = LENGTH(LINE BUFF)
        J = J-1 %while J>0 %and CHARNO(LINE BUFF, J)=0
        LENGTH(LINE BUFF) = J

        ! Add various bits and characters required by Printronix printer
        %for j = addr(line buff)+1,1,lb %cycle
           p = byte integer(j)
           byte integer(j) = inverse(p&7)<<3 ! inverse(p>>3) ! 64
        %repeat
        BUFF = LINE BUFF.TO STRING(PLOT).TO STRING(NL)
        FLUSH OUTPUT
   %repeat
   DRAWN = TRUE
%end

%routine DRAW LINE (%integer TX,TY)
   ! This is algorithm 162 in the Collected Algorithms from CACM.
   !
   ! XYMOVE computes the code string required to move the pen of a
   ! digital incremental X-Y plotter from an initial point (XZ,YZ) to
   ! a terminal point (XN,YN) by the "best" approximation to the
   ! straight line between the points. The permitted elemental pen
   ! movement is to an adjacent point in a plane Cartesian point latice,
   ! diagonal moves permitted.

   %integer A,B,D,E,F,T,I,XMOVE,YMOVE,X,Y
   %const %byte %integer %array XCODE(1:16) = 4,0,0,0,0,0,4,0,4,5,5,5,5,5,4,5
   %const %byte %integer %array YCODE(1:16) = 1,1,0,1,0,2,2,2,2,2,0,2,0,1,1,1
   ! PY,PX+PY,PX,PX+PY,PX,PX+NY,NY,PX+NY,NY,NY+NX,NX,NX+NY,NX,NX+PY,PY,NX+PY

   %routine MOVE (%integer X, Y)
      ! Move incrementaly over the screen.
      SX = SX + 1 %if X=0
      SX = SX - 1 %if X=5
      SY = SY + 1 %if Y=1
      SY = SY - 1 %if Y=2
   %end

   %routine MARK
      ! make mark in line buffer - represented as a linear bit string
      %byte %integer %name B
!     b == page_a(sy)_x(sx>>3); ! NOT EMAS
      B == PAGE(SY)_X(SX>>3);  ! EMAS
A label to avert a compiler bug:
      b = b ! (16_80 >> (sx&7) )
   %end

   MARK %and %return %if SX=TX %and SY=TY

   A = TX - SX
   B = TY - SY
   D = A + B
   T = B - A

   I = 0
   %if B>=0 %then I=2
   %if D>=0 %then I=I+2
   %if T>=0 %then I=I+2
   %if A>=0 %then I=8-I %else I=I+10

   A =  -A %if A<0
   B =  -B %if B<0
   F = A + B
   D = B - A
   %if D>=0 %then T=A %and D=-D %else T= B
   E = 0

   XMOVE = XCODE (I-1)
   YMOVE = YCODE (I-1)
   X = XCODE (I)
   Y = YCODE (I)
   %cycle
      A = D + E
      B = T + E + A
      MARK
      %if B>=0 %start
          E = A
          F = F - 2
          MOVE (X, Y)
      %finish %else %start
          E = E + T
          F = F - 1
          MOVE (XMOVE,YMOVE)
      %finish
      %exit %if F<=0
   %repeat
   MARK
%end

%external %routine P300 (%integer COM, X, Y)
   %switch SW(0:7)
   %return %if DEVICE#PRINTER %or COM > 7
   -> SW (COM)

SW(0): ! Initialise
       %if VIEWING = 0 %start
!          VIEWING = 1;     ! VAX, MOUSES
!          OPEN OUTPUT (VIEWING, "LP.LIS")
           VIEWING = 80;    ! EMAS
           OPEN OUTPUT (VIEWING, "LP#LIS")
       %finish
!       PAGE == ANY SEGMENT(64*1024) ; ! MOUSES only
       ! Next bit for machines with Banners - 
       X = OUT STREAM
       SELECT OUTPUT (VIEWING)
       NEWPAGE
       SELECT OUTPUT (X)
!        PAGE = 0;                                ! NOT EMAS
       PAGE (X) = 0 %for X = 0, 1, DOTS HIGH-1; ! EMAS ONLY
        SX = 0
        SY = 0
        %return

SW(1): ! Terminate
       draw page %if drawn # TRUE
!       RELEASE SEGMENT (PAGE)  ;  ! MOUSES only
       %return

SW(2): ! Update
       %return

SW(3): ! Newframe
       DRAW PAGE %if drawn # TRUE
!       PAGE = 0;                               ! NOT EMAS
       PAGE (X) = 0 %for X = 0,1,DOTS HIGH-1; ! EMAS ONLY
       SX = 0
       SY = 0
       DRAWN = FALSE
       %return

SW(4): ! Move
       SX = X
       SY = Y
       %return

SW(5): ! Line
       DRAW LINE (X,Y)
       DRAWN = FALSE
       %return

SW(6): ! Character
       ! Send it back to EDWIN to get it done as a software one.
       SET CHAR QUALITY (1)
       CHARACTER (X)
       %return

SW(7): ! Attributes - none supported.
%end

%end %of %file
