! SILK: Generate silk screen mask for HP 7580 plotter
! from ESDL .BIC file
! RWT December 1985
! NB this is a quick hack for a specific board, not
! necessarily suitable for general purpose use

%begin
%include "inc:util.imp"

!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] coords/pinname signalname}*
!        {^P [1:at,2:on,3:pack,4:subpack,5:delay,6:value,7:size,8:place] parm}*
!        ^G

%recordformatspec pinf
%recordformatspec netf

%recordformat chipf(%record(chipf)%name next,%record(pinf)%name pins,
                    %integer name,type,pack,on,at,
                           ni,no,nio,nt,wide,high,x,y)
%recordformat pinf(%record(pinf)%name nextinchip,nextinnet,
                   %record(chipf)%name chip,%record(netf)%name net,
                   %integer number,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

%own%integer DEVSCALE=2, DEVDENOM=1
%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)mainfile="",hpfile=""
%constinteger xflip=1<<31,yflip=1<<30,dots=1<<29
%integer bools = xflip+dots

%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>)
%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

%routine scale(%integername x,y,%integer flip,negate)
  %returnif chips==nil
  x = (x*devscale*250{or 254})//(devdenom*25)
  y = (y*devscale*250{or 254})//(devdenom*25)
  x = width-x %if flip&xflip#0
  y = height-y %if flip&yflip#0
  x = -x %if negate&xflip#0
  y = -y %if negate&yflip#0
%end

%predicate contains(%string(*)%name s,%integer k)
! Does strng 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(chead_ni); read(chead_no)
    read(chead_nio); read(chead_nt)
    readstring; chead_name = codestring
    readstring; chead_type = 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_number = i>>2; phead_flags = i&3
        readstring
        %if contains(s,':') %start
          phead_x = getnum; verify(':'); phead_y = getnum
          scale(phead_x,phead_y,bools,0)
        %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
        scale(chead_wide,chead_high,0,bools)
      %finishelseif i=pplace %start
        chead_x = getnum; verify(':')
        chead_y = getnum
        scale(chead_x,chead_y,bools,0)
      %finish
      readsym
    %repeat
    verify('^G')
    readsym
  %repeat
  %result == chead
%end

!  HP output rountines
%constinteger RED=1, GREEN=2, BLUE=3, BLACK=4
%own%integer CURX=-32768, CURY=-32768, OFFX=-32768, OFFY=-32768
%own%integer PENDIA=0, PENDOWN=0
%routine COMMA
  printsymbol(',')
%end
%routine PEN(%integer c)
  printstring("SP");write(c,0);newline
%end
%routine MOVE TO(%integer x,y)
  printstring("PU"); write(x-offx,0)
  printsymbol(','); write(y-offy,0); newline
  curx = x;  cury = y;  pendown = 0
%end
%routine DRAW LINE TO(%integer x,y)
  printstring("PD"); write(x-offx,0)
  printsymbol(','); write(y-offy,0); newline
  curx = x;  cury = y;  pendown = 1
%end
%routine DRAW OUTLINE CIRCLE(%integer r)
  printstring("CI"); write(r,0); newline
  pendown = 1
%end

%routine DRAW NUMBER(%integer v)
![nominal numeric output]
  printstring("LB ");write(v,1);printsymbol(3);newline
%end

%routine DRAW TEXT(%string(255)s)
  printstring("LB".s); printsymbol(3); newline
%end

%routine INITIALISE
  offx = width>>1 %if offx = -32768
  offy = height>>1 %if offy = -32768
  %returnunless bools&dots=0
  printstring("AS1");  newline
  printstring("VS4");  newline
%end

%routine END OF PHASE
  move to(curx,cury)
%end

%routine TERMINATE
  select output(1)
  end of phase
  printstring("NR"); newline
  select output(0)
%end

%routine MARK HOLE(%integer r)
%integer x=curx,y=cury
  draw outline circle(r)
  move to(x,y+3*r//2)
  draw line to(x,y-3*r//2)
  move to(x+3*r//2,y)
  draw line to(x-3*r//2,y)
  move to(x,y)
%end

%routine DRAW ALIGNMENT MARKS
%integer border=150*devscale//devdenom,
         r1=50*devscale//devdenom, r2=70*devscale//devdenom
  move to(width+border,0)
  mark hole(r1); draw outline circle(r2)
  draw line to(width+border,height)
  mark hole(r1); draw outline circle(r2)
  move to(width,height+border)
  mark hole(r1); draw outline circle(r2)
  draw line to(0,height+border)
  mark hole(r1); draw outline circle(r2)
  move to(-border,height)
  mark hole(r1); draw outline circle(r2)
  draw line to(-border,0)
  mark hole(r1); draw outline circle(r2)
  move to(0,-border)
  mark hole(r1); draw outline circle(r2)
  draw line to(width,-border)
  mark hole(r1); draw outline circle(r2)
  end of phase
%end

%routine draw boxes
%record(chipf)%name c
%record(pinf)%name p
%integer one,x,y
  s = "1"; one = codestring
  c == chips_next
  %while c##nil %cycle
    s = hashstring(c_type); toupper(s)
    %unless s -> ("HOLE") %start
      moveto(c_x,c_y)
      drawlineto(c_x+c_wide,c_y)
      drawlineto(c_x+c_wide,c_y+c_high)
      drawlineto(c_x,c_y+c_high)
      drawlineto(c_x,c_y)
      %unless bools&dots=0 %start
        p == c_pins
        %while p##nil %cycle
          %exitif p_pin=one
          p == p_nextinchip
        %repeat
        %unless p==nil %start
          moveto(p_x,p_y); drawlineto(p_x,p_y); moveto(p_x,p_y)
        %finish
      %finish
    %finish
    c == c_next
  %repeat
%end

!  Acquire Parameters

  define param("Board description (.BIC) file",mainfile,pamnodefault)
  define param("HP file",hpfile,pamnewgroup)
  define boolean params("Xflip,Yflip,Dots",bools,pamnewgroup)
  processparameters(cliparam)

  mainfile = mainfile.".BIC" %unless contains(mainfile,'.')
  toupper(mainfile)
  %if hpfile="" %start
    hpfile = mainfile
    %if length(hpfile)>4 %start
      %if substring(hpfile,length(hpfile)-3,length(hpfile))=".BIC" %start
        hpfile = substring(hpfile,1,length(hpfile)-4)
      %finish
    %finish
  %finish
  hpfile = hpfile.".SLK" %unless contains(hpfile,'.')
  toupper(hpfile)

!  Initialise, Read BIC file

{}printstring("Reading ".mainfile); newline
  hashtab(sym) == nil %for sym = 0,1,hashmask
  openinput(1,mainfile); selectinput(1)
  readsym; verify('^S'); readsym; verify('3')
  readsym; verify('^U'); readsym; verify('4')
  chips == nil
  chips == readchips
  verify('^J'); read(sym)
  width = |chips_wide|; height = |chips_high|
  scale(width,height,0,0)
  chips_next == reverse(readchips)

!  Produce output file

{}printstring("Writing ".hpfile); newline
  openoutput(1,hpfile); selectoutput(1)
  initialise
  pen(2)
  draw alignment marks
  printstring("SI.425,.75"); newline
  draw boxes
  terminate
  
{}printstring("Done"); newline

%endofprogram
