! EDWIN 4.3  June 1982

! This main module of EDWIN should be portable to any other machine
! with no source changes, except that on systems such as RSX where
! a small limit on the number of significant characters in external
! linkage, the folowing ECCE command must be applied to it -
!    F/{/ed/}/
! Note this means that any alterations to this module MUST NOT use
! The IMP {} comment convention.

%include "ecsc03.subsyss_iospecs"

%const %integer TRUE = 0,   FALSE = 1

%include "CONFIG"

%include "UTILITY"

%include "CHARSPEC"

! Constants
%const %integer OFF = -1

! control
%own %integer STORING = OFF;! Current stream for storing
%own %integer VIS = 0;      ! Current Char visibility
%const %integer %array RA (0:3) = 0, 5, 3, 6
%own %integer %array ATTRIBUTES (0:15) = 1,0,12,0,0,0,90,5,36,0,0,0,0,0,0,1

! Screen information
%own %integer XO = 0;      ! Origin (bottom left) of device window
%own %integer YO = 0
%own %integer XS = 1023;   ! Size of device window
%own %integer YS = 1023
%own %integer CX = 0;      ! Current virtual position
%own %integer CY = 0
%own %integer XV = 1023;   ! Size of virtual window
%own %integer YV = 1023
%own %integer XL = 0;      ! Origin of virtual window (Left edge)
%own %integer XR = 1023;   ! (Right edge)
%own %integer YB = 0;      ! (Bottom edge)
%own %integer YT = 1023;   ! (Top edge)
%own %integer OWXL=0, OWXR=1023, OWYB=0, OWYT=1023
! Old Window bounds for aspect ratioing retrospectivly.

%routine SWOP (%integer %name A,B)
   %integer C
   C=A;  A=B;  B=C
%end

%external %routine MAP TO DEVICE COORDS (%integer %name X, Y)
   X = MUL DIV (X-XL, XS, XV) + XO
   Y = MUL DIV (Y-YB, YS, YV) + YO
%end

%external %routine MAP TO VIRTUAL COORDS (%integer %name X, Y)
   X = MUL DIV (X-XO, XV, XS) + XL
   Y = MUL DIV (Y-YO, YV, YS) + YB
%end

%routine VECTOR (%integer FX, FY, TX, TY, V)
   ! Draw visible line from virtual coordinates (FX,FY) to (TX,TY).
   ! But if V = 0 just move to (TX,TY).
   %own %integer OTX = 0, OTY = 0

   MAP TO DEVICE COORDS (FX,FY)
   MAP TO DEVICE COORDS (TX,TY)

   %if V#0 %start
       DRIVE DEV (4, FX, FY) %if OTX#FX %or OTY#FY
       DRIVE DEV (5, TX, TY)
   %finish %else DRIVE DEV (4, TX, TY)
   OTX = TX
   OTY = TY
%end

%routine CLIP (%integer TX, TY, V)
   ! Draw a vector (visible if V#0) to virtual position (TX,TY)
   ! but only that part of it (if any) which lies within the virtual window
   %integer F, T, FX, FY
   %constinteger LEFT = 1, RIGHT = 2, ABOVE = 4, BELOW = 8

   %integer %fn CODE (%integer X, Y)
      ! Set one bit for each of the conditions that (X,Y) lies
      ! above, below, to the left, or to the right of window
      %integer C
      C = 0
      C = LEFT %if X<XL
      C = RIGHT %if X>XR
      C = C + ABOVE %if Y>YT
      C = C + BELOW %if Y<YB
      %result = c
   %end

   %return %if VIEWING<0

   FX=CX; FY=CY;          ! Let FROM be current position
   CX=TX; CY=TY;          ! Update current position to TO
   VECTOR (FX,FY,TX,TY,V) %and %return %if CLIPPING<0

   T=CODE (TX,TY)
   VIS = T; ! Remember whether inside window for CHARS
   %cycle
      F = CODE (FX,FY)
      %return %if F&T#0;  ! Both endpoints outside window (same side)
      %if F+T=0 %start;   ! Both endpoints inside window
          VECTOR (FX,FY,TX,TY,V); ! So draw the line and it's over with
          %return
      %finish
      %if F=0 %start;     ! FROM is inside window: swop FROM and TO
          SWOP(F,T); SWOP(FX,TX); SWOP(FY,TY)
      %finish
      ! Now FROM is outside and TO is either inside or outside
      ! So shift FROM along the line FROM/TO until it comes
      ! to lie on the window's edge.
      %if F&LEFT#0 %start
          FY=MUL DIV(TY-FY,XL-FX,TX-FX)+FY; FX=XL
      %finish %else %start
          %if F&RIGHT#0 %start
              FY=MUL DIV(TY-FY,XR-FX,TX-FX)+FY; FX=XR
          %finish %else %start
              %if F&ABOVE#0 %start
                  FX=MUL DIV(TX-FX,YT-FY,TY-FY)+FX; FY=YT
              %finish %else %start 
                  %if F&BELOW#0 %start
                      FX=MUL DIV(TX-FX,YB-FY,TY-FY)+FX; FY=YB
                  %finish
              %finish
          %finish
      %finish
   %repeat
%end

%routine INSERT (%integer A, X, Y); ! Insert instruction into display file
   %integer CODE, OOS

   %return %if STORING < 0
   OOS = OUT STREAM
   SELECT OUTPUT (STORING)
   CODE = A&15
   %if CODE<=5 %and -128<=X<=127 %and -128<=Y<=127 %start
       WRITE (A ! 16, 1)
       WRITE ((X&255)<<8! Y&255, 1)
   %finish  %else %start
       WRITE (A, 1)
       %if CODE = 15 %then WRITE  (X, 1) %else %start
           WRITE (X, 1) %and WRITE (Y, 1) %unless 9<=CODE<=12
       %finish
   %finish
   NEWLINE
   SELECT OUTPUT (OOS)
%end


%routine SET ATTRIBUTE (%integer WHAT,TO)
   ATTRIBUTES(WHAT) = TO
   TO = MUL DIV (TO, XS, XV) %IF WHAT=2; !  Scale char size to dev units.
   DRIVE DEV (7, WHAT, TO) %if VIEWING>=0
%end

%integer %fn CHAR OFFSET (%integer DIR)
   ! Dir is X or Y and the result is the current character offset in dir.
   %const %integer %array RX (0:3) = 1, 0, -1, 0
   %const %integer %array RY (0:3) = 0, 1, 0, -1
   %integer SIZE, ROT

   ROT = (ATTRIBUTES (3)//90)&3
   SIZE = ATTRIBUTES (2)
   %result = SIZE * RX(ROT) %if DIR = 'X'
   %result = SIZE * RY(ROT)
%end

%routine DO ASPECT
   %integer MD,MV,N

   MD = VIEWPORT GRADIENT (YS,XS)
   MV = MUL DIV (5000,IMOD(YT-YB),IMOD(XR-XL))
   %if MD#MV %start
       %if MD>MV %start
           N = (MUL DIV(MD,IMOD(XR-XL),5000)+YB - YT)//2
           YB = YB - N
           YT = YT + N
       %finish %else %start
           N = (MUL DIV(5000,IMOD(YT-YB),MD)+XL - XR)//2
           XL = XL - N
           XR = XR + N
       %finish
   %finish
%end


%constinteger ONE=32;  !  Internal representation of unity

%routine %spec INTERPRET (%integer PC,SIZE,ROT)

%routine MARKER (%integer N)
   %const %integer %array MK(0:10) = '.', 'O', '#', 'A', 'X', '*', '+', '>', '<', 'V', '^'
   %integer scale
   !  This draws a marker at the current position.
   %return %unless 0<=N<=10

   DRIVE DEV (6, MK(N), 0) %and %return %if XLIM(DEVICE)<250;      !  For VDUs.
   SCALE = ONE
   SCALE = ONE * 10 %if XLIM(DEVICE)>4095; !  For calcomps & HP plotters.
   INTERPRET (CHARPDF(2000-N*2), MUL DIV(SCALE, XV, XS), 0)
%end

%routine INTERPRET(%integer PC,SIZE,ORIENT)
   ! PC=0 => PDF read from the input stream, else read from CHAR PDF.
   ! Interpret instructions in display file starting
   ! at (relative) PC until an END instruction is found
   ! For more information on display file layout, see GRAFIX.PDFDOC.
   ! Codes are   0 LINEA   1 MOVEA   2 MARKERA
   !             3 LINER   4 MOVER   5 MARKERR
   !             6 SUBPIC  7 old END 8 WINDOW
   !             9 CHAR   10 ATTRIBUTES   11 END
   %integer WORD, CODE, X, Y, Z, P, LSAVE, CSIZE, ACTIVE, OX, OY
   %switch C (0:15)

   %routine GET (%integer %name N)
      %if PC=0 %then READ(N) %else N=CHARPDF(PC) %and PC=PC+1
   %end

   ACTIVE = FALSE
   %cycle
      GET (WORD)
      CODE=WORD&15
      %if CODE<=5 %start;  !Draw, Move, Marker
          ACTIVE = TRUE
          GET(X)
          %if WORD&16=0 %start; !Long form
              GET(Y)
          %finish%elsestart;           !Short form
              Y=X&255; X=X>>8&255
              X=X!!(\255) %if X&128#0
              Y=Y!!(\255) %if Y&128#0
          %finish
          %if CODE>=3 %start; !Relative
              %if ORIENT&4#0 %start; !Coordinate swop
                  Z=X; X=Y; Y=Z
              %finish
              X=-X %if ORIENT&1#0;   !Y-axis reflection
              Y=-Y %if ORIENT&2#0;   !X-axis reflection
              !Change relative to absolute coords
              X = MUL DIV (X,SIZE,ONE) + CX
              Y = MUL DIV (Y,SIZE,ONE) + CY
              CODE=CODE-3;           !Map to absolute codes
          %finish
    %finish
    ->C(CODE)
C(2):CLIP(X,Y,0);MARKER(WORD>>12&15); %continue ; !Point
C(0):CLIP(X,Y,1); %continue;       !+Draw
C(1):CLIP(X,Y,0); %continue;       !Move
C(6): %signal 14, 5
C(8): ! SET new WINDOW
      GET(XL);  GET(XR);  GET(CODE);  GET (YB);  GET (YT)
      DO ASPECT %if ATTRIBUTES(15)#0
      XV=XR-XL
      YV=YT-YB
      %continue
C(9):%if ATTRIBUTES(4) & 1 = 0  %start
         DRIVE DEV (6, WORD>>4, VIS)
         CX = CX + ATTRIBUTES (2)
     %finish %else %start
         ! Software character if char not PUT.
         P = 2000 - ((WORD>>4 & 255) - 21) << 1
         CSIZE = MULDIV (ATTRIBUTES (2), ONE, 12)
         %continue %if CSIZE < 8; ! Not worth drawing
         %if ATTRIBUTES(1)#0 %start
             LSAVE = ATTRIBUTES(1)
             DRIVE DEV(7,1,0)
         %finish %else LSAVE = -1
         OX = CX;   OY = CY
         INTERPRET(CHARPDF(P),CSIZE,RA((ATTRIBUTES(3)//90)&3))
         CLIP (OX + CHAR OFFSET ('X'), OY + CHAR OFFSET ('Y'), 0)
         DRIVE DEV(7,1,LSAVE) %if LSAVE>=0
     %finish
     ACTIVE = TRUE
     %continue
C(15):
C(10): %if CODE=10 %then CODE=WORD>>4&255 %else GET(CODE)
       SET ATTRIBUTE (WORD>>12&15, CODE)
       %continue
C(11): %return %if ACTIVE=TRUE
  %repeat
C(13): C(14): %signal 14,5
C(7):C(12):
%end



!*******************************************************************
!*                                                                 *
!*               U S E R   R O U T I N E S                         *
!*                                                                 *
!*******************************************************************

%routine %spec WINDOW (%integer A,B,C,D)
%routine %spec VIEW PORT (%integer A,B,C,D)

%external %routine INITIALISE FOR (%integer TYPE)
   DRIVE DEV (0, TYPE, 0); ! Initialise device driver
   VIEW PORT (0, DVX(DEVICE), 0, DVY(DEVICE)) %if 0<=DEVICE<=NUM DEV
   WINDOW (0, 1023, 0, 1023)
%end

%external %routine TERMINATE EDWIN {%alias "ED$TER"}
   DRIVE DEV (1, 0, 0); ! Tell the device driver to Terminate
   INSERT (12, 0, 0); ! Close the PDF if it was in use.
%end

%external %routine LINE ABS (%integer X,Y); !Draw line to absolute pos.
   INSERT(0,X,Y); CLIP(X,Y,1)
%end

%external %routine MOVE ABS (%integer X,Y); !Move to absolute position
   INSERT(1,X,Y); CLIP(X,Y,0)
%end

%external %routine MARKER ABS {%alias "ED$MKA"} (%integer N,X,Y)
   INSERT(N<<12!2,X,Y); CLIP(X,Y,0); MARKER (N)
%end

%external %routine LINE REL (%integer X,Y); !Draw a visible rel vector
   INSERT(3,X,Y); CLIP(X+CX,Y+CY,1)
%end

%external %routine MOVE REL (%integer X,Y); !Draw an invisible rel vector
   INSERT(4,X,Y); CLIP(X+CX,Y+CY,0)
%end

%external %routine MARKER REL {%alias "ED$MKR"} (%integer N,X,Y)
   INSERT(N<<12!5,X,Y); CLIP(X+CX,Y+CY,0); MARKER(N)
%end

%external %routine CHARACTER (%integer CH)
   %const %integer UNIT = 12
   %integer LSAVE, SIZE, OX, OY

   INSERT (CH<<4!9,0,0)

   %return %if VIEWING < 0

   %if ATTRIBUTES(4)&1 = 0 %start
       DRIVE DEV (6,CH,0) %if VIS=0
       CX = CX + ATTRIBUTES (2)
   %finish %else %start
       %return %unless 32<=CH<=127
       SIZE = MUL DIV (ATTRIBUTES(2), ONE, UNIT)
       %return %if SIZE < 8
       %if ATTRIBUTES(1)#0 %start
           LSAVE = ATTRIBUTES(1)
           DRIVE DEV(7,1,0)
       %finish %else LSAVE = -1
       OX = CX;   OY = CY
       INTERPRET(CHARPDF(2000-(CH-21)<<1), SIZE, RA(ATTRIBUTES(3)//90&3))
       CLIP (OX + CHAR OFFSET ('X'), OY + CHAR OFFSET ('Y'), 0)
       DRIVE DEV(7,1,LSAVE) %if LSAVE>=0
   %finish
%end

%external %routine TEXT (%string(255) ST)
   %integer I
   CHARACTER (CHAR NO (ST,I)) %for I = 1,1,LENGTH(ST)
%end

%external %routine NEW FRAME
   DRIVE DEV (3,0,0)
   INSERT (11,0,0)
   CX=0; CY=0
%end

%external %routine UPDATE
   DRIVE DEV (2,0,0); ! Get driver to update the picture.
%end

%external %routine CLIP ON
   CLIPPING = 0
%end

%external %routine CLIP OFF
   CLIPPING = OFF
%end

%external %routine STORE ON {%alias "ED$SON"} (%integer STREAM)
   STORING = STREAM
%end

%external %routine STORE OFF {%alias "ED$SOF"}
   STORING = OFF
%end

%external %routine VIEW ON (%integer STREAM)
   VIEWING = STREAM
%end

%external %routine VIEW OFF
   VIEWING = OFF
%end

%external %routine WINDOW (%integer A,B,C,D)
   XL = A;   OWXL = A;   XR = B;   OWXR = B
   YB = C;   OWYB = C;   YT = D;   OWYT = D
   INSERT(8, A, B);   INSERT(8, C, D)
   DO ASPECT %if ATTRIBUTES(15)#0
   XV = XR-XL;   YV = YT-YB
%end

%external %routine VIEW PORT (%integer A,B,C,D)
   %integer S

   ! We don't allow users to choose silly values as it confuses them!
   %return %if DEVICE<0; ! Anything goes for NULL device
   SWOP (A,B) %if A>B
   SWOP (C,D) %if C>D
   A = 0 %if A<0
   C = 0 %if C<0
   B = XLIM(DEVICE) %if B>XLIM(DEVICE)
   D = YLIM(DEVICE) %if D>YLIM(DEVICE)
   DRIVE DEV (8, A, C); ! Set lower window bounds
   DRIVE DEV (9, B, D); ! Set upper window bounds (If  driver req.)
   XO=A;  XS=B-A;  YO=C;  YS=D-C

   S = STORING
   STORING = OFF
   WINDOW (OWXL, OWXR, OWYB, OWYT)
   STORING = S
%end

%routine STORE ATTRIBUTE (%integer WHAT, IT)
   INSERT ((WHAT<<8 ! IT) <<4 ! 10, 0, 0)
%end

%external %routine SET COLOUR (%integer TO)
   %const %integer THIS = 0, DEF = 1, MAX = 255
   TO = DEF %unless 0<=TO<=MAX
   STORE ATTRIBUTE (THIS, TO)
   %return %if ATTRIBUTES (THIS) = TO
   SET ATTRIBUTE (THIS, TO)
%end

%external %routine SET LINE STYLE (%integer TO)
   %const %integer THIS = 1, DEF = 0, MAX = 4
   TO = DEF %unless 0<=TO<=MAX
   STORE ATTRIBUTE (THIS, TO)
   %return %if ATTRIBUTES (THIS) = TO
   SET ATTRIBUTE (THIS, TO)
%end

%external %routine SET CHAR SIZE {%alias "ED$SCS"} (%integer TO)
   %const %integer THIS = 2
   INSERT (THIS<<12 ! 15, TO, 0)
   %return %if ATTRIBUTES (THIS) = TO
   SET ATTRIBUTE (THIS, TO)
%end

%external %routine SET CHAR ROT {%alias "ED$SCR"} (%integer TO)
   %const %integer THIS = 3
   ! Force the parameter into the range 0 to 360.
   %if TO < 0 %start
       TO = TO + 360 %until TO >= 0
   %finish %else %if TO>=360 %start
       TO = TO - 360 %until TO < 360
   %finish
   INSERT (THIS<<12+15, TO, 0)
   %return %if ATTRIBUTES (THIS) = TO
   SET ATTRIBUTE (THIS, TO)
%end

%external %routine SET CHAR QUALITY {%alias "ED$SCQ"} (%integer TO)
   %const %integer THIS = 4, DEF = 0, MAX = 2
   TO = DEF %unless 0<=TO<=MAX
   STORE ATTRIBUTE (THIS, TO)
   %return %if ATTRIBUTES (THIS) = TO
   SET ATTRIBUTE (THIS, TO)
%end

%external %routine SET CHAR FONT {%alias "ED$SCF"} (%integer TO)
   %const %integer THIS = 5, DEF = 0, MAX = 7
   TO = DEF %unless 0<=TO<=MAX
   STORE ATTRIBUTE (THIS, TO)
   %return %if ATTRIBUTES (THIS) = TO
   SET ATTRIBUTE (THIS, TO)
%end

%external %routine SET CHAR SLANT {%alias "ED$SCI"} (%integer TO)
   %const %integer THIS = 6, DEF = 90, MAX = 180
   TO = DEF %unless 0<=TO<=MAX
   STORE ATTRIBUTE (THIS, TO)
   %return %if ATTRIBUTES (THIS) = TO
   SET ATTRIBUTE (THIS, TO)
%end

%external %routine SET INTENSITY (%integer TO)
   %const %integer THIS = 7, DEF = 5, MAX = 7
   TO = DEF %unless 0<=TO<=MAX
   STORE ATTRIBUTE (THIS, TO)
   %return %if ATTRIBUTES (THIS) = TO
   SET ATTRIBUTE (THIS, TO)
%end

%external %routine SET SPEED (%integer TO)
   %const %integer THIS = 8, DEF = 36, MAX = 255
   TO = DEF %unless 0<=TO<=MAX
   STORE ATTRIBUTE (THIS, TO)
   %return %if ATTRIBUTES (THIS) = TO
   SET ATTRIBUTE (THIS, TO)
%end

%external %routine ASPECT RATIOING (%integer MODE)
   %const %integer THIS = 15, DEF = 1
   %integer S
   MODE = DEF %unless MODE = 0
   STORE ATTRIBUTE (THIS, MODE)
   SET ATTRIBUTE (THIS, MODE)
   S = STORING
   STORING = OFF
   WINDOW (OWXL, OWXR, OWYB, OWYT)
   STORING = S
%end

%external %routine INQUIRE POSITION {%alias "ED$IP"} (%integer %name X, Y)
   X = CX;  Y = CY
%end

%external %routine INQUIRE WINDOW {%alias "ED$IW"} (%integer %name A, B, C, D)
   A = XL;  B = XR;  C = YB;  D = YT
%end

%external %routine INQUIRE VIEW PORT {%alias "ED$IV"} (%integer %name A, B, C, D)
   A = XO;  B = XS+XO;  C = YO;  D = YS+YO
%end

%external %routine  REVIEW 
   ! This reads a PDF form the current input stream and draws it.
   INTERPRET (0,ONE,0)
%end

%end %of %file
