//ane.bcpl
//new analyzer for sil files

get "sysdefs.d"
get "ana.defs"


//B U I L D C O M P O N E N T B L O C K S F R O M M A C T

let AddComps() be
[
for char = $0 to $9 do BuildTemplate(8,char)
for font = 9 to 13 do
[
for char = 0 to 127 do BuildTemplate(font,char)
]
]

and BuildTemplate(font,char) be
[
// make a template for the macro defined at font,char
let ptr = @(Mact+(font-8)*128+char)
if ptr eq 0 then return

let mfatal = false
let numtnames = 0
let numcpoints = 0
let numpnames = 0

//first, count the number of type name strings in the definition
//and the number of connection points and pin names.
//also check for overlapping connection points

let link = ptr; until (link eq 0)%(link eq -1) do
[
let btype = link>>node.type
if btype eq btString then
[
let stp = link>>node.val //symbol table ptr
let sttype = stp>>strec.type //symbol type
switchon sttype into
[
case stRcomp:
case stComp:
link>>node.used = 1;numtnames = numtnames+1;endcase //mark the node used so that
//it won’t be considered as a pin name by FindBestName

case stPin: numpnames = numpnames+1; endcase

default:
ErrM("*nName in compdef has wrong symbol type:",font,char,lv (stp>>strec.st),link)
]
]

if (btype eq btSchar)&(link>>node.used eq 0) then //non-duplicate connection point
[
numcpoints = numcpoints+1
//look forward through the list and search for duplicate
//cpoints. Mark them used and complain
let x = link>>node.x
let y = link>>node.y
let l2 = @link; until (l2 eq 0)%(l2 eq -1) do
[
let tl = l2;l2 = @l2
if tl>>node.type eq btString then loop
if tl>>node.used then loop //already a duplicate
if (tl>>node.x eq x)&(tl>>node.y eq y) then
[
ErrM("*nCompdef contains overlapping connection points:",font,char,0)
tl>>node.used = 1
]
]
]
link = @link
]

//do some checks on the reasonableness of the definition
if numtnames gr 1 then
[
ErrM("*nCompdef has more than one type name",font,char,0)
mfatal = true
]

if numpnames gr numcpoints then
[
ErrM("*nCompdef has more pin names than connection points:",font,char,0)
mfatal = true
]

if numcpoints eq 0 then
[
ErrM("*nCompdef has no connection points:",font,char,0)
mfatal = true
]

if mfatal then return //don’t try to build template

//the definition seems reasonable- build the component definition template.
//the number of entries in the definition block is the number of connection points

let templ = vec 256 //space for template
Zero(templ,256)
let conpointindex = 1
let link = ptr; until (link eq 0)%(link eq -1) do
[
let tl = link; link = @link
let btype = tl>>node.type
if btype eq btString then //pin name or component name
[
let sttype = (tl>>node.val)>>strec.type
if (sttype eq stComp)%(sttype eq stRcomp) then
[
templ>>compblock.comptype = tl>>node.val
]
loop //go on to next block
]
//the node is a connection point
let pinname = FindBestName(tl,ptr)
test pinname eq 0

ifnot pinname = pinname>>node.val //here we want the stp, not the block pointer

ifso if numcpoints gr numpnames then pinname = DefaultPin(tl)
//don’t default pin if there are enough names
if pinname eq 0 then
[
ErrM("*nCan’t find name for conpoint in compdef:",font,char,0)
mfatal = true
]
MakeCpoint(conpointindex,templ,tl,pinname) //copy into templ

conpointindex = conpointindex+1
]


//check that all strings in the definition have been used
let link = ptr; until (link eq 0)%(link eq -1) do
[
if (link>>node.type eq btString) & (link>>node.used ne 1) then
[
ErrM("*nCan’t assign string in compdef:",font,char,lv((link>>node.val)>>strec.st))
mfatal = true
]
link = @link
]

if mfatal then return //don’t try to use the template

//finish the template
templ>>compblock.boardloc = 0
templ>>compblock.group = 0
templ>>compblock.numcpoints = numcpoints

//the template is now complete. We go through all the
//components, and add an instance of the template every
//place the component appears

let temlength = numcpoints*(size conpoint)/16 + (offset compblock.group)/16 +1 //length of template

let mindex = (font-8)*128 + char
let nlink = Comps; until nlink eq 0 do
[
if nlink>>comp.mindex eq mindex then AddComp(nlink,templ,temlength)
nlink = @nlink
]
]


and ErrM(st1,font,char,st2,junk) be
[
let v = vec 128; v!0 = 0
AppendS(st1,v)
AppendS(" Font: ",v)
AppendN(font-4,v)
AppendS(" Char: ",v)
AppendC(char,v)
AppendC($*s,v)
if st2 ne 0 then AppendS(st2,v)
Err(v)
]

and FindBestName(pnode,searchlist) =valof
[
//pnode points to a pin node (in searchlist)
//find the closest unused string in searchlist
//if there is none, or if there is a pin closer
//to the candidate than pnode, return 0, otherwise
//mark the name used and return it

let px = pnode>>node.x
let py = pnode>>node.y
let cand = 0 //pointer to candidate node
let dcand = 10000 //distance to candidate node
let ptr = searchlist ; until (ptr eq 0)%(ptr eq -1) do
[
let tl = ptr; ptr = ptr>>node.link
if tl>>node.used eq 1 then loop
if tl>>node.type ne btString then loop
let dist = Ndist(px,py,tl)
if dist ls dcand then
[
cand = tl
dcand = dist
]
]

if cand eq 0 then resultis 0

//check that there is no pin closer to the candidate string than
//dcand

px = cand>>node.x
py = cand>>node.y
ptr=searchlist; until (ptr eq 0)%(ptr eq -1) do
[
let tl = ptr; ptr = ptr>>node.link
if tl>>node.type ne btSchar then loop //not a connection point
if Ndist(px,py,tl) ls dcand then resultis 0 //there is a closer connection point
]
cand>>node.used = 1 //mark the candidate string used
resultis cand //and return it
]

and Ndist(x,y,node) = valof //manhattan distance
[
let nx = node>>node.x
let ny = node>>node.y
resultis (nx gr x?nx-x,x-nx)+(ny gr y?ny-y,y-ny)
]

and DefaultPin(nodeptr) = valof
[
resultis selecton nodeptr>>node.val into
[
case chLcon: Instp
case chRcon: Outstp
default: 0
]
]

and MakeCpoint(cpi,templ,tl,pname) be
[
templ>>compblock.x↑cpi = tl>>node.x
templ>>compblock.cpt↑cpi = selecton tl>>node.val into
[
case chTcon: cptTop
case chBcon: cptBot
case chLcon: cptLeft
case chRcon: cptRight
]
templ>>compblock.y↑cpi = tl>>node.y
templ>>compblock.pinname↑cpi = pname
//all other fields will be zero, since templ was cleared initially
]


and AddComp(place,templ,temlength) be
[
//add an instance of the templ to the component at place
CheckFit(temlength) //make sure the template will fit

MoveBlock(NewItem,templ,temlength) //add the template
let p = NewItem; NewItem=NewItem+temlength

place>>comp.conpoints = p //point to the compblock

//must fix up the connection point cordinates
let x = place>>comp.xmin
let y = place>>comp.ymin

for i = 1 to p>>compblock.numcpoints do
[
p>>compblock.x↑i = (p>>compblock.x↑i) + x
p>>compblock.y↑i = (p>>compblock.y↑i)+y
]
]