begin ; ! April-1985-style Command Line Interpreter
option  "-nocheck-nostack-nodiag-low"

conststring  title="CLI version 04/10/90"
owninteger  i,trouble,tmode=0,bottom

recordformat  dictf(integer  beg,pos,lim,alt)
recordformat  eventfm(integer  event,(integer  sub or  integer  subevent),
integer  extra,pc,string (255) message,integerarray  r(0:15))

@16_1000 shortinteger  linenum
@16_10c0 routine  printsymbol(integer  k)
@16_10c4 routine  printstring(string (255)s)
@16_10c8 integerfn  readsymbol
@16_10cc integerfn  nextsymbol
@16_10d0 routine  prompt(string (255)s)
@16_10d4 integerfn  testsymbol
@16_10d8 routine  selectinput(integer  s)
@16_10dc routine  selectoutput(integer  s)
@16_10e0 routine  resetinput
@16_10e4 routine  resetoutput
@16_10e8 routine  closeinput
@16_10ec routine  closeoutput
@16_10f0 routine  openinput(integer  s,string (255)f)
@16_10f4 routine  openoutput(integer  s,string (255)f)
@16_10f8 routine  etheropen(integer  port,statport)
@16_10fc routine  etherclose(integer  port)
@16_1100 routine  etherwrite(integer  port,bytename  buf,integer  length)
@16_1104 integerfn  etherread(integer  port,bytename  buf,integer  max)
@16_1108 integerfn  fcomm(integer  cn,string (255)s)
!16_1124 %integerfn idiv(%integer dividend,divisor)

integerfn  idiv(integer  dividend,divisor)
integer  sign=(dividend!!divisor)&16_80000000,quotient=0,one=1
  dividend = -dividend if  dividend<0
  divisor = -divisor if  divisor<0
  cycle 
    exitif  divisor<<1<0; divisor = divisor<<1
    one = one<<1; result =0 if  one=0
  repeatuntil  dividend-divisor<0
  cycle 
    one = one>>1; exitif  one=0; divisor = divisor>>1
    while  dividend-divisor>=0 cycle 
      quotient = quotient+one; dividend = dividend-divisor
    repeat 
  repeat 
  quotient = -quotient unless  sign=0
  result  = quotient
end 

@16_1140 routine  setterminalmode(integer  m)
@16_117c integerfn  defname(string (255)s,record (dictf)name  d,integer  size)
@16_1180 integerfn  refname(string (255)s,record (dictf)name  d)
@16_1184 routine  transname(integer  tag,string (255)name  s)
@16_1188 routine  define event handler
@16_1190 routine  phex2(integer  x)
@16_1194 routine  phex4(integer  x)
@16_1198 routine  phex(integer  x)
@16_3400 record (eventfm)event
@16_35c4 shortinteger  userno
@16_3652 shortinteger  terminalmode
@16_372c byteinteger  cylock
@16_372d byteinteger  fsport
@16_3f98 integer  mainprogram,mexglob
@16_3fac integer  cliparamad
@16_3fb0 record (dictf)comdict,fildict,extdict,sysdict
@16_3ff0 integer  freebot,freetop,membot,memtop
routine  space; printsymbol(' '); end 
routine  newline; printsymbol(nl); end 

string (255)fn  itos(integer  v,p)
string (255)s
bytename  l
  routine  printsymbol(integer  x)
    l = l+1; l[l] = x
  end 
  routine  spaces(integer  x)
    x = x-1 and  printsymbol(' ') while  x>0
  end 
  routine  write(integer  n,p)
  integer  q,r
    if  p>0 start 
      p = ¬p; printsymbol(' ') and  p = p+1 if  n>=0
    finish 
    p = -120 if  p<-120
    q = idiv(n,10); *move.l d1,r
    if  q=0 start 
      p = p+1 if  n<0; spaces(-1-p); printsymbol('-') if  n<0
    else 
      p = p+1 if  p<0; write(q,p)
    finish 
    printsymbol(|r|+'0')
  end 
  s = ""; l == length(s)
  write(v,p)
  result  = s
end 

routine  write(integer  n,p)
  printstring(itos(n,p))
end 

predicate  starts(string (*)name  s,integer  c)
  falseif  length(s)=0
  falseunless  charno(s,1)=c
  true 
end 

string (255)fn  substring(string (255) s, integer  from,to)
string (255) t
  t = ""
  from = 1 if  from<=0
  to = length(s) if  to>length(s)
  while  from <= to cycle 
    t = t.tostring(charno(s,from));  from = from+1
  repeat 
  result  = t
end 

predicate  started(string (*)name  s,integer  c)
  falseunless  starts(s,c)
  s = substring(s,2,length(s))
  true 
end 

routine  truncate(string (*)name  s,integer  c)
  while  started(s,c) cycle 
  repeat 
end 

predicate  resolves(string (*)name  var,fore,
                    string (255)match,string (*)name  aft)
integer  i

  integerfn  INDEX(string (*)name  var,match)
  !Return index position of first occurrence of match within string
  integer  i=0,j,l
    l=length(match)
    cycle 
      result  = 0 if  i > length(var)-l
      i = i+1
      j = 0
      cycle 
        result  = i if  j = l
        j = j+1
      repeat  until  charno(var,i+j-1) # charno(match,j)
    repeat 
  end 

  routine  do aft
    aft = substring(var,i+length(match),length(var)) if  addr(aft) # 0
  end 

  i = index(var,match)
  false  if  i = 0
  if  addr(fore) # 0 start 
    if  addr(fore) # addr(var) start 
      fore = substring(var,1,i-1)
      do aft
    finish  else  start 
      do aft
      length(var) = i-1
    finish 
  finish  else  do aft
  true 
end 

!Aux (%signal does not work)
routine  signal(integer  e,s,i,string (255)m)
{}selectoutput(0); printstring("*BNAG*")
  event_message = m
  event_extra = i
  e = e&15+(s&255)<<4
  *move.l e,d0
  *jsr 16_1114
end 

routine  standardise(string (*)name  s)
bytename  k
integer  i
  for  i = 1,1,length(s) cycle 
    k == charno(s,i)
    k = k&95 if  'A'<=k&95<='Z'
  repeat 
end 

routine  lock
  cylock = cylock+1
end 

routine  unlock
  returnif  cylock=0
  cylock = cylock-1
  cylock = 0 and  signal(0,1,0,"^Y") if  cylock=128
end 

routine  diagnose; !Print event message, line number, etc
integer  e,s,x,i
shortintegername  ac,ir,sr

  routine  symbol(integer  x)
    if  ' '<=x<=126 start 
      printsymbol('''') if  '0'<=x<='9'
      printsymbol(x)
      printsymbol('''') if  '0'<=x<='9'
    finishelse  write(x,0)
  end 

  e = event_event&15
  s = event_subevent
  x = event_extra
  returnif  e=0 and  s=0;                !normal %stop
  selectinput(0); resetinput; closeinput
  returnif  e=0 and  s=1;                !^Y
  newline
  printstring("** ")
  printstring("Event "); write(e,0); space; write(s,0)
rest:
  space and  write(x,0) unless  x=0
somerest:
  space and  printstring(event_message) unless  event_message=""
norest:
  newline; printstring("Stopped at")
  if  linenum#0 start 
    printstring(" line "); write(linenum,0)
  finish 
  if  linenum=0 or  e=0 start 
    printstring(" PC "); phex(event_pc)
  finish 
  newline
end 

routine  close all ins
integer  i=4
  onevent  9 start 
  finish 
  cycle 
    i = i-1; selectinput(i)
    returnif  i=0; closeinput
  repeat 
end 

routine  close all outs
integer  i=4
  onevent  9 start 
  finish 
  cycle 
    i=i-1; selectoutput(i)
    returnif  i=0; closeoutput
  repeat 
end 

routine  uclose all outs
integer  i=4
  onevent  9 start 
  finish 
  cycle 
    i = i-1; exitif  i=0
    openoutput(i,"")
  repeat 
  selectoutput(0)
end 

integerfn  allocate(integer  amount)
  amount = (amount+3)>>2<<2
  result  = 0 if  bottom+amount+1000>freetop
  bottom = bottom+amount
  result  = bottom-amount
end 

routine  loadfile(string (255)n,integername  s,l)
string (255)z
integer  x,y,dict
integer  pos=1,blocks,pad

  integerfn  hex
  integer  n=0,k
    cycle 
      result  = n if  pos>=length(z); pos = pos+1
      k = charno(z,pos-1)-'0'
      result  = n if  k<0
      n = n<<4+k
    repeat 
  end 
  
  standardise(n)
{}printstring("Loading ";n)
  dict = refname(n,fildict)
  if  dict>0 start                         {If entry exists
    x = integer(dict); y = integer(dict+4)
    if  x<y start                          {if file remembered
{}printstring(" - already there"); newline
      s = x; l = y-x; return 
    finish 
  finish 
  s = 0; l = 0
{}printstring(" - reading it")
  z = "Z".tostring('0'+userno).n.tostring(nl)  {Read file into store
  lock
    etherwrite(fsport,charno(z,1),length(z))
    length(z) = etherread(fsport,charno(z,1),255)
  unlock
  if  charno(z,1)='-' start 
    x = charno(z,2)-'0'; y =length(z)
    y = y-1 if  charno(z,y)=nl {and it ought to be}
    n = "Load ".n
    n = n." fails:"
    z = substring(z,3,y)
    z = n.z
{}printstring(z); newline
    signal(3,3,x,z)
  finish 
  blocks = hex; pad = hex
  x = blocks<<9-pad
  y = allocate(x)
  if  y<=0 start 
    y = bottom
    lock
      cycle 
        x = etherread(fsport,byteinteger(y),512)
      repeatuntil  x#512
    unlock
{}printstring("No space"); newline
    signal(2,1,l,"No space for ".n)
  finish 
  s = y; l = x
  lock
    cycle 
      x = etherread(fsport,byteinteger(y),512)
      y = y+x
    repeatuntil  x#512
  unlock
  if  dict>0 start 
    if  integer(dict)=integer(dict+4)=-1 start  {marked for remembering
      integer(dict) = s; integer(dict+4) = s+l
      freebot = bottom
    finish 
  finish 
{}printstring(" - Done"); newline
end 

routine  define symbol(string (255)symbol,value)
integer  stag,vtag
  standardise(symbol)
  stag = defname(symbol,comdict,8)
  if  stag=0 start 
    printstring("*Command dict full"); newline; return 
  finish 
  vtag = defname(value,fildict,8)
  if  vtag=0 start 
    printstring("*File dict full"); newline; return 
  finish 
  if  stag<0 start 
    stag = stag<<1>>1
!   printstring(symbol.": ".value." replaces ")
!   transname(integer(stag),symbol); printstring(symbol); newline
  finish 
  integer(stag) = vtag<<1>>1
end 

routine  run program(string (255)file,parm)
recordformat  mex header f(short  magic,flags,export,import,
   integer  code,short  reset,main,integer  own,stack,spare1,spare2)
record (mexheaderf)name  h
bytename  first,second
integer  start,limit

  routine  run(integer  xpc,xd6)
  integer  env
    onevent  0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 start 
      *move.l env,a6
      set terminal mode(tmode)
      if  event_event!event_subevent=0 c 
      then  close all outs elsestart 
        uclose all outs
        selectinput(0); resetinput; closeinput
      finish 
      close all ins; diagnose; return 
    finish 
    tmode = terminalmode
    *move.l a6,env
    linenum = 0
    *move.l xpc,a0
    *move.l xd6,d6
    *jsr (a0)
    signal(0,0,0,"")
  end 

  cliparamad = addr(parm)
  bottom = freebot
  loadfile(file,start,limit); limit = limit+start
  first == byteinteger(start)
  second == byteinteger(start+1)
  unless  first=16_fe start 
    printstring("File ".file." starts with ")
    phex2(first); printstring(" instead of FE")
    newline; return 
  finish 
  while  started(parm,' ') cycle ; repeat 
  parm = tostring(255) if  parm=""
  prompt(":"); selectinput(0); selectoutput(0)
  mainprogram = 0
  if  1<=second<=3 start ; !Mex format
    mainprogram = start
    loadfile("fmac:mexrun.mob",start,limit); limit = limit+start
    parm = "" if  parm = tostring(255)
    h == record(start)
    start = start+32+h_export
    if  h_import!h_own#0 c 
    or  shortinteger(start+h_reset<<1)#16_4e75 start 
      printstring("File fmac:mexrun.mob is not as expected"); newline; return 
    finish 
selectoutput(0); printstring("Diving in"); newline
    run(start+h_main<<1,(bottom+3+256)&¬3)
selectoutput(0); printstring("Emerged"); newline
  else 
    printstring("Warning: ".file." starts with FE"); phex2(second); newline
    run(limit-4,(bottom+3+256)&¬3)
  finish 
end 

routinespec  obeystream(string (255)parms)

routine  obeyfile(string (255)verb,parms)
  onevent  3,4,9 start 
    printstring(event_message); newline
    return 
  finish 
  openinput(1,verb) {Test whether file accessible}
  selectinput(0); closeinput
  openinput(0,verb)
  selectinput(1); closeinput
  selectinput(0)
  obeystream(parms)
end 

predicate  verify
string (255)x
integer  tag
  tag = refname("VERIFY",comdict)
  falseif  tag<=0
  tag = integer(tag)
  transname(tag,x)
  tag = length(x)
  falseif  tag=0
  tag = charno(x,tag)&1
  falseif  tag=0
  true 
end 

routine  obeyline(string (255)line)
string (255)verb,param,junk
integer  tag,pos,sym,lives=9
constintegerarray  bit(0:3) = 0,
{ ?>=<;:9876543210/.-,+*)('&%$#"!
2_01010111111111110100111111111100,
2_11111111111111111111111111111111,
2_01111111111111111111111111111111
    
  onevent  2,3,4,9 start 
    printstring(event_message); newline
    return 
  finish 

!First parse the line, split it up into verb and parameters.
!Then perform verb translation.

  truncate(line,' '); truncate(line,'{'); truncate(line,' ')
  if  started(line,'@') start 
    truncate(line,' ')
    line = "@".line
  finish 
  cycle 
    verb = ""
    pos = 1
    cycle 
      exitif  pos>length(line); sym = charno(line,pos)
      exitif  bit(sym>>5&3)&1<<(sym&31)=0
      verb = verb.tostring(sym); pos = pos+1
    repeat 
    returnif  verb=""
!!  sym = '=' %and pos = pos+1 %if sym='_'  {suppress symbol translation}
    if  charno(verb,length(verb))='_' start 
      sym = '='; length(verb) = length(verb)-1
    finish 
    param = substring(line,pos,length(line))
    exitif  sym='='
    standardise(verb)
    tag = refname(verb,comdict)
    exitif  tag<=0
    tag = integer(tag)
    transname(tag,verb)
    line = verb.param
    lives = lives-1
    if  lives=0 start 
      printstring("Symbol translation looping: ".line); newline
      verb = ""; return 
    finish 
  repeat 

  if  verify start 
    printstring(verb)
    printstring(param)
    newline
  finish 

!Now decide whether the command is a symbol definition or
!a request to run a program or a request to obey another file.

  if  length(param)>1 and  charno(param,1)='=' start 
    definesymbol(verb,substring(param,2,length(param)))
  elseif  starts(verb,'@')
    verb = substring(verb,2,length(verb))
    verb = verb.".COM" unless  resolves(verb,junk,".",junk)
    obeyfile(verb,param)
  else 
    verb = verb.".MOB" unless  resolves(verb,junk,".",junk)
    runprogram(verb,param)
  finish 
end 

routine  obeystream(string (255)parms)
string (63)array  parameter(1:8)
string (255)parm,rest,line
integer  i

  integerfn  nsym
  integer  k
    k = nextsymbol&127
    k = nl if  k<' '
    result  = k
  end 

  integerfn  rsym
  constinteger  esc=27
  integer  k,x
    x = nsym
    readsymbol(k); result  = x unless  k=esc
    readsymbol(k); result  = x unless  k='?'
    readsymbol(k); result  = x
  end 

  predicate  fits(integer  n)
    trueif  length(line)+n<=255
    false 
  end 

  routine  read line
  string (63)p
  integer  k,kk,i
    selectinput(0)
    prompt("} ")
    cycle 
      cycle 
        line = ""
        cycle 
          k = rsym; exitif  k=nl
          if  k='%' and  '1'<=nsym<='8' start 
            k = rsym; p = parameter(k-'0')
            if  fits(length(p)) start 
              line = line.p
            else 
              line = line."%" if  fits(1)
              line = line.tostring(k) if  fits(1)
            finish 
          elseif  k='-' and  nsym=nl
            k=rsym
          else 
            line = line.tostring(k) if  fits(1)
          finish 
        repeat 
        exitunless  started(line,'!')
        continueunless  starts(line,'!')
        kk = 0
        for  i = 2,1,length(line) cycle 
          k = charno(line,i); kk = k unless  k=' '
          printsymbol(k)
        repeat 
        newline unless  kk=':'
      repeat 
    repeatuntil  line#""
  end 

  onevent  0,9 start 
printstring("Event in obeystream "); printsymbol(event_event+'0'); newline
    resetinput; closeinput
    return 
  finish 
  parms = parm." ".rest while  resolves(parms,parm,"  ",rest)
  parms = rest while  resolves(parms,parm," ",rest) and  parm=""
  parms = parm.",".rest while  resolves(parms,parm," ",rest)
  parms = parm.",".rest while  resolves(parms,parm,"/",rest)
  parms = parms.","
  for  i = 1,1,8 cycle 
    parm = "" unless  resolves(parms,parm,",",parms)
    parameter(i) = parm
  repeat 
  cycle 
    readline
    obeyline(line)
  repeat 
end 

integerfn  handle event; ! Unwind stack frames until event trap is found
constinteger  bsr=16_6100,jsrel=16_4eba,bra=16_6000,link=16_4e50
constinteger  jmpabslong=16_4ef9,moveltoa6=16_2c7c
constinteger  jsra6=16_4eae,jsrabsshort=16_4eb8,jsrabslong=16_4eb9
integer  frame,bit
integer  retad,entry,eventbeg,eventend,temp,olda6
integername  a4,a5,a6,fp,sp

  predicate  ok(integer  lo,hi); ! False unless accessible
    falseunless  lo<=hi and  lo&1=0
    trueif  membot<=lo and  hi<memtop
    trueif  0<=lo and  hi<16_4000
    false 
  end 

!  %returnif event_event=9 %and event_subevent=3 %and event_extra=0;!FShangup

! A4 and A5 are assumed to be used as base registers.  Their values
! are interpreted as stack frame pointers established using the
! LINK instruction, but only if they point into the stack at an
! even address.  The lower of A4 and A5 is selected as pointing to
! the most recent stack frame.  It is assumed to point at a pair
! (Old Frame Pointer, Return Address).  The return address is used
! to find the BSR instruction which called the current procedure,
! thereby locating its entry-point.  As confirmation, the first
! instruction of the thus located procedure is verified to be a
! LINK instruction with the appropriate register number.  The word
! preceeding the entry point is taken to be the event mask, bit i
! being set if event i is trapped at this level. The word
! preceeding that is taken to be the relative displacement of the
! (start of the) event block.  The trap is ignored if the event was
! signalled from within that event block, so we need to know where
! the end is.  We assume that the event block is preceeded by a BRA
! instruction to just past the event block.

  bit=1<<event_event
  a4 == event_r(12); a5 == event_r(13); a6 == event_r(14); sp == event_r(15)
  cycle 
    cycle 
      frame = 0; fp == sp
      frame = 5 and  fp == a5 if  ok(a5,a5+7) and  sp<=a5
      frame = 4 and  fp == a4 if  ok(a4,a4+7) and  sp<=a4 c 
                              and  (frame=0 or  a4<fp)
      result =1 if  frame=0
      retad = integer(fp+4)
      olda6 = a6
      result =2 unless  ok(retad-6,retad-1)
      if  shortinteger(retad-4)=bsr start 
        entry = shortinteger(retad-2)+retad-2
      elseif  shortinteger(retad-4)=jsrel
        entry = shortinteger(retad-2)+retad-2
      elseif  shortinteger(retad-4)=jsrabsshort
        entry = shortinteger(retad-2)
      elseif  shortinteger(retad-4)=jsra6
        result =3 unless  ok(fp+8,fp+11)
        a6 = integer(fp+8)
        temp = a6+shortinteger(retad-2)
        result =4 unless  ok(temp,temp+11)
        result =5 unless  shortinteger(temp)=moveltoa6 c 
                 and  shortinteger(temp+6)=jmpabslong
        result =6 unless  integer(temp+2)=olda6
        entry = integer(temp+8)
      elseif  shortinteger(retad-6)=jsrabslong
        entry = integer(retad-4)
      finishelseresult =7
      cycle 
        result =8 unless  ok(entry-4,entry+3)
        temp = shortinteger(entry)
        if  temp=bra start 
          entry = shortinteger(entry+2)+entry+2
        elseif  temp=jmpabslong
          entry = integer(entry+2)
        finishelseexit ;                       ! Not stepping stone ->
      repeat 
      result =9 unless  temp=link+frame
      exitif  shortinteger(entry-2)&bit#0;     ! Trap found ->
      sp = fp+8; fp = integer(fp);             ! Simulate UNLK, RTS
    repeat 
    eventbeg = shortinteger(entry-4)+entry-4
    result =10 unless  ok(eventbeg-4,eventbeg-1)
    result =11 unless  shortinteger(eventbeg-4)=bra
    eventend = shortinteger(eventbeg-2)+eventbeg-2
    result =12 unless  ok(eventbeg,eventend-1)
    a6 = olda6 andexitunless  eventbeg<event_pc<=eventend
    sp = fp+8; fp = integer(fp)
  repeat 
  sp = fp+shortinteger(entry+2);  !simulate link
  event_r(8) = eventbeg
  temp = addr(event_r(0))
  *move.l temp,a0
  *movem.l (a0),#16_ffff
  *jmp (a0)
end 

shortinteger(16_3f00-i<<2) = 16_4afc for  i = 1,1,64*3

mexglob = 0
cylock = 0

event_event = -1
define event handler
unless  event_event=-1 start 
  trouble = handle event;        !Returns only if trouble
  selectoutput(0)
  set terminal mode(tmode)
  printstring("*Trouble "); printsymbol(trouble+'@'); printsymbol(':')
  uclose all outs; close all ins
  selectinput(0); resetinput; closeinput
  selectoutput(0); diagnose
{}printstring(" tsk")
  *sub.l #2048,sp
{}printstring(" tsk"); newline
  cycle 
    obeyfile(":t","")
  repeat 
finish 
event_event = 0
*sub.l #2048,sp

printstring(title); newline
bottom = freebot
obeyfile("fmac:astartup.com","")
cycle 
  obeyfile(":t","")
repeat 

endofprogram