!Rom generator program for Finite State Machines
!RWT 1984

!This program reads in a description of a finite state machine,
!in the format given below, and produces as output one or more
!binary files which, in conjunction with the FROMBIN command
!and the MWARE program can be used to produce control PROMs.

! Input:
! Number of inputs
!  Input Names
! Number of Outputs
!  Output Names
! Number of state bits
! { state : { = clone-state } { Output = expression }*
!           { next-state-number expression }* ; }* .
! Expressions involve input names, 0, 1, and )(+.\ and are
!  terminated by a colon, thereby allowing them to extend
!  over several lines.
! Output:
! A binary file for each byte the rom is wide.
! The state bits form the most significant part of both
! address and data, the input and output bits increase
! in significance, i.e. the first input bit is the least
! significant address bit, the last output bit is the most
! significant data bit (but less significant than the least
! significant state bit).

%begin

%include "inc:util.imp"
%string(31)file = cliparam

  file = file.tostring('.')
  openinput(1,file."fsm"); selectinput(1)
  openoutput(1,file."lis"); selectoutput(1)

%integer line=1, pend=-1, i
%integer inputs, outputs, statebits
%integer inbits, outbits, max
%integer errorcount=0

%routine error(%string(255)s)
  errorcount = errorcount+1
  printstring(s." at line "); write(line,0); newline
  selectoutput(0)
  printstring(s." at line "); write(line,0); newline
  selectoutput(1)
%end

%routine wbsym(%integer x); !Write back symbol
  pend = x
%end

%integerfn rsym;            !Read symbol, skip comments, count lines
%integer x
  x = pend; pend = -1; %result = x %unless x<0
  %cycle
    readsymbol(x); line = line+1 %if x=nl
    %if x='{' %start
      %cycle
        readsymbol(x); line = line+1 %if x=nl
      %repeatuntil x='}'
    %elseif x='|'
      readsymbol(x) %until x=nl
      line = line+1
    %finishelseresult = x
  %repeat
%end

%integerfn sigsym;           !Read significant symbol
%integer x
  x = rsym %until ' '#x#nl
  %result = x
%end

%integerfn rdec;             !Read decimal number
%integer x=sigsym,n=0
  %unless '0'<=x<='9' %start
    error("Warning: decimal number expected")
    %result = 0
  %finish
  %cycle
    %exitunless '0'<=x<='9'
    n = n*10-'0'+x
    x = rsym
  %repeat
  wbsym(x); %result = n
%end

%routine rname(%string(15)%name s); !Read signal name or special symbol
%integer x = sigsym
  s = tostring(x)
  %returnunless 'A'<=x&95<='Z'
  %cycle
    x = rsym
    %exitunless 'A'<=x&95<='Z' %or '0'<=x<='9' %or x=''''
    s = s.tostring(x)
  %repeat
  wbsym(x)
%end

  inputs = rdec

%string(15)%array in(0:inputs); !0 never used

  rname(in(i)) %for i=1,1,inputs
  outputs = rdec

%begin
%string(15)%array out(0:outputs); !0 never used

  rname(out(i)) %for i=1,1,outputs
  statebits = rdec
  inbits = statebits+inputs
  outbits = statebits+outputs
  %if outbits>32 %start
    error("Rom too wide"); %stop
  %finish
  max = 1<<inbits-1

%begin
%integerarray a(0:max);    ! ROM
%integerarray e(0:max);    ! Expression stack

%constinteger maxstack=50
%shortarray opd(1:maxstack); !Operand stack
%shortarray opt(1:maxstack); !Operator stack
%integer pd,pt
%constbytearray op(0:6)='0','1',')','(','+','.','\'
%constinteger zero=0,one=1,right=2,left=3,or=4,and=5,not=6

%routine dostate(%integer state)
%integer low = state<<inputs, high = low+1<<inputs-1; !array bounds

  %routine expr
  %string(15)s
  %integer k
  %constinteger d=1,t=2
  %integer mode = d; !indicates whether expecting operanD or operaTor

    %routine m(%integer x,y); !verify mode=x, then set to y
    %string(63)e
      %unless mode=x %start
        e = "Got "
        %if x=d %start
          e = e.tostring(k)
          error(e." when expecting an operand")
        %else
          error(e.s." when expecting an operator")
        %finish
      %finish
      mode = y
    %end

    %routine dpush(%integer x); !add to reverse polish list
{printstring("Dpush "); %if x>=0 %then printsymbol(op(x)) %else write(x,0); newline
      %returnif x=left
      error("Expression too complex") %if pd>=maxstack
      pd = pd+1; opd(pd) = x
    %end

    %integerfn tpop; !off operator stack
{printstring("Tpop:")
      %result = 0 %if pt<=0
      pt = pt-1; %result = opt(pt+1)
    %end

    %routine tpush(%integer x); !onto operator stack
    %integer y
{printstring("Tpush "); %if x>=0 %then printsymbol(op(x)) %else write(x,0); newline
      %unless x=left %start
        %while pt>0 %and x<=opt(pt) %cycle; !flush high-prec ops
          y = tpop; %exitif x=right %and y=left
          dpush(y)
        %repeat
      %finish
      %returnif x=right
      error("Expression too complex") %if pt>=maxstack
      pt = pt+1; opt(pt) = x
    %end

! Part 1 of EXPR: read expression and build reverse polish list

    pd = 0; pt = 0; tpush(left); !Initial open bracket
    %cycle
      rname(s); k = charno(s,1)
      %if k='(' %start
        m(d,d); tpush(left)
      %elseif k=')'
        m(t,t); tpush(right)
        %if pt=0 %start
          error("Spurious ')'")
          k = rsym %until k=',' %or k=';'; %exit
        %finish
      %elseif k='+'
        m(t,d); tpush(or)
      %elseif k='.'
        m(t,d); tpush(and)
      %elseif k='\'
        m(d,d); tpush(not)
      %elseif k=',' %or k=';'
        wbsym(k) %unless k=','
        m(t,0); tpush(right)
        %unless pt=0 %start
          error("Premature ','")
          %exit
        %finish
        %exit
      %elseif k='0'
        m(d,t); dpush(zero)
      %elseif k='1'
        m(d,t); dpush(one)
      %else
        m(d,t); k = inputs
        %while k>0 %cycle
          %exitif in(k)=s; k = k-1
        %repeat
        %if k=0 %start
           error("Unknown operand ".s)
           k = 0
        %finish
        dpush(-k)
      %finish
    %repeat

! Part 2 of EXPR: evaluate expression just built

  %integer p=0
  %integer stackbit=1,nextbit,i
    %while p<pd %cycle
      nextbit = stackbit>>1
      p = p+1; k = opd(p)
      %if k=one %start
        e(i) = e(i)!stackbit %for i=low,1,high
        stackbit = stackbit<<1
      %elseif k=zero
        e(i) = e(i)&\stackbit %for i=low,1,high
        stackbit = stackbit<<1
      %elseif k=not
        e(i) = e(i)!!nextbit %for i=low,1,high
      %elseif k=and
        stackbit = nextbit; nextbit = nextbit>>1
        %for i=low,1,high %cycle
          e(i) = e(i)&\nextbit %if e(i)&stackbit=0
        %repeat
      %elseif k=or
        stackbit = nextbit; nextbit = nextbit>>1
        %for i=low,1,high %cycle
          e(i) = e(i)!nextbit %if e(i)&stackbit#0
        %repeat
      %else
        k = 1<<(-1-k)
        %for i=low,1,high %cycle
          %if i&k#0 %start
            e(i) = e(i)!stackbit
          %else
            e(i) = e(i)&\stackbit
          %finish
        %repeat
        stackbit = stackbit<<1
      %finish
    %repeat
    error("Bit lost") %unless stackbit=2
    e(i) = e(i)&1 %for i=low,1,high
  %end

  %routine equals
  %integer x = sigsym
    %returnif x='='
    wbsym(x)
    error("Warning: '=' expected")
  %end

  %routine setoutput
  %string(15)o; rname(o)
  %integer k,i
    equals
    k = outputs
    %while k>0 %cycle
      %exitif out(k)=o
      k = k-1
    %repeat
    error("Unknown output ".o) %if k=0
    k = 1<<(k-1)
    expr
    %for i=low,1,high %cycle
      %if e(i)=0 %then a(i) = a(i)&\k %else a(i) = a(i)!k
    %repeat
  %end

  %routine nextstate
  %integer i,old,new,f=0
    new = rdec; equals; expr
    %for i=low,1,high %cycle
      %if e(i)#0 %start
        old = a(i)>>outputs
        error("Ambiguous next state") %and f=1 %if old#state %and f=0
        a(i) = a(i)!!(old!!new)<<outputs
      %finish
    %repeat
  %end

! Main part of DOSTATE

%integer k = sigsym,i
  %unless k=':' %start
    error("Warning: ':' expected")
  %finish
  k = sigsym
  %if k='=' %start; !clone output configuration of another state
    k = rdec
    k = k<<inputs!!low
    %for i=low,1,high %cycle
      a(i) = a(i!!k)
    %repeat
    k = sigsym
    error("',' expected") %unless k=','
  %finishelse wbsym(k)
  k = 1<<outputs-1
  a(i) = a(i)&k!state<<outputs %for i=low,1,high
  %cycle
    k = sigsym; wbsym(k)
    %if '0'<=k<='9' %then nextstate %c
    %elseif 'A'<=k&95<='Z' %then setoutput %c
    %elseexit
  %repeat
  k = rsym
  error("Warning: ';' expected") %unless k=';'
%end{dostate}

! Rest of main program

  a(i) = 0 %and e(i) = 0 %for i=0,1,max
  %cycle
    i = sigsym; %exitif i='.'; wbsym(i)
    dostate(rdec)
  %repeat
  %if errorcount#0 %start
    selectoutput(0); printstring("No output generated"); newline
    %stop
  %finish

! Generate output file(s)

%integer k = rsym

  %if k='4' %start; !Nibble mode
    %for k = 0,1,(outbits-1)>>2 %cycle
      selectoutput(0); printstring("Writing ".file.tostring(k+'0')); newline
      openoutput(2,file.tostring(k+'0')); selectoutput(2)
      printsymbol(a(i)>>(k<<2)&16_0f+16_f0) %for i = 0,1,max
      closeoutput
    %repeat
  %else;            !Byte mode
    %for k = 0,1,(outbits-1)>>3 %cycle
      selectoutput(0); printstring("Writing ".file.tostring(k+'0')); newline
      openoutput(2,file.tostring(k+'0')); selectoutput(2)
      printsymbol(a(i)>>(k<<3)&255) %for i = 0,1,max
      closeoutput
    %repeat
  %finish

! Generate listing of rom contents

  selectoutput(0); printstring("Writing listing file"); newline
  selectoutput(1)
  %for i=0,1,max %cycle
    %if i&7=0 %then phex4(i) %and space
    space; phex(a(i))
    newline %if i&7=7
  %repeat
  newline %unless i&7=7

%end
%end
%endofprogram
