%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)
%OWNINTEGER MODE=0;                         !CONSOLE OR TEXT COMMANDS

%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));        !(CONST AD)
%INTEGER TBASE;  TBASE = CBASE+119;         !(CONST AD)
%INTEGER CI;                                !COMMAND INDEX (AD)
%INTEGER TI;                                !TEXT INDEX (AD)
%OWNINTEGER CMAX=0;                         !COMMAND MAX (AD)

%OWNINTEGERARRAY STORED(1:192);             !DEFS OF X,Y,Z
%OWNINTEGER POS1=0, POS2=0, POS3=0;         !DEF POINTERS;

    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;             !BUFFERED DEVICE
       I = I-256 %IF OUTDEV > 1
    %REPEAT

%INTEGERARRAY A(0:I)
%INTEGER TOP;  TOP = ADDR(A(0))<<1+1;       !TOP OF BUFF (CONST BAD)
%INTEGER BOT;  BOT = TOP+I+I-SEXTRA;        !BOTTOM OF BUFF (CONST BAD)
%INTEGER LBEG;                              !LINE START (BAD)
%INTEGER PP;                                !PREVIOUS POINTER (BAD)
%INTEGER FP;                                !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:13)
%SWITCH S('A':92)

%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,61,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  [  \  ]     _


%INTEGERFNSPEC BYTE(%INTEGER N)

%ROUTINE READ SYM
    %IF PEND # 0 %THEN SYM=PEND %AND PEND=0 %ELSE %START
       %IF MODE<0 %START
          %IF FP=FEND %START
             SYM=NL; MODE=0
             %RETURN
          %FINISH
          SYM=BYTE(FP)
          FP=FP+1
          %IF SYM=NL %START
             LEND=LEND+1 %UNTIL BYTE(LEND)=NL
             MODE=0
          %FINISH
          %RETURN
       %FINISH
       %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
1:  TYPE = 1
    READ SYM %UNTIL SYM # ' '
    %RETURN %IF SYM < ' ';                  !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 SYM
          %EXIT %UNLESS '0' <= SYM <= '9'
          NUM = (NUM<<2+NUM)<<1-'0'+SYM
       %REPEAT
       PEND = SYM
    %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 LOAD PP(%INTEGER K)
!BYTE(PP) = K
%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
%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))
    PP = PP+1;  FP = FP+1
    ->1
%END


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

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

%ROUTINE BREAK
    LOAD PP(NL);  PP = PP+1;  LBEG = PP
%END

%ROUTINE REFRESH
    FP = FP+1;  MAKE SPACE;  READ LINE
%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
%OWNINTEGER MARKER=94
%INTEGER P
    PRINT1 = LEND;  PRINT2 = FP+PP
    P = LBEG
    %CYCLE 
       %IF P = PP %START
          PRINT SYMBOL(MARKER) %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)
    %CYCLE
       PP1 = PP;  FP1 = FP
       %WHILE FP # LEND %CYCLE
          K = BYTE(FP)
          %IF K = T1 %AND (FP#MS %OR CODE='D' %OR CODE='T') %START
             I = FP;  J = TEXT
             %UNTIL BYTE(I) # L %CYCLE
                I = I+1;  J = J-1;  L = INTEGER(J)
                ->YES %IF L = 0
             %REPEAT
          %FINISH
          LOAD PP(K)
          PP = PP+1;  FP = FP+1
       %REPEAT
       LIM = LIM-128
       %EXIT %IF LIM = 0 %OR FP = FEND
       %IF CODE # 'U' %THEN BREAK %ELSE PP = PP1
       REFRESH
    %REPEAT
    PP = PP1;  FP = FP1
    %RESULT = 0
YES:MS = FP;  ML = I
    %RESULT = 1
%END


!INITIALISE
    PP = TOP-1;  BREAK;                     !FOR BOUNCING OFF
    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
2:  PROMPT(PR) %IF PR # 0 %AND MODE=0;  PR = '>'
    READ ITEM;  ->2 %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
    ->3 %UNLESS SYM = '%'
    READ SYM;  SYM = SYM-32 %IF SYM >= 96
    CODE = SYM;  ->ER5 %IF CODE < 'A'
    READ ITEM
    ->T(SYMTYPE(CODE)>>4)
T(1):!%C, %S
    ->EOF %IF CODE = 'C'
    SWITCH INPUTS
    ->99
T(2):                                       !%X, %Y, %Z
    ->ER1 %IF SYM # '='
    I = (CODE-'X')<<6+1
    %CYCLE
       READ SYM
       STORED(I) = SYM
       %EXIT %IF SYM = NL
       ->ER6 %IF I&63 = 0
       I = I+1
    %REPEAT
    ->2
T(3):                                       !%M, %F, %Q
    MON = 'M'-CODE
       ->2
3:  I = TYPE&15;  ->ER2 %IF I < 4
    CODE = SYM;  TEXT = 0;  NUM = 1;        !DEFAULT VALUES
    READ ITEM
    ->T(I)
T(13):                                      !CHANGE CONSOLE<=>TEXT
    MODE=\MODE
    ->2
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
    %CYCLE
       READ SYM
       %EXIT %IF SYM = I
       PEND = SYM %AND %EXIT %IF SYM = NL
          ->ER6 %IF TI <= CI
          INTEGER(TI) = SYM;  TI = TI-1
    %REPEAT
    ->ER4 %IF (SYM=NL %OR TI=TEXT) %AND CODE # 'I' %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
    ->3 %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
    READ SYM %WHILE INPUT # 0;              !IGNORE TYPE AHEAD
    ->1

!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('\'):                                     !INVERT
NO: ->GET %IF NUM < 0
    CI = CI+3 %AND ->GET %IF INTEGER(CI) = '\'
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))
    PP = PP+1;  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
    ->NO %IF FP # MS
     FP = ML
S('I'):                                     !+INSERT
    MAKE SPACE
    ->NO %IF PP-LBEG+LEND-FP > 80
    I = TEXT
    %CYCLE
       ->OK %IF INTEGER(I) = 0
       LOAD PP(INTEGER(I))
       PP = PP+1;  I = I-1
    %REPEAT
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);  PP = PP+1
       READ CH(I)
    %REPEAT
S('B'):                                     !+BREAK (INSERT NEWLINE)
    BREAK
    ->OK
S('P'):                                     !PRINT
    PRINT LINE
    ->GET %IF NUM = 0
S('M'):                                     !+MOVE
    RIGHT STAR
    ->NO %IF FP = FEND
    BREAK
M1: REFRESH
    ->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
    %CYCLE
       K = BYTE(PP-1)
       %EXIT %IF K = NL %AND PP # LBEG
       FP = FP-1;  PP = PP-1;  LOAD FP(K)
    %REPEAT
    LBEG = PP;  MS = 0
    ->OK

!CLOSE FILE (+EOF ON COMMAND STREAM)
EOF:CODE = 'C'
    SWITCH INPUTS %IF IN = SIN
    %CYCLE
       RIGHT STAR
       %EXIT %IF FP = FEND
       BREAK
       REFRESH
    %REPEAT
    SELECT OUTPUT(MOUT)
    %WHILE TOP # PP %CYCLE
       PRINT CH(BYTE(TOP));  TOP = TOP+1
    %REPEAT
%ENDOFPROGRAM
