! EDWIN driver for Cursor Addressable Terminals and line printers.

! For systems where memory is a precious resource, the following constants
! can be set to -1, and the driver can still drive most CATs.
%include "ecsc03.subsyss_iospecs"

%const %integer MAX X = 131, MAX Y = 63

%external %routine %spec TTMODE (%integer WHAT)
%external %integer %fn %spec TTGET
%external %routine %spec TTPUT (%integer CH)
%external %routine %spec FLUSH OUTPUT
%external %integer %spec DEVICE

%const %integer TRUE = 0, FALSE = 1

! Control characters
%const %integer BS = 8, TAB = 9, VT = 11, FF = 12, CR = 13, DC1 = 17
%const %integer DLE = 16, ESC = 27, FS = 28, RS = 30, DEL = 127
! Control for Visual 200s
%const %integer G ON = 'F', G OFF = 'G'
%const %integer G MODE REQ = X'80'
! The array holds the code symbols for edges with the codes -
%const %integer TOP=1, XLEFT=2, BOT=4, XRIGHT=8
%const %byte %integer %array CODE (0:15) = 'a'-2, 'a'-2, 'a'-2, 'm', 'a'-2, 'a', 'l', 'o',
                                     'a'-2,   'e', '`', 'c', 's', 'n', 'd', 'b'

! Device configuration data -
%const %integer MIN CAT = -3
%const %integer Wide LP = -3
%const %integer Narr LP = -2
%const %integer Video   = -1
%const %integer INTERACTS = 0; ! Terminal >= interacts => terminal has Graphics capabitility.
%const %integer Visual 200 = 0
%const %integer Bantam 550 = 1
%const %integer Soroc  120 = 2
%const %integer HazeltineE = 3
%const %integer VT52       = 4
%const %integer MAX CAT = 4

%const %integer %array MODELS (MIN CAT:MAX CAT) = 'L',    72,   'V',   200,   550,   120,   'E',    52
%const %integer %array MAXSXS (MIN CAT:MAX CAT) = 131,    72,    79,    79,    79,    79,    79,    79
%const %integer %array MAXSYS (MIN CAT:MAX CAT) =  63,    63,    23,    23,    23,    23,    23,    23
%const %integer %array ERASES (INTERACTS:MAX CAT) =                    'v',   'K',   '*',    FS,   'w'
%const %integer %array CURS U (INTERACTS:MAX CAT) =                    'A',   DEL,    VT,   DEL,   'A'
%const %integer %array CURS D (INTERACTS:MAX CAT) =                    'B',    NL,    NL,    NL,   'B'
%const %integer %array CURS R (INTERACTS:MAX CAT) =                    'C',   TAB,    FF,   TAB,   'C'
%const %integer %array CURS L (INTERACTS:MAX CAT) =                    'D',    BS,    BS,    BS,   'D'
%const %integer %array UPS    (INTERACTS:MAX CAT) =                    'A',   'A',    VT,    FF,   'A'
%const %integer %array DOWNS  (INTERACTS:MAX CAT) =                    'B',   'B',    NL,    VT,   'B'
%const %integer %array RIGHTS (INTERACTS:MAX CAT) =                    'C',   'C',    CR,   DLE,   'C'
%const %integer %array LEFTS  (INTERACTS:MAX CAT) =                    'D',   'D',    BS,    BS,   'D'

%own %integer UP, DOWN, LEFT, RIGHT, CUR U, CUR D, CUR R, CUR L
%own %integer ERASE, MAX SX, MAX SY
%own %integer MODEL = M'NO'
%own %integer YT, XR    ; ! Current Max Window bound

! device information
%own %integer MODE = 0;      ! 0 if alphamode, 1 if Graph mode, -1 if unknown
%own %integer SX = 0, SY = 0;! Current device position
%own %byte VIS = TRUE;       ! TRUE if the current position is in the device window
%own %byte DIF = TRUE;       ! TRUE if an UPDATE has just been done
%own %byte INIT = FALSE;     ! Indicates if the driver is initialised
%own %byte STYLE = 0, FONT = 0
%const %integer dim = '4', bright = '3'; ! Intensity for Visual 200
%const %byte %integer %array AS(0:7) = '*', '-', '#', '+', '*' (*)
%const %integer TCS = 1;     ! True char size.
%record %format COLF (%byte %integer %array COL (0:MAXX))
%own %record (COLF) %array SCREEN (0:MAXY)
! The codes are held in SCREEN in the forms -
! 0 + 7 bits of char
! 1 + 3 bits of line style + 4 bits of code as above

%routine G M ON
   ! Enter Graphics mode.
   %return %if MODE=GON %or MODEL#Visual 200
   TTPUT (ESC)
   TTPUT (GON)
   MODE = GON
%end

%routine G M OFF
   ! Come out of graphics mode.
   %return %if MODE=GOFF %or MODEL#Visual 200
   TTPUT (ESC)
   TTPUT (GOFF)
   MODE = GOFF
%end

%routine DO MOVE
   %switch MS (0:MAX CAT)
   %return %unless INTERACTS <= MODEL <= MAX CAT
   -> MS (MODEL)
MS(Visual 200):
MS(VT 52):
   GMOFF
   TTPUT (ESC);   TTPUT('Y');   TTPUT(23-SY+32);   TTPUT(SX+32)
   %return
MS(Bantam 550):
   TTPUT (ESC);   TTPUT('Y');   TTPUT(SX+32)
   TTPUT (ESC);   TTPUT('X');   TTPUT(23-SY+32)
   %return
MS(Soroc 120):
   TTPUT(ESC);   TTPUT ('=');   TTPUT(32+23-SY);   TTPUT(SX+32)
   %return
MS(HazeltineE): TTPUT (ESC);   TTPUT (DC1)
   %if SX=0 %then TTPUT ('`') %else TTPUT (SX)
   TTPUT ('w' - SY)
%end

%external %routine CATS (%integer COM, X, Y)

   %routine UPDATE
      %byte %integer SP
      %integer I, J, K, ST
      ! Draw the screen
      %if model < interacts %and maxx>=0 %and DIF=TRUE %start
   
          %for I=YT,-1,0 %cycle
               %for J=XR,-1, 0 %cycle
                    %exit %if SCREEN(I)_COL(J)#0
               %repeat
               %for K=0,1,J %cycle
                   SP = SCREEN (I)_COL(K)
                   SP = ' ' %if SP=0
                   %if SP&GMODE REQ#0 %start; ! Line
                       ST = SP>>4&7;   ! Line style
                       TTPUT (AS(ST))
                   %finish %else TTPUT (SP)
               %repeat
               TTPUT (13)
               TTPUT (NL) %if I>0
          %repeat
          TTPUT (FF) %if YT<63 %and model=wide LP

      %finish %else %start
          DO MOVE %if model >= interacts
      %finish
      FLUSH OUTPUT
      MODE= 0
      DIF = FALSE
   %end

   %routine SWAP (%integer %name A, B)
      %integer C
      C = A;   A = B;   B = C
   %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. 
   
      ! The Algorithm has been modded to draw lines on Visual 200s.
      ! Using horisontal & vertical lines where possible, otherwise '+'.
      ! or on other terminals by filling character positions.

      %integer A,B,D,E,F,T,I,XMOVE,YMOVE,X,Y
      %own %integer %array XCODE(1:16) = 4,0,0,0,0,0,4,0,4,5,5,5,5,5,4,5 
      %own %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)
         %const %integer SPLOGE = 15 ; ! All directions set for non v200 terms.

         %routine ADD (%integer P)
            %byte %integer %name sp
            %if maxx>=0 %start
                SP == SCREEN (SY)_COL(SX)
                %return %if SP#0 %and SP&G MODE REQ = 0
                ! Return if a character is already in this slot.
                P = P ! SP & 15;    ! OR in lines crossing in other drns.
                SP = GMODE REQ ! (STYLE << 4) ! P
            %finish
            %return %if MODEL<INTERACTS %or (CODE(P)=95 %and MODEL=Visual200)
            ! CODE()=95 => nothing to do on Visual 200, => optimise.
            %if model >= interacts %start
                do move
                %if STYLE#0 %then TTPUT (AS(STYLE)) %else %start
                    %if model=Bantam 550 %then TTPUT (ESC) %and TTPUT(DEL) %else %start
                        %if model=Visual 200 %then GMON %and TTPUT (CODE(P)) %else TTPUT ('*')
                    %finish
                %finish
            %finish
         %end

         %if STYLE#0 %or model#Visual 200 %then %start
             ADD (SPLOGE)
             %if X=0 %then SX = SX + 1 %else %start
                 SX = SX - 1 %if X=5
             %finish
             %if Y=1 %then SY = SY + 1  %else %start
                 SY = SY - 1 %if Y=2
             %finish
         %finish %else %start
             %if X=0 %then ADD(XRIGHT) %and SX=SX+1 %and ADD(XLEFT) %else %start
                 ADD(XLEFT)  %and SX=SX-1 %and ADD(XRIGHT) %if X=5
             %finish
             %if Y=1 %then ADD(TOP) %and SY=SY+1 %and ADD(BOT) %else %start
                 ADD(BOT) %and SY=SY-1 %and ADD(TOP) %if Y=2
             %finish
         %finish
      %end

      SWAP (SX, TX) %and SWAP (SY, TY) %if SX > TX; ! Optimise mode.
      MOVE (SX, SY) %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
         %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
      MOVE (TX, TY) %unless Model=Visual 200 %and STYLE=0
   %end

   %switch SW(0:9)
   %return %unless 7<=DEVICE<=10 %and 0<=COM<=9
   %signal 14, 0 %if INIT#TRUE %and COM#0
   -> SW(COM)

SW(0): ! Initialise
       X = X & 95 %if X<127; ! Upper case characters only
       MODEL = M'NO'
       %for COM = MIN CAT, 1, MAX CAT %cycle
            MODEL = COM %and %exit %if X=MODELS(COM)
       %repeat
       %signal 14, 0 %if model=M'NO'
       ERASE = ERASES (MODEL)
       MAXSX = MAXSXS (MODEL)
       MAXSY = MAXSYS (MODEL)
       %if model>=interacts %start
           CUR U = CURS U (MODEL)
           CUR D = CURS D (MODEL)
           CUR R = CURS R (MODEL)
           CUR L = CURS L (MODEL)
           UP    = UPS    (MODEL)
           DOWN  = DOWNS  (MODEL)
           RIGHT = RIGHTS (MODEL)
           LEFT  = LEFTS  (MODEL)
       %finish
       SCREEN (Y) = 0 %for Y=0,1,MAXY
       TTMODE (1) %unless model < interacts
       INIT = TRUE
       %return

SW(1): !Terminate
       TTPUT (ESC) %and TTPUT (bright) %if MODEL=Visual 200
       SX = 0;   SY = 0
       UPDATE
       TTMODE (0) %unless MODEL<interacts
       %return

SW(2): ! Update
       UPDATE
       %return

SW(3): ! New frame
       %if MODEL>=INTERACTS %start
           TTPUT (RS) %if MODEL=SOROC 120
           TTPUT (ESC)
           TTPUT (ERASE)
           TTPUT (0) %for Y=0,1,20
           FLUSH OUTPUT
       %finish
       SCREEN(Y) = 0 %for Y=0,1,MAXY
       X = 0;   Y = 0
       -> SW(4)

SW(5): ! Line Abs
       DRAW LINE (X, Y)

SW(4): ! Move Abs
       SX = X;   SY = Y
       DO MOVE %unless MODEL<INTERACTS
       VIS = TRUE
       DIF = TRUE
       %return

SW(6): ! Character
       %return %unless VIS=TRUE
       %unless model < interacts %start
           %if MODEL=Visual200 %start
               %if FONT#0 %and '`'<=X<='z' %then GMON %else GMOFF
           %finish
           TTPUT (X)
       %finish
       SCREEN (SY)_COL(SX) = X %if MAX X >= 0
       SX = SX + TCS
       VIS = FALSE %if SX>XR
       DIF = TRUE
       %return

SW(7): ! Attribute  Change
       STYLE = Y&7 %if X = 1
       %if MODEL = Visual 200 %start
           FONT = Y %if X = 5
           %if X = 7 %start; ! Intensity
               TTPUT (ESC)
               %if Y>=4 %then TTPUT (bright) %else TTPUT (dim)
           %finish
       %finish
       %return

SW(8): ! Set lower window bounds
       %return

SW(9): ! Set upper device window bounds
       XR = X
       XR = MAX SX %if XR>MAX SX
       YT = Y
       YT = MAX SY %if YT>MAX SY
       %return
%end

%dynamic %routine VCURSOR (%integer %name CH,X,Y)

   %signal 14,8 %if MODEL<INTERACTS

   TTPUT (7)
   DO MOVE;   FLUSH OUTPUT;  MODE = 0;   ! Update (in line).
   X = SX;   Y = SY;
   %cycle
      CH = TTGET
      %exit %if CH >= ' '; ! Key hit, => return
      %continue %if CH#ESC %and model=Visual200
      CH = TTGET; ! The significant character of the ESC sequence.
      %if CH=CUR U %start
        Y =Y+1 %if Y<23
         TTPUT (ESC);   TTPUT (UP)
      %finish %else %start
         %if CH=CUR R %start
                X =X+1 %if X<79
                TTPUT (ESC);   TTPUT (RIGHT)
         %finish %else %start
             %if CH=CUR L %start
                 X =X-1 %if X>0
                 TTPUT (ESC);   TTPUT (LEFT)
             %finish %else %start
                 %if CH=CURD %start
                     Y =Y-1 %if Y>0
                     TTPUT (ESC);   TTPUT (DOWN)
                 %finish %else %continue
             %finish
         %finish
     %finish
     FLUSH OUTPUT
   %repeat
%end

%end %of %file
