! TOBEPI:  Convert a design from ESDL to BEPI format, by taking an ESDL
! ".BIC" file (that has preferably been through the interactive route
! vetting program VZAP), and generating from that the three data files
! (".CPL", ".NET", ".PAG") required by BEPI programs ONE to FIVE or the
! single program SWRAP which replaces these.
! RWT November 1985 revised August 1986

%begin
%include "inc:util.imp"

!Reminder of ESDL data format:

!ESDL -> ^S [0:eic,1:fic,2:cic,3:bic] UNIT*
!UNIT -> ^U [1:spec,2:unit,3:chip,4:board,5:pack] HEAD UNIT*
!        ^J nsubs HEAD* {^N{^A netname nfans {subno tno}*}+}*
!        ^E
!HEAD -> ^H0 nin nout nio nt label name
!        {^T tno<<2+[0:dup,1:out,2:in,3:inout] pinname signalname}*
!        {^P [1:at,2:on,3:pack,4:subpack,5:delay,6:value,7:size,8:place] parm}*
!        ^G

!In particular for .BIC files we have:

! ^S3^U4 HEAD(label=circuitname,name=boardname,p7(size)) ^Jn
! {HEAD(label=uselessPACKnumber,name=chipname,p1(ATslotname),
!       p2(ONpackname),p7(size),p8(place(x:y)))}n
! {^N{^A ...}+}* ^E
! pinnames in ^T lists take the form  x:y/pinname

%recordformatspec pinf
%recordformatspec netf

%recordformat chipf(%record(chipf)%name next,%record(pinf)%name pins,
                    %integer label,name,on,at,wide,high,x,y)
%recordformat pinf(%record(pinf)%name nextinchip,nextinnet,
                   %record(chipf)%name chip,%record(netf)%name net,
                   %integer tno,flags,pin,signal,index,x,y)
%recordformat netf(%record(netf)%name nextnet,nextsubnet,net,
                   %record(pinf)%name pins, %integer signal,index)

%constinteger pat=1,pon=2,psize=7,pplace=8

%integer sym;          !Current I-code symbol
%string(255)s;         !Current I-code string
%integer si;           !Signal name index, normally -1, but for
                       !a signal A<17>, si would be 17 and s A

%constinteger hashmask=63
%recordformat hashf(%record(hashf)%name next,%string(255)s)
%record(hashf)%namearray hashtab(0:hashmask)

%record(netf)%name nets;   !List of nets in main circuit
%record(chipf)%name chips; !List of chips in main circuit, of which
                           !the first is the main circuit itself.

%integer width,height
%string(255)file=""
%constinteger xflip=1<<31,yflip=1<<30,xyswap=1<<29
%integer bools = xflip,xoffset=0,yoffset=0

%integerfn codestring
! Returns a code value which uniquely identifies
! global string S, which is entered in a dictionary.
! This consists of a primary hash table with simple
! unsorted linked lists hanging off each entry.
! The actual number returned is the address of the string.
%record(hashf)%name h
%integer value=0,i
  %result = 0 %if length(s)=0
  %for i = 1,1,length(s) %cycle
    value = value<<1+(charno(s,i)!32)
  %repeat
  value = value&hashmask
  h == hashtab(value)
  %cycle
    %if h==nil %start; !Tag new entry onto (end of) list
      h == record(heapget(5+length(s)))
      h_s = s; h_next == hashtab(value)
      hashtab(value) == h
      %result = addr(h_s)
    %finish
    %result = addr(h_s) %if h_s=s
    h == h_next
  %repeat
%end

%string(255)%fn hashstring(%integer tag)
! Re-constitutes a hash-coded string
  %result = "" %if tag=0
  %result = string(tag)
%end

%string(255)%fn indexstring(%integer i)
! Re-constitutes index part of a signal name
%string(255)s
  %result = "" %if i<0
  s = itos(i,0)
  %result = "<".s.">"
%end

%record(*)%map reverse(%record(*)%name list)
! Reverses an arbitrary singly linked list, provided
! the first field in the record is used for linking.
%recordformat f(%record(f)%name next)
%record(f)%name head,tail,temp
  head == list; tail == nil; %result == nil %if list==nil
  %cycle
    temp==head; head==temp_next; temp_next==tail; tail==temp
  %repeatuntil head==nil
  %result==tail
%end

!  ESDL I-code input procedures

%routine readsym
! Read significant character and store in global SYM.
! NB newlines are not significant but spaces are.
  readsymbol(sym) %until sym#nl
  %returnunless sym='^'
  readsymbol(sym); sym=sym+'^'<<8
%end

%routine verify(%integer want)
! Ensure global SYM corresponds to WANT
  %routine p(%integer x)
    printsymbol(x>>8) %unless x>>8=0
    printsymbol(x)
  %end
  %returnif sym=want
  printstring("Got "); p(sym)
  printstring(" when expecting "); p(want)
  newline; %stop
%end

%routine readstring
! Read string and store in global S and SI (if of form signal<num>)
%owninteger dotted=0
%integer indexed=0
%integer len,i
  si = -1
  read(len); readsym; verify(':')
  length(s) = len
  %for i=1,1,len %cycle
    readsym
    indexed = i %if indexed=0 %and sym='<'
    charno(s,i) = sym
  %repeat
  %if indexed#0 %start
    length(s) = indexed-1
    si = 0
    %cycle
      indexed = indexed+1; i = charno(s,indexed)-'0'
      %exitunless 0<=i<=9
      si = si*10+i
    %repeat
  %finish
%end

%integerfn getnum
! Extract a possibly signed decimal number off the front of global
! string S, which is shortened by the number plus its terminator.
%integer n=0,sign=0,pos=1
  %while pos<=length(s) %cycle
    sym = charno(s,pos); pos = pos+1
    %if sym='-' %start
      sign = 1
    %finishelseif '0'<=sym<='9' %start
      n = n*10-'0'+sym
    %finishelseexit
  %repeat
  s = substring(s,pos,length(s))
  %result = n %if sign=0
  %result=-n
%end

%predicate contains(%string(*)%name s,%integer k)
! Does string S contain character K?
%integer i
  %for i = 1,1,length(s) %cycle
    %trueif charno(s,i)=k
  %repeat
  %false
%end

%record(chipf)%map readchips
! Read {^H0 ... ^G}*
%integer i
%record(chipf)%name chead,ctail
%record(pinf)%name phead,ptail

  ctail == nil
  readsym
  %while sym='^H' %cycle
    readsym; verify('0')
    chead == new(chead); chead = 0
    chead_next == ctail; ctail == chead
    read(i); read(i); read(i); read(i)  {ni,no,nio,nt}
    readstring; chead_label = codestring
    readstring; chead_name = codestring
    ptail == nil; readsym
    %while sym='^T' %cycle
      read(i)
      %if i&3#0 %start; !In,Out,or InOut
        phead == new(phead); phead = 0; phead_chip == chead
        phead_nextinchip == ptail; ptail == phead
        phead_tno = i>>2; phead_flags = i&3
        readstring
        %if contains(s,':') %start
          phead_x = getnum; verify(':'); phead_y = getnum
        %finish
        phead_pin = codestring
        readstring; phead_signal = codestring; phead_index = si
      %else; !Dummy pin
        readstring; readstring
      %finish
      readsym
    %repeat
    chead_pins == reverse(phead)
    %while sym='^P' %cycle
      read(i); readstring
      %if i=pon %start
        chead_on = codestring
      %finishelseif i=pat %start
        chead_at = codestring
      %finishelseif i=psize %start
        chead_wide = getnum; verify(':')
        chead_high = getnum
      %finishelseif i=pplace %start
        chead_x = getnum; verify(':')
        chead_y = getnum
      %finish
      readsym
    %repeat
    verify('^G')
    readsym
  %repeat
  %result == chead
%end

%record(netf)%map readnets
! Read {^N {subnet}*}*
%record(netf)%name head,tail

  %record(netf)%map readsubnets
  ! Read {^A subnetname pairs {chip pin}*}*
  %record(netf)%name head,tail
  %record(pinf)%name p,term
  %record(chipf)%name sub
  %integer nfans,subno,tno
    tail == nil
    %while sym='^A' %cycle
      head == new(head); head = 0
      head_nextsubnet == tail; tail == head
      readstring; head_signal = codestring; head_index = si
      read(nfans); p == nil
      %while nfans>0 %cycle
        nfans = nfans-1; read(subno); read(tno)
        sub == chips
! Scan component list (by position)
        sub == sub_next %and subno = subno-1 %while subno>0
        term == sub_pins
! Scan component's pin list (by pin number value)
        term == term_nextinchip %while term##nil %and term_tno#tno
        %if term==nil %start
          printstring("Terminal "); write(tno,0)
          printstring(" not found in component ")
          printstring(hashstring(sub_name))
          printstring(" at "); printstring(hashstring(sub_at)); newline
        %elseif term_flags&4=0; !All is well: not been here before
          term_flags = term_flags!4; term_net == head
          term_nextinnet == p; p == term
        %finishelseunless term_net==head %start; !Confusion
          printstring("Terminal"); write(term_tno,1)
          printstring(", pin ".hashstring(term_pin))
          space; printstring(hashstring(term_signal))
          printstring(" of ".hashstring(term_chip_label)); space
          printstring(hashstring(term_chip_name)." at ")
          printstring(hashstring(term_chip_at)); newline
          printstring("is already connected to ")
          printstring(hashstring(term_net_signal))
          printstring(indexstring(term_net_index))
          printstring(", cannot also connect to ".s)
          printstring(indexstring(si)); newline
        %finish
      %repeat
      head_pins == p
      readsym
    %repeat
    head == tail
    %cycle
      tail_net == head; tail == tail_nextsubnet
    %repeatuntil tail==nil
    %result == head
  %end

  tail == nil
  %while sym='^N' %cycle
    readsym; head == readsubnets
    head_nextnet == tail; tail == head
  %repeat
  %result == head
%end

%routine xy(%integer x,y,p,q)
%integer t
  x = width-x %if bools&xflip#0
  y = height-y %if bools&yflip#0
  %if bools&xyswap#0 %start
    t = x; x = y; y = t
  %finish
  write(x-xoffset,p); printsymbol('0')
  write(y-yoffset,q); printsymbol('0')
%end

%routine warn(%integer d,%string(19)type)
  %returnif d<=0
  printstring("Warning: "); printstring(type)
  printstring(" names exceed recommended maximum length by")
  write(d,1); printstring(" characters"); newline
%end

%routine produce cpl file
%record(chipf)%name c
%integer i,maxcn=0,maxpn=0
%string(255)chipname

  %routine putcpl
  %record(pinf)%name p
  %string(255)pinname
    maxcn = length(chipname) %if length(chipname)>maxcn
    p == c_pins
    %while p##nil %cycle
      pinname = hashstring(p_pin)
      maxpn = length(pinname) %if length(pinname)>maxpn
      printstring(chipname); spaces(10-length(chipname)-length(pinname))
      space; printstring(pinname); xy(p_x,p_y,6,6); newline
      p == p_nextinchip
    %repeat
  %end

  printstring("Writing .CPL file")
  openoutput(1,file.".CPL"); selectoutput(1)
  c == chips; chipname = "EDGE"; putcpl
  c == chips_next
  %while c##nil %cycle
    chipname = hashstring(c_at)
    putcpl
    c == c_next
  %repeat
  closeoutput; selectoutput(0); newline
  warn(maxcn-5,"component")
  warn(maxpn-5,"pin")
%end

%routine produce net files
%record(netf)%name m,n
%record(pinf)%name p
%string(255)signame,pinname,chipname
%integer maxsn=0,i

  printstring("Writing .NET and .PAG files")
  openoutput(2,file.".PAG")
  openoutput(1,file.".NET"); selectoutput(1)
  m == nets
  %while m##nil %cycle
    n == m; m == m_nextnet
    signame = hashstring(n_signal)
    %if n_index>=0 %start
      signame = signame."<".itos(n_index,0).">"
    %finish
    maxsn = length(signame) %if length(signame)>maxsn
    %if charno(signame,1)='.' %then selectoutput(2) %else selectoutput(1)
    %cycle
      p == n_pins; i = 0
      %while p##nil %cycle
        printstring(signame)
        pinname = hashstring(p_pin)
        %if p_chip==chips %then chipname = "EDGE" %elsestart
          chipname = hashstring(p_chip_at)
        %finish
        space; spaces(14-length(signame)-length(chipname))
        printstring(chipname."-".pinname)
        newline
        p == p_nextinnet
      %repeat
      n == n_nextsubnet
    %repeatuntil n==nil
  %repeat
  selectoutput(2); closeoutput
  selectoutput(1); closeoutput
  selectoutput(0); newline
  warn(maxsn-9,"signal")
%end

!  Acquire Parameters

  define param("Filename",file,pamnodefault)
  define int param("XOffset",xoffset,pamnewgroup)
  define int param("YOffset",yoffset,0)
  define boolean params("Xflip,Yflip,XYswap",bools,pamnewgroup)
  processparameters(cliparam)

  toupper(file)

!  Initialise, Read BIC file

  hashtab(sym) == nil %for sym = 0,1,hashmask
  printstring("Reading .BIC file")
  openinput(1,file.".BIC"); selectinput(1)
  readsym; verify('^S'); readsym; verify('3')
  readsym; verify('^U'); readsym; verify('4')
  chips == readchips
  width = chips_wide; height = chips_high
  verify('^J'); read(sym)
  chips_next == reverse(readchips)
  nets == readnets
  verify('^E')
  closeinput; newline

!  Produce output files

  produce cpl file
  produce net files

%endofprogram
