! Old EDWIN driver for Cursor Addressable Terminals and line printers.

from Edwin include Device
from Edwin include Icodes
from Edwin include Iprocs
from Edwin include Specs
from Imp   include Ascii

const integer MAX X = 131, MAX Y = 23

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

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=79
const byte MAX SY=23
own integer MODEL = -1
own integer XL, YB, 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
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 (no longer used!!) + 4 bits of code as above

routine ESC PLUS (integer SYM)
   TTPUT (ESC)
   TTPUT ('[') if MODEL=VT131
   TTPUT (SYM)
end

routine GMON
   ! Enter Graphics mode.
   return if MODE=GON or (MODEL#VISUAL200 and MODEL#VT131)
   if MODEL=VT131 start
       TTPUT (ESC);   TTPUT ('(');  TTPUT ('0')
   else
       ESC PLUS (GON)
   finish
   MODE = GON
end

routine GMOFF
   ! Come out of graphics mode.
   return if MODE=GOFF or (MODEL#VISUAL200 and MODEL#VT131)
   if MODEL=VT131 start
       TTPUT (ESC);   TTPUT ('(');  TTPUT ('B')
   else
       ESC PLUS (GOFF)
   finish
   MODE = GOFF
end

routine DO MOVE
   integer I
   switch MS (0:MAX CAT)
   -> MS (MODEL)
MS(Visual 200):
   ESC PLUS ('Y');   TTPUT (23-SY+32);   TTPUT (SX+32)
   return
MS(VT131):
   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
   if I > 99 start
       TTPUT ((I//100) + '0')
       I = Rem (I, 100)
       TTPUT ((I//10) + '0')
       TTPUT (Rem(I, 10) + '0')
   else if I > 9
       TTPUT ((I//10) + '0')
       TTPUT (Rem(I, 10) + '0')
   else
       TTPUT (I+'0')
   finish
   TTPUT ('H')
end

external routine Yawn alias "EDWIN___Y" (integer COM, X, Y)
   own integer WX, WY

   routine UPDATE
      DO MOVE if model >= interacts
      GMOFF
      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 name SP                                  { screen pointer
         return unless 0<=SX<=MAX SX and 0<=SY<=MAX SY
         SP == SCREEN_ROW(SY)_COL(SX)
         return if (SP#0 and SP&G MODE REQ = 0)   { character already in slot }
         return if P&(SP&15)=P                     { nothing new on screen }
         P = P ! (SP & 15)                           { OR in lines crossing in other directions. }
         SP = GMODEREQ ! P                           { Update the screen map }
         return if CODE200(P)=95
         ! CODE200(P)=95 => nothing to do on Visual 200 or VT131, => optimise.
         DO MOVE
         GMON
         if MODEL=VISUAL200 start
             TTPUT (CODE 200 (P))
         else { %if MODEL=VT131
             TTPUT (CODE 100 (P))
         finish
      end

      routine MOVE (integer X, Y)
         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
      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 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 }

      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=VISUAL200 or MODEL=VT131)
   end

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

SW(0): ! Initialise
       MAX SX = 79
       DEV DATA_DVY = 23
       DEV DATA_MVY = 23
       DEV DATA_ARF = 45
       DEV DATA_NUM CHAR SIZES = 255
       DEV DATA_NUM CHAR ROTS  = 255
       X = 131 if X=220  { VT220 = VT100 for present }
       if X=200 start
           DEVICE DATA_NAME = "a Visual 200 terminal"
           MODEL = VISUAL 200
       else if X=131
           DEVICE DATA_NAME = "a VT100 terminal"
           MODEL = VT131
           TTPUT (esc);  TTPUT ('[');  TTPUT ('?');  TTPUT ('3');  TTPUT ('h')
           DEV DATA_ARF = 30
           MAX SX = 130
       else
           signal 14, 0
       finish
       DEV DATA_DVX = MAX SX
       DEV DATA_MVX = MAX SX
       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)
       SCREEN == NIL
       SCREEN == NEW (SCREEN)
       SCREEN = 0
       TTMODE (1)
       TTPUT (ESC) and TTPUT ('(') and TTPUT ('0') if MODEL=VT131   { enable graphics characters }
       INIT = TRUE
       return

SW(1): !Terminate
       if MODEL = VT131 start
           TTPUT (ESC);   TTPUT ('(');   TTPUT ('B')   { Default char set  }
           TTPUT (esc);  TTPUT ('[');  TTPUT ('?');  TTPUT ('3');  TTPUT ('l')
       finish
       SX = 0;   SY = 0                                { -> bottom of screen }
       UPDATE
       DISPOSE (SCREEN)
       TTMODE (0)
       return

SW(2): ! Update
       UPDATE
       return

SW(3): ! New frame
       X = 0
       ESC PLUS ('H') if MODEL=VT52
       if MODEL=VT131 then ESC PLUS ('2') and TTPUT (ERASE) c
                       else ESC PLUS (ERASE)
       Y = 22
       UPDATE
       SCREEN = 0
       Y = 0
       -> SW(4)

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

SW(4): ! Move Abs
       VIS = False and return unless 0<=X<=MAX SX and 0<=Y<=MAX SY
       SX = X;   SY = Y
       DO MOVE
       VIS = TRUE
       DIF = TRUE
       return

SW(6): ! Character
       return unless VIS=TRUE
       GMOFF
       TTPUT (X)
       X = 0 if X = ' '
       SCREEN_ROW(SY)_COL(SX) = X
       SX = SX + TCS
       VIS = FALSE if SX>XR
       DIF = TRUE
       Inquire Position (x, y)
       Clip (x, y, 0)
       return

SW(7): ! Attribute  Change
       return

SW(8): ! Set lower window bounds
       XL = X;   YB = Y
       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(12): ! Lower Box bounds
        WX = X;  WY = Y
        return

SW(13): ! Upper Box bounds & Draw the box
        SWAP (WX, X) if WX > X
        SWAP (WY, Y) if WY > Y
        return if WX > XR or X < XL or WY > YT or Y < YB
        WX = XL if WX < XL
        WY = YB if WY < YB
        X = XR if X > XR
        Y = YT if Y > YT
        ! Box now clipped into the screen.
        Yawn (4, wx, wy)
        Yawn (5, wx, y)
        Yawn (5, x, y)
        Yawn (5, x, wy)
        Yawn (5, wx, wy)
        return
SW(*):
end

external routine VCURSOR alias "EDWIN___Y_REQ" (integer name CH,X,Y)

   routine ESCAPE
      TTPUT (ESC)
      if MODEL=VT131 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
      CH = TTGET; ! The significant character of the ESC sequence.
      if MODEL=VT131 start
          CH = TTGET until 'A'<=CH<='D'
      finish
      if CH=CUR U start
        Y =Y+1 if Y<23
         ESCAPE;   TTPUT (UP)
      finish else start
         if CH=CUR R start
                X =X+1 if X<MAX SX
                ESCAPE;   TTPUT (RIGHT)
         finish else start
             if CH=CUR L start
                 X =X-1 if X>0
                 ESCAPE;   TTPUT (LEFT)
             finish else start
                 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