    ! 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