!!This version of ECCE, derived from a paper copy believed to
!!date from 1975, removes PDP9/15 machine code, and other
!!IMP9/15 system dependencies, and I'm hopeful that as a result
!!it might well work if compiled with a generic Imp compiler.

!!RWT Feb 2002

!!Code slightly rearranged to fit the model of Peter Stephen's Imp to C
!!translator.  This now compiles and runs exactly as on the pdp9/15
!!Remember to run it with redirected input, eg ecce < file.txt > file2.txt

!!The I/O library currently assumes you are running on Unix.
!!NOTE: There IS a bug in this implementation.  Lines of more
!!than 120 characters have a newline inserted at the 120th character
!!on output.  This *may* be an original bug rather than an artifact
!!of the translation or run-time library..

!!GT 02 Mar 2002

%external %integer outstream

%owninteger in=1;                           !current input stream
%constinteger min=1;                        !main input stream
%constinteger mout=1;                       !main output stream
%constinteger sin=2;                        !secondary input stream
%constinteger sextra=122;                   !extra buff for sin
%constinteger size=30000;                   !of edit buffer (bytes)
%owninteger mon=0;                          !monitor indic
%owninteger print1=0,print2=0;              !print indicators
%constinteger stop=-5000;                   !loop stop (const)
%integer i,j,k,pp1,sym
%integer code;                              !command code letter
%integer text;                              !text pointer
%integer num;                               !repetition number
%integername mainfp;                        ! == fp or mfp (for sin)
%constinteger cbase=1,tbase=120
%ownintegerarray c(cbase:tbase);            !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 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
%byteintegerarray byte(1:size)
%owninteger top = 2;                     !top of buff (index)
%owninteger bot = size-sextra;           !bottom of buff (index)
%integer lbeg;                              !line start (index)
%integer pp;                                !previous pointer (index)
%owninteger fp=0;                           !file pointer (index)
%integer lend;                              !line end (index)
%owninteger fend;                           !end of file in buff (index)
%owninteger ms=0;                           !match start (index)
%owninteger ml=0;                           !match limit (index)
! 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;                         !ditto

%routine prompt(%string(1) s); ! Emulating pdp9/15 routine
  %integer oldstream, c
  c = charno(s, 1)
  %if c # 0 %then %start  ; ! c = 0 is supposed to flush output on the 9/15
    oldstream = outstream
    select output(0)
    print symbol(c)
    select output(oldstream)
  %finish
%end

%routine load pp(%integer k);               !!!also increments pp
    byte(pp) = k;  pp = pp+1
%end

%routine load fp(%integer k)
    byte(fp) = k
%end

%routine left star
    %cycle
       %return %if pp = lbeg
       fp = fp-1;  pp = pp-1
       load fp(byte(pp))
    %repeat
%end

%routine right star
    %cycle
       %return %if fp = lend
       load pp(byte(fp))
       fp = fp+1
    %repeat
%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 symbol(sym)
    %finish
%end

%routine read item
    type = 1
    %cycle
       read sym %until sym # ' '
       %return %if sym < 32;                !nl
       sym = sym-32 %if sym >= 96;          !ensure upper case
       type = symtype(sym)
       %return %unless type&15 = 0
       %exit %unless type=32
       pos1 = pos2;  pos2 = pos3
       pos3 = (sym-'X')<<6+1
    %repeat
    %if type = 0 %start
       num = sym-'0'
       %cycle
          read symbol(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
    %cycle
       text = chain;  %return %if text = 0
       chain = c(text+1);  c(text+1) = ci
    %repeat %until c(text) = 'X'
%end

%routine stack(%integer v)
    c(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;           !output about half
    p2 = lbeg %if code = 'C';               !but all if closing
    %monitor %and %stop %if p2 = top;               !!!logical error
    %cycle
       k = byte(p1);  print symbol(k);  p1 = p1+1
    %repeat %until k = nl %and p1-p2 >= 0
    select output(0)
    lbeg = top+lbeg-p1;  p2 = pp;  pp = top
    %cycle
       %return %if p1 = p2
       load pp(byte(p1));  p1 = p1+1
    %repeat
%end

%routine read line
%integer k
!    %on %event 9 %start
!       ->eof
!    %finish
    %if fp # fend %start
       lend = fp
       lend = lend+1 %while byte(lend) # nl
       %return
    %finish
    ms = 0;  print1 = 0;  print2 = 0
    select input(in)
    fp = bot-sextra+1
    %cycle
       %if fp # bot %then read symbol(k) %else k = nl
       ->eof %if k<0
       load fp(k);  fp = fp+1
    %repeat %until k = nl
    fend = fp; lend = fend-1
    fp = bot-sextra+1
    select input(0)
    %return
eof:fp = bot;  lend = fp;  fend = lend
    load fp(nl)
    select input(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('^') %if p # lbeg %and num = 0
          p = fp
       %finish
       %exit %if p = lend
       print symbol(byte(p))
       p = p+1
    %repeat
    print string("**END**") %if p = fend
    newline
%end

%integerfn matched
%integer i,j,k,l,t1,fp1,lim
    lim = c(ci-3)&(\127);  t1 = c(text)
L1:  pp1 = pp;  fp1 = fp
    ->L3 %unless fp = ms %and (code='F' %or code='U')
    k = byte(fp)
L2:  load pp(k); fp = fp+1
L3:  ->L10 %if fp = lend
    k = byte(fp)
    ->L2 %unless k = t1
    i = fp;  j = text
L6:  i = i+1;  j = j-1
    l = c(j)
    ->L6 %if byte(i) = l
    ->L2 %if l # 0
    ms = fp;  ml = i
    %result = 1
L10: 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
       ->L1
    %finish
    pp = pp1;  fp = fp1
    %result = 0
%end


%externalintegerfn main;                                     !edit15: ecce for pdp9/15
!initialise
%switch t(0:12)
%switch s('A':'\')

!%on %event 9 %start
!    printstring("Caught event"); newline
!    ->eof
!%finish

    select input(0)
    pp = top-1;  load pp(nl);               !for bouncing off
    lbeg = pp;  mainfp == fp
    stored(1) = nl;  stored(65) = nl;  stored(129) = nl
    select output(0); print string("EDIT
")
    read line

!read command line

L1:  prompt(">")
    read item; ->eof %if sym<0; ->L1 %if type = 1
    ci = cbase;  ti = tbase;  chain = 0
    %if type = 0 %and cmax # 0 %start
       c(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)
    %finish
L2:  i = type&15;  ->er2 %if i < 4
    code = sym;  text = 0;  num = 1;        !default values
    read item
    ->t(i)
t(2):                                       !%x, %y, %z
    ->er1 %if sym # '='
    i = (code-'X')<<6
    %cycle
       read sym
       i = i+1;  stored(i) = sym
       ->L1 %if sym = nl
    %repeat
t(3):                                       !%m, %f, %q
    mon = 'M'-code
    ->L1
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
L61: read sym
    %if sym # nl %start
       %if sym # i %start
          ->er6 %if ti <= ci
          c(ti) = sym;  ti = ti-1
          ->L61
       %finish
    %finish %else %start
       pend = sym
       ->er4 %unless code = 'S' %or code = 'I'
    %finish
    ->er4 %if ti = text %and code # 'S'
    c(ti) = 0;  ti = ti-1
    ->L81
t(8):                                       !move,erase
    ->L100 %unless sym = '-'
    code = code+10
L81: read item
    ->L101
t(9):                                       !close bracket
    unchain;  ->er3 %if text = 0
    code = 'Z'; c(text+2) = num
    text = text+3
t(10):                                      !+get,kill,etc.
L100:->er1 %if type = 3
L101:read item %if type = 0
    ->put
t(11):                                      !open bracket
    code = 'X'
    ->L121
t(12):                                      !comma
    code = 'Y'
    read item %if type = 1
L121:text = chain;  chain = ci
    num = 0
put:stack(code);  stack(text);  stack(num)
    ->er6 %if ci+4 >= ti
    ->L2 %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:print string(" ()")
    ->er7
er4:print string(" TEXT FOR")
t(0):
er5:space;  print symbol(code&127)
    ->er7
er6:print string(" SIZE")
er7:print symbol('?')
    newline;  cmax = 0 %if ci # cbase
L10: ->L1 %if sym<32
    read sym
    ->L10

!execute command line

go: ci = cbase
get:code = c(ci)&127;  ->L99 %if code = 0
    text = c(ci+1)
    num = c(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 c(ci) = '\'
skp:i = c(ci);  ci = c(ci+1) %if i = 'X'
    ci = ci+3
    num = c(ci-1)-1 %and ->no %if i > 'X'
    ->skp %if i # 0

!execution error report

    print string("FAILURE: ")
    %if code='O' %or code='W' %start
       print symbol(code-10);  code = '-'
    %finish
    print symbol(code)
    %if text # 0 %start
       print symbol('''')
       %while c(text) # 0 %cycle
          print symbol(c(text))
          text = text-1
       %repeat
       print symbol('''')
    %finish
    newline
    print1 = 0

!end of command line

L99: ->L1 %if sym # nl
    ->L1 %unless (mon>=0 %and print1#lend) %or (mon>0 %and print2#fp+pp)
    num = 0; print line
    ->L1

!individual commands

s('X'):                                     !open bracket
    c(text+2) = num+1
    ->get
s('Z'):                                     !close bracket
    ->get %if num = 0 %or num = stop
    c(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 = c(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 c(i) = 0
    load pp(c(i));  i = i-1
    ->i1
s('G'):                                     !get (line from tt)
    prompt(":")
!!  make space
    read symbol(i)
    ->no %if i = ':'
    left star
    %while i # nl %cycle
       load pp(i)
       read symbol(i)
    %repeat
s('B'):                                     !+break (insert newline)
    make space
    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: fp = fp+1;  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
    ->L99
eof:code = 'C';                             !+eof on command stream
    switch inputs %if in = sin
    %cycle
       right star
       %exit %if fp = fend
       load pp(nl);  lbeg = pp
       fp = fp+1;  make space;  read line
    %repeat
    select output(mout)
    %while top # pp %cycle
       print symbol(byte(top));  top = top+1
    %repeat
    select output(0)
    %if code # 'C' %then %monitor
%end

%endoffile
