!!This version was typed in by RWT (Feb 2002)
!!from a paper copy believed to date from 1975.

BEGIN;                                      !EDIT15: ECCE FOR PDP9/15
OWNINTEGER IN=1;                            !CURRENT INPUT STREAM
OWNINTEGER MIN=1;                           !MAIN INPUT STREAM
OWNINTEGER MOUT=1;                          !MAIN OUTPUT STREAM
OWNINTEGER SIN=2;                           !SECONDARY INPUT STREAM
OWNINTEGER MON=0;                           !MONITOR INDIC
OWNINTEGER PRINT1=0,PRINT2=0;               !PRINT INDICATORS
OWNINTEGER STOP=-5000;                      !LOOP STOP (CONST)
INTEGER I,J,K,PP1,SYM
INTEGER CODE;                               !COMMAND CODE LETTER
INTEGER TEXT;                               !TEXT POINTER (AD)
INTEGER NUM;                                !REPETITION NUMBER
OWNINTEGER SEXTRA=0;                        !EXTRA BUFF FOR SIN
INTEGERNAME MAINFP;                         ! == FP OR MFP (FOR SIN)

OWNINTEGERARRAY C(0:119);                   !COMMAND -> <- TEXT
! EACH COMMAND UNIT -- LETTER, PARENTHESIS OR COMMA -- IS
! REPRESENTED BY A TRIO:  CODE(+LIM)  TEXT  NUM
! IN THE CASE OF PARENTHESES AND COMMAS 'TEXT' IS A POINTER
! TO ANOTHER COMMAND UNIT (NOT TO A TEXT STRING)
INTEGER CBASE;  CBASE = ADDR(C(0))
INTEGER TBASE;  TBASE = CBASE+119
INTEGER CI;                                 !COMMAND INDEX (AD)
INTEGER TI;                                 !TEXT INDEX (AD)
OWNINTEGER CMAX=0;                          !COMMAND MAX (AD)
INTEGERARRAY STORED(1:192);                 !DEFS OF X,Y,Z
OWNINTEGER POS1=0, POS2=0, POS3=0

    I = FREESTORE-10;                       !WORK OUT FREE SPACE
    CYCLE J = 2,-1,0
       SELECT INPUT(J); SELECT OUTPUT(J)
       SEXTRA = 122 IF J = SIN AND INDEV # 0
       I = I-256 IF INDEV&(¬1) # 0
       I = I-256 IF OUTDEV&(¬1) # 0
    REPEAT

INTEGERARRAY A(0:I)
INTEGER TOP;  TOP = ADDR(A(0))<<1+1;        !TOP OF BUFF (BAD)
INTEGER BOT;  BOT = TOP+I+I-SEXTRA;         !BOTTOM OF BUFF (BAD)
INTEGER LBEG;                               !LINE START (BAD)
INTEGER PP;                                 !PREVIOUS POINTER (BAD)
OWNINTEGER FP=0;                            !FILE POINTER (BAD)
INTEGER LEND;                               !LINE END (BAD)
OWNINTEGER FEND;                            !END OF FILE IN BUFF (BAD)
OWNINTEGER MS=0;                            !MATCH START (BAD)
OWNINTEGER ML=0;                            !MATCH LIMIT (BAD)
! SIGNIFICANCE OF FILE POINTERS:
! [NL] O N E NL T W . . . O NL N E X T NL . . NL L A S T NL [NL]
!      !        !   !     !  !                                !
!      T        L   P     F  L                                F
!      O        B   P     P  E                                E
!      P        E            N                                N
!               G            D                                D

INTEGER TYPE,CHAIN;                         !COMMAND INPUT VARS
OWNINTEGER PEND=0, PR=0;                    !DITTO
SWITCH T(0:12)
SWITCH S('A':92)

ROUTINE LOAD PP(INTEGER K);                 !!!ALSO INCREMENTS PP
!BYTE(PP) = K; !PP = PP+1
INTEGER P
    LAC PP;  OPR 2064;  !RCR;  DAC P
    OPR 768; !SZL; JMP L1
    LAC K;  OPR 1032;  !RTL;  OPR 1032
    OPR 1032;  OPR 1032;  DAC K
    LAC #-256;  OPR 513;  !SKP!CMA
L1: LAC #-256;  AND* P;  XOR K;  DAC* P
    ISZ PP
END

ROUTINE LOAD FP(INTEGER K)
!BYTE(FP) = K
INTEGER P
    LAC FP;  OPR 2064;  !RCR;  DAC P
    OPR 768; !SZL;  JMP L1
    LAC K;  OPR 1032;  !RTL;  OPR 1032
    OPR 1032;  OPR 1032;  DAC K
    LAC #-256;  OPR 513;  !SKP!CMA
L1: LAC #-256;  AND* P;  XOR K;  DAC* P
END

INTEGERFN BYTE(INTEGER P)
    OPR 2064; !RCR;  DAC P
    LAC* P;  OPR 768;  !SZL;  JMP L1
    OPR 1040; !RTR;  OPR 1040
    OPR 1040;  OPR 1040
L1: AND #255;  JMP* BYTE
    STOP
END

ROUTINE LEFT STAR
1:  RETURN IF PP = LBEG
    FP = FP-1;  PP = PP-1
    LOAD FP(BYTE(PP))
    ->1
END

ROUTINE RIGHT STAR
1:  RETURN IF FP = LEND
    LOAD PP(BYTE(FP))
    ISZ FP
    ->1
END

OWNINTEGERARRAY SYMTYPE(33:95) = C
     64, 3, 3, 3, 2, 3, 3,11, 9,64, 3,12, 2, 3, 3,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 3, 3, 3,64,
   3, 2,10,18, 5, 8,52,10, 2, 6,10,10,10,56, 2, 2,
  10,50,10,22, 5, 5, 6, 2,32,32,32, 3,10, 3, 3, 3
!     !     #  $  %  &  '  (  )  *  +  ,  -  .  /
!  0  1  2  3  4  5  6  7  8  9  :     <  =  >  ?
!  @  A  B  C  D  E  F  G  H  I  J  K  L  M  N  O
!  P  Q  R  S  T  U  V  W  X  Y  Z  [  ¬  ]     _

ROUTINE READ SYM
    IF PEND # 0 THEN SYM=PEND AND PEND=0 ELSE START
       WHILE POS3 # 0 CYCLE
          SYM = STORED(POS3);  POS3 = POS3+1
          RETURN UNLESS SYM = NL
          POS3 = POS2; POS2 = POS1; POS1 = 0
       REPEAT
       READ CH(SYM)
    FINISH
END

ROUTINE READ ITEM
    TYPE = 1
1:  READ SYM
    ->1 IF SYM = ' '
    RETURN IF SYM < 32;                     !NL
    SYM = SYM-32 IF SYM >= 96;              !ENSURE UPPER CASE
    TYPE = SYMTYPE(SYM)
    RETURN UNLESS TYPE&15 = 0
    IF TYPE = 32 START
       POS1 = POS2;  POS2 = POS3
       POS3 = (SYM-'X')<<6+1
       ->1
    FINISH
    IF TYPE = 0 START
       NUM = SYM-'0'
       CYCLE
          READ CH(PEND)
          EXIT UNLESS '0' <= PEND <= '9'
          NUM = (NUM<<2+NUM)<<1-'0'+PEND
       REPEAT
    FINISH ELSE START
       TYPE = 0
       NUM = 0;   RETURN IF SYM = '*'
       NUM = STOP+1;  RETURN IF SYM = '?'
       NUM = STOP;                          ! '!'
    FINISH
END

ROUTINE UNCHAIN
1:  TEXT = CHAIN;  RETURN IF TEXT = 0
    CHAIN = INTEGER(TEXT+1);  INTEGER(TEXT+1) = CI
    ->1 IF INTEGER(TEXT) # 'X'
END

ROUTINE STACK(INTEGER V)
    INTEGER(CI) = V;  CI = CI+1
END

ROUTINE MAKE SPACE
INTEGER K,P1,P2
    RETURN IF MAINFP-PP-240 > 0
    SELECT OUTPUT(MOUT)
    P1 = TOP;  P2 = (P1+LBEG)>>1;           !OUPTUT ABOUT HALF
    P2 = LBEG IF CODE = 'C';                !BUT ALL IF CLOSING
    MONITOR 20 IF P2 = TOP;                 !!!LOGICAL ERROR
1:  K = BYTE(P1);  PRINT CH(K);  ISZ P1
    ->1 UNLESS K = NL AND P1-P2 >= 0
    SELECT OUTPUT(0)
    LBEG = TOP+LBEG-P1;  P2 = PP;  PP = TOP
2:  RETURN IF P1 = P2
    LOAD PP(BYTE(P1));  ISZ P1
    ->2
END

ROUTINE READ LINE
INTEGER K
    IF FP # FEND START
       LEND = FP
       LEND = LEND+1 WHILE BYTE(LEND) # NL
       RETURN
    FINISH
    SELECT INPUT(IN);  FAULT 9 ->EOF
    FP = BOT-121
1:  IF FP # BOT THEN READ CH(K) ELSE K = NL
    LOAD FP(K);  ISZ FP
    ->1 UNLESS K = NL
    FEND = FP; LEND = FEND-1
    FP = BOT-121
    ->2
EOF:FP = BOT;  LEND = FP;  FEND = LEND
    LOAD FP(NL)
2:  SELECT INPUT(0)
    MS = 0;  PRINT1 = 0;  PRINT2 = 0
END

ROUTINE SWITCH INPUTS
OWNINTEGER MFP,MLEND,MEND,SFP,SEND
    IF IN = MIN START
       LEFT STAR
       IN = SIN
       MFP = FP;  MLEND = LEND;  MEND = FEND
       MAINFP == MFP
       BOT = BOT+SEXTRA;  FP = SFP; FEND = SEND
       READ LINE
    FINISH ELSE START
       PP = LBEG
       IN = MIN
       BOT = BOT-SEXTRA;  SFP = FP;  SEND = FEND
       FP = MFP;  LEND = MLEND;  FEND = MEND
       MAINFP == FP
    FINISH
END

ROUTINE PRINT LINE
INTEGER P
    PRINT1 = LEND;  PRINT2 = FP+PP
    P = LBEG
    CYCLE
       IF P = PP START
          PRINT SYMBOL(94) IF P # LBEG AND NUM = 0
          P = FP
       FINISH
       EXIT IF P = LEND
       PRINT SYMBOL(BYTE(P))
       P = P+1
    REPEAT
    PRINTTEXT '**END**' IF P = FEND
    NEWLINE
END

INTEGERFN MATCHED
INTEGER I,J,K,L,T1,FP1,LIM
    LIM = INTEGER(CI-3)&(¬127);  T1 = INTEGER(TEXT)
1:  PP1 = PP;  FP1 = FP
    ->3 UNLESS FP = MS AND (CODE='F' OR CODE='U')
    K = BYTE(FP)
2:  LOAD PP(K); ISZ FP
3:  ->10 IF FP = LEND
    K = BYTE(FP)
    ->2 UNLESS K = T1
    I = FP;  J = TEXT
6:  I = I+1;  J = J-1
    L = INTEGER(J)
    ->6 IF BYTE(I) = L
    ->2 IF L # 0
    MS = FP;  ML = I
    RESULT = 1
10: LIM = LIM-128
    IF LIM # 0 AND FP # FEND START
       IF CODE # 'U' START
          LOAD PP(NL);  LBEG = PP
       FINISH ELSE PP = PP1
       FP = FP+1;  MAKE SPACE;  READ LINE
       ->1
    FINISH
    PP = PP1;  FP = FP1
    RESULT = 0
END

!INITIALISE

    PP = TOP-1;  LOAD PP(NL);               !FOR BOUNCING OFF
    LBEG = PP;  MAINFP == FP
    STORED(1) = NL;  STORED(65) = NL;  STORED(129) = NL
    PROMPT('E'); PROMPT('D'); PROMPT('I'); PROMPT('T')
    PROMPT(NL); PROMPT('>'); PROMPT(0)
    READ LINE

!READ COMMAND LINE

1:  FAULT 9 ->EOF
    PROMPT(PR) AND PROMPT(0) IF PR # 0
    PR = '>'
    READ ITEM; ->1 IF TYPE = 1
    CI = CBASE;  TI = TBASE;  CHAIN = 0
    IF TYPE = 0 AND CMAX # 0 START
       INTEGER(CMAX+2) = NUM
       READ ITEM;  ->ER2 IF TYPE # 1
       ->GO
    FINISH
    IF SYM = '%' START
       READ SYM; SYM = SYM-32 IF SYM >= 96
       CODE = SYM;  ->ER5 IF CODE<=32
       READ ITEM
       ->T(SYMTYPE(CODE)>>4)
T(2):                                       !%X, %Y, %Z
    ->ER1 IF SYM # '='
    I = (CODE-'X')<<6
    CYCLE
       READ SYM
       I = I+1;  STORED(I) = SYM
       ->1 IF SYM = NL
    REPEAT
T(3):                                       !%M, %F, %Q
    MON = 'M'-CODE
    ->1
    FINISH
2:  I = TYPE&15;  ->ER2 IF I < 4
    CODE = SYM;  TEXT = 0;  NUM = 1;        !DEFAULT VALUES
    READ ITEM
    ->T(I)
T(4):                                       !FIND
    NUM = 0 UNLESS TYPE = 0
T(5):                                       !+DEL,TRAV,UNCOVER
    CODE = NUM<<7+CODE;  NUM = 1
    READ ITEM IF TYPE = 0
T(6):                                       !+INSERT,SUBST,VERIFY
    ->ER4 IF TYPE # 3
    TEXT = TI;  I = SYM
61: READ SYM
    IF SYM # NL START
       IF SYM # I START
          ->ER6 IF TI <= CI
          INTEGER(TI) = SYM;  TI = TI-1
          ->61
       FINISH
    FINISH ELSE START
       PEND = SYM
       ->ER4 UNLESS CODE = 'S' OR CODE = 'I'
    FINISH
    ->ER4 IF TI = TEXT AND CODE # 'S'
    INTEGER(TI) = 0;  TI = TI-1
    ->81
T(8):                                       !MOVE,ERASE
    ->100 UNLESS SYM = '-'
    CODE = CODE+10
81: READ ITEM
    ->101
T(9):                                       !CLOSE BRACKET
    UNCHAIN;  ->ER3 IF TEXT = 0
    CODE = 'Z'; INTEGER(TEXT+2) = NUM
    TEXT = TEXT+3
T(10):                                      !+GET,KILL,ETC.
100:->ER1 IF TYPE = 3
101:READ ITEM IF TYPE = 0
    ->PUT
T(11):                                      !OPEN BRACKET
    CODE = 'X'
    ->121
T(12):                                      !COMMA
    CODE = 'Y'
    READ ITEM IF TYPE = 1
121:TEXT = CHAIN;  CHAIN = CI
    NUM = 0
PUT:STACK(CODE);  STACK(TEXT);  STACK(NUM)
    ->ER6 IF CI+4 >= TI
    ->2 UNLESS TYPE = 1
    UNCHAIN;  ->ER3 IF TEXT # 0
    CMAX = CI
    STACK('Z');  STACK(CBASE);  STACK(1);   !EXTRA CLOSE B
    STACK(0)
    ->GO

!COMMAND INPUT ERROR REPORTS

ER1:SPACE;  PRINT SYMBOL(CODE)
ER2:CODE = SYM
    ->ER5
ER3:PRINTTEXT ' ()'
    ->ER7
ER4:PRINTTEXT ' TEXT FOR'
T(0):
ER5:SPACE;  PRINT SYMBOL(CODE&127)
    ->ER7
ER6:PRINTTEXT ' SIZE'
ER7:PRINT SYMBOL('?')
    NEWLINE;  CMAX = 0 IF CI # CBASE
10: ->1 IF INPUT = 0
    READ SYM
    ->10

!EXECUTE COMMAND LINE

GO: CI = CBASE
GET:CODE = INTEGER(CI)&127;  ->99 IF CODE = 0
    TEXT = INTEGER(CI+1)
    NUM = INTEGER(CI+2)
    CI = CI+3
REP:NUM = NUM-1
    ->S(CODE)
OK: ->REP UNLESS NUM = 0 OR NUM = STOP
    ->GET
S(92):                                      !INVERT
NO: ->GET IF NUM < 0
    CI = CI+3 AND ->GET IF INTEGER(CI) = 92
SKP:I = INTEGER(CI);  CI = INTEGER(CI+1) IF I = 'X'
    CI = CI+3
    NUM = INTEGER(CI-1)-1 AND ->NO IF I > 'X'
    ->SKP IF I # 0

!EXECUTION ERROR REPORT

    PRINTTEXT 'FAILURE: '
    IF CODE='O' OR CODE='W' START
       PRINT SYMBOL(CODE-10);  CODE = '-'
    FINISH
    PRINT SYMBOL(CODE)
    IF TEXT # 0 START
       PRINT SYMBOL('''')
       WHILE INTEGER(TEXT) # 0 CYCLE
          PRINT SYMBOL(INTEGER(TEXT))
          TEXT = TEXT-1
       REPEAT
       PRINT SYMBOL('''')
    FINISH
    NEWLINE
    READ CH(SYM) WHILE INPUT # 0
    PRINT1 = 0

!END OF COMMAND LINE

99: ->1 IF SYM # NL OR INPUT # 0
    ->1 UNLESS (MON>=0 AND PRINT1#LEND) OR (MON>0 AND PRINT2#FP+PP)
    NUM = 0; PRINT LINE
    ->1

!INDIVIDUAL COMMANDS

S('X'):                                     !OPEN BRACKET
    INTEGER(TEXT+2) = NUM+1
    ->GET
S('Z'):                                     !CLOSE BRACKET
    ->GET IF NUM = 0 OR NUM = STOP
    INTEGER(CI-1) = NUM
S('Y'):                                     !+COMMA
    CI = TEXT
    ->GET
S('R'):                                     !RIGHT SHIFT
    ->NO IF FP = LEND
    LOAD PP(BYTE(FP));  FP = FP+1
    ->OK
S('L'):                                     !LEFT SHIFT
    ->NO IF IN = SIN OR PP = LBEG
    FP = FP-1;  PP = PP-1;  LOAD FP(BYTE(PP))
    MS = 0
    ->OK
S('E'):                                     !ERASE
    ->NO IF FP = LEND
    FP = FP+1
    ->OK
S('O'):                                     !ERASE BACK
    ->NO IF PP = LBEG
    PP = PP-1
    ->OK
S('V'):                                     !VERIFY
    I = FP-1;  J = TEXT+1
V1: I = I+1; J = J-1
    K = INTEGER(J)
    ->V1 IF BYTE(I) = K
    ->NO IF K # 0
    MS = FP;  ML = I
    ->OK
S('F'):                                     !FIND
    ->NO IF MATCHED = 0
    ->OK
S('U'):                                     !UNCOVER
    ->NO IF MATCHED = 0;  PP = PP1
    ->OK
S('D'):                                     !DELETE
    ->NO IF MATCHED = 0;  FP = ML
    ->OK
S('T'):                                     !TRAVERSE
    ->NO IF MATCHED = 0
S('S'):                                     !SUBSTITUTE
    FP = ML IF FP = MS
S('I'):                                     !INSERT
    MAKE SPACE
    ->NO IF PP-LBEG+LEND-FP > 80
    I = TEXT
I1: ->OK IF INTEGER(I) = 0
    LOAD PP(INTEGER(I));  I = I-1
    ->I1
S('G'):                                     !GET (LINE FROM TT)
    PROMPT(':'); PROMPT(0)
    MAKE SPACE
    READ CH(I)
    ->NO IF I = ':'
    LEFT STAR
    WHILE I # NL CYCLE
       LOAD PP(I)
       READ CH(I)
    REPEAT
S('B'):                                     !BREAK (INSERT NEWLINE)
    LOAD PP(NL);  LBEG = PP
    ->OK
S('P'):                                     !PRINT
    PRINT LINE
    ->GET IF NUM = 0
S('M'):                                     !+MOVE
    RIGHT STAR
    ->NO IF FP = FEND
    LOAD PP(NL); LBEG = PP
M1: ISZ FP;  MAKE SPACE;  READ LINE
    ->OK
S('K'):                                     !KILL (LINE)
    PP = LBEG;  FP = LEND
K1: ->NO IF FP = FEND
    ->M1
S('J'):                                     !JOIN (DELETE NEWLINE)
    RIGHT STAR
    ->NO IF PP-LBEG > 80
    ->K1
S('W'):                                     !MOVE BACK
    ->NO IF IN = SIN
    MAKE SPACE
    ->NO IF LBEG = TOP
    LEND = FP-PP+LBEG-1
W1: K = BYTE(PP-1)
    ->W2 IF K = NL AND PP # LBEG
    FP = FP-1;  PP = PP-1;  LOAD FP(K)
    ->W1
W2: LBEG = PP;  MS = 0
    ->OK
T(1):                                       !%S, %C
    ->EOF IF CODE = 'C'
    SWITCH INPUTS
    ->99
EOF:CODE = 'C';                             !+EOF ON COMMAND STREAM
    SWITCH INPUTS IF IN = SIN
    CYCLE
       RIGHT STAR
       EXIT IF FP = FEND
       LOAD PP(NL);  LBEG = PP
       ISZ FP;  MAKE SPACE;  READ LINE
    REPEAT
    SELECT OUTPUT(MOUT)
    WHILE TOP # PP CYCLE
       PRINT CH(BYTE(TOP));  TOP = TOP+1
    REPEAT
ENDOFPROGRAM