! 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.
%const %integer MAX X = 131, MAX Y = 63

%include "Edwin:device.inc"
%include "Edwin:icodes.inc"

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

! 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 VT100      = 5
%const %integer MAX CAT = 5

%const %integer %array MODELS (MIN CAT:MAX CAT) = 'L',   'H',   'V',   200,   550,   120,   'E',    52,  100
%const %byte %array    ASPECT (MIN CAT:MAX CAT) =  50,    50,    45,    45,    45,    45,    45,    45,   45
%const %integer %array MAXSXS (MIN CAT:MAX CAT) = 131,    72,    79,    79,    79,    79,    79,    79,   79
%const %integer %array MAXSYS (MIN CAT:MAX CAT) =  63,    63,    23,    23,    23,    23,    23,    23,   23
%const %integer %array ERASES (INTERACTS:MAX CAT) =                    'v',   'K',   '*',    FS,   'J',  'J'
%const %integer %array CURS U (INTERACTS:MAX CAT) =                    'A',   DEL,    VT,   DEL,   'A',  'A'
%const %integer %array CURS D (INTERACTS:MAX CAT) =                    'B',    NL,    NL,    NL,   'B',  'B'
%const %integer %array CURS R (INTERACTS:MAX CAT) =                    'C',   TAB,    FF,   TAB,   'C',  'C'
%const %integer %array CURS L (INTERACTS:MAX CAT) =                    'D',    BS,    BS,    BS,   'D',  'D'
%const %integer %array UPS    (INTERACTS:MAX CAT) =                    'A',   'A',    VT,    FF,   'A',  'A'
%const %integer %array DOWNS  (INTERACTS:MAX CAT) =                    'B',   'B',    NL,    VT,   'B',  'B'
%const %integer %array RIGHTS (INTERACTS:MAX CAT) =                    'C',   'C',    CR,   DLE,   'C',  'C'
%const %integer %array LEFTS  (INTERACTS:MAX CAT) =                    'D',   'D',    BS,    BS,   'D',  '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 = MIN CAT - 1
%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
%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))
%record %format ROWF (%record (COLF) %array ROW (0:MAXY))
!$IF ERCC compiler $START
!%own %record (ROWF) SCREEN
!$ELSE $IF IMP77 compiler
%own %record (ROWF) %name SCREEN
!$FINISH
! 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 ESC PLUS (%integer SYM)
   TTPUT (ESC)
   TTPUT ('[') %if MODEL=VT100
   TTPUT (SYM)
%end

%routine GMON
   ! Enter Graphics mode.
   %return %if MODE=GON %or (MODEL#VISUAL200 %and MODEL#VT100)
   %if MODEL=VT100 %then TTPUT (SI) %else ESC PLUS (GON)
   MODE = GON
%end

%routine GMOFF
   ! Come out of graphics mode.
   %return %if MODE=GOFF %or (MODEL#VISUAL200 %and MODEL#VT100)
   %if MODEL=VT100 %then TTPUT (SO) %else ESC PLUS (GOFF)
   MODE = GOFF
%end

%routine DO MOVE
   %integer I
   %switch MS (0:MAX CAT)
   %return %unless INTERACTS <= MODEL <= MAX CAT
   -> MS (MODEL)
MS(Visual 200):
MS(VT 52):
!   GMOFF
   ESC PLUS ('Y');   TTPUT (23-SY+32);   TTPUT (SX+32)
   %return
MS(Bantam 550):
   ESC PLUS ('Y');   TTPUT (SX+32)
   ESC PLUS ('X');   TTPUT (23-SY+32)
   %return
MS(Soroc 120):
   ESC PLUS ('=');   TTPUT (32+23-SY);   TTPUT (SX+32)
   %return
MS(HazeltineE): ESC PLUS (DC1)
   %if SX=0 %then TTPUT ('`') %else TTPUT (SX)
   TTPUT ('w' - SY)
   %return
MS(VT100):
   I = (24-SY)//10
   %if I=0 %start { Suppress leading 0 }
       ESC PLUS (REM(24-SY,10)+'0')
   %else
       ESC PLUS (I+'0')
       TTPUT (REM(24-SY,10)+'0')
   %finish
   TTPUT (';')
   I = (SX+1)//10
   TTPUT (I+'0') %if I#0 { Suppress leading 0 }
   TTPUT (REM(SX+1,10)+'0');   TTPUT ('H')
%end

%external %routine VIDEOS %alias "EDWIN___V" (%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_ROW(I)_COL(J)#0
               %repeat
               %for K=0,1,J %cycle
                   SP = SCREEN_ROW(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

      %else
          DO MOVE %if model >= interacts
          GMOFF
          %if MODEL = VISUAL200 %start
              ESC PLUS (BRIGHT)                           { Default intensity }
          %finish %else %if MODEL = VT100 %start
              ESC PLUS (0);   TTPUT ('m')               { Default intensity }
          %finish
      %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.

      %const %integer LX = 5, RX = 4, UY = 1, DY = 2
      %integer A,B,D,E,F,T,I,XMOVE,YMOVE,X,Y
      %own %integer %array XCODE(1:16) = RX,0,0,0,0,0,RX,0,RX,LX,LX,LX,LX,LX,RX,LX 
      %own %integer %array YCODE(1:16) = UY,UY,0,UY,0,DY,DY,DY,DY,DY,0,DY,0,UY,UY,UY
      ! 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 ADD (%integer P)
         %byte STY                                       { STYle << 4 }
         %byte %name SP                                  { screen pointer
         %if MAXX>=0 %start
             SP == SCREEN_ROW(SY)_COL(SX)
             %return %if (SP#0 %and SP&G MODE REQ = 0)   { character already in slot }
             STY = STYLE << 4
             %return %if STY=(SP&16_70) %and P&(SP&15)=P { nothing new on screen }
             P = P ! (SP & 15)                           { OR in lines crossing in other directions. }
             SP = GMODEREQ ! STY ! P                     { Update the screen map }
         %finish
         %return %if MODEL<INTERACTS %or %c
                     (CODE200(P)=95 %and (MODEL=VISUAL200 %or MODEL=VT100))
         ! CODE200(P)=95 => nothing to do on Visual 200 or VT100, => optimise.
         %if model >= interacts %start
             DO MOVE
             %if STYLE#0 %then TTPUT (AS(STYLE)) %else %start
                 %if MODEL=Bantam550 %start
                     ESC PLUS (DEL)
                 %finish %else %if MODEL=VISUAL200 %start
                     GMON;   TTPUT (CODE 200 (P))
                 %finish %else %if MODEL=VT100 %start
                     GMON;   TTPUT (CODE 100 (P))
                 %finish %else TTPUT ('*')
             %finish
         %finish
      %end

      %routine MOVE (%integer X, Y)
         %const %integer SPLOGE = 15     { line from point in all directions }

         %if STYLE#0 %or (MODEL#VISUAL200 %and MODEL#VT100) %start
             ADD (SPLOGE)
             %if X=0 %start
                 SX = SX + 1
             %finish %else %if X=5 %start
                 SX = SX - 1
             %finish
             %if Y=1 %start
                 SY = SY + 1
             %finish %else %if Y=2 %start
                 SY = SY - 1
             %finish
         %else
             %if X=0 %start
                 ADD(XRIGHT);   SX = SX + 1;   ADD(XLEFT)
             %finish %else %if X=5 %start
                 ADD(XLEFT);    SX = SX - 1;   ADD(XRIGHT)
             %finish
             %if Y=1 %start 
                 ADD(TOP);   SY = SY + 1;   ADD(BOT)
             %finish %else %if Y=2 %start
                 ADD(BOT);   SY = SY - 1;   ADD(TOP)
             %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

      %if STYLE = 0 %and (MODEL=VT100 %or MODEL=VISUAL200) %start
          %if SY = TY %start                             { Horisonal Line }
              ADD (XRIGHT)
              SX = SX + 1 %and ADD (XRIGHT+XLEFT) %while SX < TX-1
              SX = TX
              ADD (XLEFT)
              %return
          %finish %else %if SX = TX %start               { Vertical line }
              %if SY > TY %start
                  ADD (BOT)
                  SY = SY - 1 %and ADD (BOT+TOP) %while SY > TY+1
                  SY = TY
                  ADD (TOP)
              %else { SY < TY }
                  ADD (TOP)
                  SY = SY + 1 %and ADD (BOT+TOP) %while SY < TY-1
                  SY = TY
                  ADD (BOT)
              %finish
              %return
          %finish
          { Otherwise the general line code must be used }
      %finish

      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)
         %else
             E = E + T
             F = F - 1
             MOVE (XMOVE, YMOVE)
         %finish
         %exit %if F<=0
      %repeat
      MOVE (TX, TY) %unless (MODEL=VISUAL200 %or MODEL=VT100) %and STYLE=0
   %end

   %switch SW(0:MAX COM)
   %signal 14, 0 %if INIT#TRUE %and COM#0
   -> SW(COM)

SW(0): ! Initialise
       DEV DATA_NAME = "a type ".Itos(X,0)." terminal"
       X = 200 %if X=55
       MODEL = MIN CAT - 1
       %for COM = MIN CAT, 1, MAX CAT %cycle
            MODEL = COM %and %exit %if X=MODELS(COM)
       %repeat
       %signal 14, 0 %if model=MIN CAT - 1
       DEV DATA_ARF = ASPECT (MODEL)
       DEV DATA_DVX = MAX SXS (MODEL)
       DEV DATA_DVY = MAX SYS (MODEL)
       DEV DATA_MVX = MAX SXS (MODEL)
       DEV DATA_MVY = MAX SYS (MODEL)
       MAXSX = MAXSXS (MODEL)
       MAXSY = MAXSYS (MODEL)
       %if model>=interacts %start
           ERASE = ERASES (MODEL)
           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
!$IF IMP77
       SCREEN == NIL
       SCREEN == NEW (SCREEN)
!$FINISH
       SCREEN = 0
       TTMODE (1) %unless model < interacts
       TTPUT (ESC) %and TTPUT ('(') %and TTPUT ('0') %if MODEL=VT100   { enable graphics characters }
       INIT = TRUE
       %return

SW(1): !Terminate
       SX = 0;   SY = 0                                { -> bottom of screen }
       UPDATE
       %if model=vt100 %then %start
         TTPUT (ESC);   TTPUT ('(');   TTPUT ('B')   { Default char set  }
       %finish
!$IF IMP77
       DISPOSE (SCREEN)
!$FINISH
       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
           ESC PLUS ('H') %if MODEL=VT52
           %if MODEL=VT100 %then ESC PLUS ('2') %and TTPUT (ERASE) %c
                           %else ESC PLUS (ERASE)
           %if MODEL=Bantam550 %start
               TTPUT (0) %for Y=0,1,20
           %finish
           FLUSH OUTPUT
       %finish
       SCREEN = 0
       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
           GMOFF %if MODEL=VISUAL200 %or MODEL=VT100
           TTPUT (X)
       %finish
       X = 0 %if X = ' '
       SCREEN_ROW(SY)_COL(SX) = X %if MAX X >= 0
       SX = SX + TCS
       VIS = FALSE %if SX>XR
       DIF = TRUE
       %return

SW(7): ! Attribute  Change
       %if X=att line style %start
           STYLE = Y&7
       %finish %else %if X=att colour %start
           %if MODEL = VISUAL200 %start
               %if 1<Y<=8 %then Y = BRIGHT %else Y = DIM
               ESC PLUS (Y)
           %finish %else %if MODEL=VT100 %start
               %if 1<Y<=8 %then Y = 1 %else Y = 0
               ESC PLUS (Y)  ;  TTPUT('m')
           %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
SW(*):
%end

%external %routine V REQ %alias "EDWIN___V_REQ" (%integer %name CH,X,Y)

   %routine ESCAPE
      TTPUT (ESC)
      %if MODEL=VT100 %start
          TTPUT ('[')
          TTPUT ('1')
      %finish
   %end

   %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 MODEL=VT100 %start
          CH = TTGET %until 'A'<=CH<='D'
      %finish
      %if CH=CUR U %start
        Y =Y+1 %if Y<23
         ESCAPE;   TTPUT (UP)
      %else
         %if CH=CUR R %start
                X =X+1 %if X<79
                ESCAPE;   TTPUT (RIGHT)
         %else
             %if CH=CUR L %start
                 X =X-1 %if X>0
                 ESCAPE;   TTPUT (LEFT)
             %else
                 %if CH=CURD %start
                     Y =Y-1 %if Y>0
                     ESCAPE;   TTPUT (DOWN)
                 %finish %else %continue
             %finish
         %finish
     %finish
     FLUSH OUTPUT
   %repeat
%end

%end %of %file
