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

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

// G R I N D U P T H E I N T E R N A L S T R U C T U R E S
let DoSchars() be
[
//go through cpins, epins, grounds and pseudonet chars
//and try to assign them to line ends. Also assign numbers
//to cpins and epins

let rptr = Schars; until rptr eq 0 do
[
let ptr =rptr; rptr = @rptr
let x = ptr>>node.x
let y = ptr>>node.y
switchon ptr>>node.val into
[
case chCpin:
case chEpin:
[
let line = FindLE(Hlines,12,3,x,y) //find line end
//for this pin. cp↑1 must be 0

let pnum = FindNode(Numbers,4,4,x,y) //find a
//pinnumber for the pin

if line eq 0 do
[
Errxy(x,y,"Can’t find line for pin")
if pnum ne 0 then pnum>>node.used = 1
//remove the number from further consideration
loop
]

if pnum eq 0 do
[
Errxy(x,y,"Can’t find number for pin")
loop
]

//mark the pin number used, and assign it to the line
line>>lend.cp↑1 = ptr //points to the schar (so we know cpins from epins)
line>>lend.cp↑2 = -1 //remove from further connection consideration
ptr>>node.used = 1
ptr>>node.assoc = pnum //points to the number
pnum>>node.used = 1
pnum>>node.assoc = line //points to the line

loop
]

case chGnd: //grounds connect to vertical lines only
[
let line = FindLE(Vlines,2,6,x,y)
if line eq 0 then
[
Errxy(x,y,"Can’t find line for ground")
loop
]

//grounds should be connected only to the ymax end
//of vertical lines
let ty = line>>lend.y
let uy = (line>>lend.cp↑0)>>lend.y
if ty ls uy then
[
Errxy(x,y,"Ground appears connected to top of line?")
loop
]

ptr>>node.assoc = line
ptr>>node.used = 1
line>>lend.cp↑1 = ptr
line>>lend.cp↑2 = -1 //nothing else may be legally connected
line>>lend.hassig = 1 //Really the signal name GND
loop
]

case chPseud: //these may connect to either end of
//vlines or hlines
[
let line = FindLE(Hlines,4,4,x,y)//try hlines
if line eq 0 then line = FindLE(Vlines,4,4,x,y)
if line eq 0 then
[
Errxy(x,y,"Can’t find line for pseudonet point")
loop
]
ptr>>node.assoc = line
ptr>>node.used = 1
line>>lend.cp↑1 = ptr
line>>lend.cp↑2 = -1
loop
]
]
]
]


and FindLE(list,xdist,ydist,x,y) = valof
[
let dist = 10000
let cand = 0
until list eq 0 do
[
let ptr = list
list = @list
if ptr>>lend.cp↑1 ne 0 then loop //line end already used
let xl = ptr>>lend.x
let delx = (xl gr x?xl-x,x-xl)
if delx gr xdist then loop
let yl = ptr>>lend.y
let dely = (yl gr y?yl-y,y-yl)
if dely gr ydist then loop
if (delx+dely) ls dist then
[
cand = ptr
dist = delx+dely
]
]
resultis cand
]


and FindNode(list,xdist,ydist,x,y) = valof
[
let dist = 10000
let cand = 0
until list eq 0 do
[
let ptr = list
list = @list
if ptr>>node.used ne 0 then loop //node already used
let xl = ptr>>node.x
let delx = (xl gr x?xl-x,x-xl)
if delx gr xdist then loop
let yl = ptr>>node.y
let dely = (yl gr y?yl-y,y-yl)
if dely gr ydist then loop
if (delx+dely) ls dist then
[
cand = ptr
dist = delx+dely
]
]
resultis cand
]

and DoSignames() be
[
//for each signal name, find the closest line which is below
//the name, and which has xmin<xname<xmax.
//if there is another name closer to the line than the candidate,
//complain. If not, associate the signal name with the line, and
//mark it used.

let ptr = Strings
until ptr eq 0 do
[
let rptr = ptr
ptr = @ptr
if ((rptr>>node.val)>>strec.type)ne stSig then loop //not a signame

let x = rptr>>node.x
let y = rptr>>node.y

let line = FindLineForSig(x,y)
if line eq 0 then
[
Errxy(x,y,"Can’t find line for signal name")
loop
]

let ly = line>>lend.y
let lx2 = line>>lend.x //this is xmax, because of the way line
//ends were put into the list by AddLE

let lx1 = (@line)>>lend.x //x of other end = xmin

if NoSigForLine(rptr,ly,lx1,lx2) then //returns true
//if the closest signal name to the line is NOT rptr

[
Errxy(x,y,"Can’t find line for signal name")
loop
]

line>>lend.hassig = 1
rptr>>node.used = 1
rptr>>node.assoc = line
]
]

and FindLineForSig(x,y) = valof
[
let dist = 30 //don’t associate a line with a signame if more than 30
//screen units away
let cand = 0
let list = Hlines
until list eq 0 do
[
let ptr = list; list = @(@list) //line endpoints are paired (2 ends)

let yl = ptr>>lend.y
if yl le y then loop //line is above signal name
let xmax = ptr>>lend.x
let xmin = (@ptr)>>lend.x
if (x ge xmax)%(x le xmin) then loop
let dely = yl-y
if dely gr dist then loop
cand = ptr //we have a new candidate
dist = dely
]
resultis cand
]

and NoSigForLine(rptr,y,xmin,xmax) = valof
[
let cand = 0
let dist = 10000
let list = Strings
until list eq 0 do
[
let ptr = list
list = @list
if ptr>>node.used ne 0 then loop //node used already
if ((ptr>>node.val)>>strec.type) ne stSig then loop //not a signame

let xs = ptr>>node.x
if (xs ge xmax)%(xs le xmin) then loop //string out in x

let ys = ptr>>node.y
if ys gr y then loop //string is below line
let dely = y-ys
if dely gr dist then loop //not close enough
dist = dely
cand = ptr
]

resultis (cand ne rptr)
]

and DoComponents() be
[
//first, we count all the remaining numbers (which should
//only be IC pinnumbers), so that later we can decide whether
//to try to hook them to IC pins

let numnums = 0
let ptr = Numbers; until ptr eq 0 do
[
if ptr>>node.used eq 0 then numnums = numnums +1
ptr = @ptr
]

//try to assign typenames and locgroups to components
//also try to assign conpoints to line ends, and any
//remaining pinnumbers to conpoints. The search rule
//for type names and locgroups is that the thing must
//be in a bounding box which extends to the right
//of the component until it runs into another component
//with (about) the same y coordinates.

let ptr = Comps
until ptr eq 0 do //for all components
[
let list = ptr; ptr = @ptr
let xmin = list>>comp.xmin
let ymin = list>>comp.ymin
let xmax = list>>comp.xmax
let ymax = list>>comp.ymax

let bbxmax = GetBB(xmax,ymin,ymax) //get bounding box
let compb = list>>comp.conpoints //pointer to compblock
if compb eq 0 then //whoops!
[
Errxy(xmin,ymin,"Component has no definition")
loop
]

if compb>>compblock.comptype eq 0 then //type not contained in definition

[
let nameptr = GetTypeName(xmin,bbxmax,ymin,ymax)
if nameptr ne 0 then
[
nameptr>>node.used=1
compb>>compblock.comptype = nameptr>>node.val //symbol table pointer for name
]
]
//getting a name may not succeed, since we require that only
//one group in a component have a type name string. If
//GetTypeName finds more than one typename in the box, it complains

//BUT, we MUST find a locgroup for each component...
let bl = GetBoardLoc(xmin,bbxmax,ymin,ymax)

if bl ne 0 then //errors have been reported by GetBoardLoc
[
compb>>compblock.boardloc = bl>>node.bloc
compb>>compblock.group = bl>>node.group
bl>>node.used = 1
]

let numc = compb>>compblock.numcpoints

//try to connect blobs to the connection points of this component
let blobptr = Schars; until blobptr eq 0 do
[
let bptr =blobptr;blobptr = @blobptr
if bptr>>node.val ne chBlob then loop
if bptr>>node.used ne 0 then loop
let blx = bptr>>node.x
let bly = bptr>>node.y
for i = 1 to numc do
[
let maxx = selecton compb>>compblock.cpt↑i into
[
case cptTop: 2
case cptLeft: 8
case cptBot: 2
case cptRight: 8
]
let maxy = selecton compb>>compblock.cpt↑i into
[
case cptTop: 8
case cptLeft: 2
case cptBot: 8
case cptRight: 2
]

let px = compb>>compblock.x↑i
let delx = (px gr blx)?px-blx,blx-px
let py = compb>>compblock.y↑i
if delx gr maxx then loop
let dely = (py gr bly)?py-bly,bly-py
if dely gr maxy then loop
//we have a blob for this pin
bptr>>node.used = 1
compb>>compblock.blob↑i = 1
bptr>>node.assoc = list //blob associated with this component
break //since the blob is used, there is no point in
//looking at more conpoints
]
]


//now go through Vlines. Check line ends with cp↑2 free
//against the connection points in this component. When
//done, all top and bottom conpoints (if any) should have
//been used

let searchvcount = 0 //count of conpoints which need vlines
for i = 1 to numc do
[
if compb>>compblock.blob↑i ne 0 then loop
let cptype = compb>>compblock.cpt↑i
if (cptype eq cptTop)%(cptype eq cptBot) then
searchvcount = searchvcount+1
]

let xptr = Vlines
until searchvcount eq 0 do
[
if xptr eq 0 then break //no more lines
let tptr = xptr; xptr = @xptr

//the line end must have cp↑1 free to be useful
if tptr>>lend.cp↑1 ne 0 then loop

let lx = tptr>>lend.x //line coordinates
let ly = tptr>>lend.y
let oy = (tptr>>lend.cp↑0)>>lend.y //y coordinate of other end
//check this line end against the connection points
for i = 1 to numc do
[
if compb>>compblock.conn↑i ne 0 then loop
if compb>>compblock.blob↑i ne 0 then loop//no need looking if
//there is a blob on this pin
let cpt = compb>>compblock.cpt↑i
switchon cpt into //check that top conpoints connect to to
//ymax end of line, bottoms to ymin end
[
case cptTop: if oy gr ly then loop; endcase

case cptBot: if oy le ly then loop; endcase

default: loop //left or right
]

let cx = compb>>compblock.x↑i
let delx = (cx gr lx?cx-lx,lx-cx)
if delx gr 3 then loop //should be very close in x
let cy = compb>>compblock.y↑i
let dely = (cy gr ly?cy-ly,ly-cy)
if dely gr 6 then loop //can be farther away in y

//connect the line end to this conpoint
compb>>compblock.conn↑i = tptr
tptr>>lend.cp↑1 = list //pointer to this component
tptr>>lend.cp↑2 =-1
//make the rest of the line unavailable
searchvcount = searchvcount-1
break //out of the for loop
]
]
if searchvcount ne 0 then
Errxy(xmin,ymin,"Can’t assign all t/b conpoints to lines")

//now go through Hlines. Check line ends with cp↑2 free
//against the connection points in this component. When
//done, all left and right conpoints (if any) should have
//been used

let searchhcount = 0 //count of conpoints which need hlines
for i = 1 to numc do
[
if compb>>compblock.blob↑i ne 0 then loop
let cptype = compb>>compblock.cpt↑i
if (cptype eq cptLeft)%(cptype eq cptRight) then
searchhcount = searchhcount+1
]

let xptr = Hlines
until searchhcount eq 0 do
[
if xptr eq 0 then break //no more lines
let tptr = xptr; xptr = @xptr

//the line end must have cp↑1 free to be useful
if tptr>>lend.cp↑1 ne 0 then loop

let lx = tptr>>lend.x //line coordinates
let ly = tptr>>lend.y
let ox = (tptr>>lend.cp↑0)>>lend.x //x coordinate of other end
//check this line end against the connection points
for i = 1 to numc do
[
if compb>>compblock.conn↑i ne 0 then loop
if compb>>compblock.blob↑i ne 0 then loop//no need looking if
//there is a blob on this pin
let cpt = compb>>compblock.cpt↑i
switchon cpt into //check that left conpoints connect to to
//xmax end of line, rights to xmin end
[
case cptLeft: if ox gr lx then loop; endcase

case cptRight: if ox le lx then loop; endcase

default: loop //top or bottom
]

let cx = compb>>compblock.x↑i
let delx = (cx gr lx?cx-lx,lx-cx)
if delx gr 6 then loop //can be far in x
let cy = compb>>compblock.y↑i
let dely = (cy gr ly?cy-ly,ly-cy)
if dely gr 3 then loop //but very close in y

//connect the line end to this conpoint
compb>>compblock.conn↑i = tptr
tptr>>lend.cp↑1 = list //pointer to this component
tptr>>lend.cp↑2 =-1
//make the rest of the line unavailable
searchhcount = searchhcount-1
break //out of the for loop
]
]
if searchhcount ne 0 then
Errxy(xmin,ymin,"Can’t assign all l/r conpoints to lines")

//if any numbers remain, try to interpret then as this component’s
//pin numbers
let xptr = Numbers
let pinstofind = numc //no pins have been found yet
until (numnums eq 0)%(pinstofind eq 0) do
[
if xptr eq 0 then break //out of numbers to check
let tptr = xptr
xptr = @xptr
if tptr>>node.used ne 0 then loop //node used
let nx = tptr>>node.x
let ny = tptr>>node.y
for i = 1 to numc do //for each connection point in
//the current component
[
if compb>>compblock.pin↑i ne 0 then loop //pin found
let px = compb>>compblock.x↑i
let py = compb>>compblock.y↑i
switchon compb>>compblock.cpt↑i into
//check that the number is in the right
//place relative to the conpoint
[
case cptLeft: //number must be above and left
case cptTop:
if (nx ge px)%(ny ge py)%((ny+6) le py)% ((nx+12) le px) then loop
endcase

case cptBot: //num must be below and left
if (nx ge px)%(ny le py)%((ny-6)ge py)% ((nx+12)le px) then loop

endcase

case cptRight: //above and right
if (nx ls px)%(ny ge py)%((ny+6) le py)% ((nx-12) ge px) then loop
endcase

default: loop
]

//assign the number to the pin
numnums = numnums-1
pinstofind = pinstofind-1
compb>>compblock.pin↑i = tptr>>node.val //the real number
compb>>compblock.defd↑i = 1 //so it won’t be output to .pn file
tptr>>node.used = 1
tptr>>node.assoc = list //associated with this component
break //out of the forloop
]
]


]


]


and GetBB(xmax,ymin,ymax) = valof //get bounding box
[
let tx = 10000
let ptr = Comps
until ptr eq 0 do
[
let tptr = ptr; ptr = @ptr
let xm = tptr>>comp.xmin

if xm ls xmax then loop //component is to left of ours
if tptr>>comp.ymin gr ymax then loop //comp is below ours
if tptr>>comp.ymax ls ymin then loop //comp is above ours
if xm ls tx then tx = xm
]

resultis tx
]

and GetTypeName(xmin,bbxmax,ymin,ymax) = valof
[
//look through all the type name strings. try to
//find exactly one which is within the bounding box
//if there is more than one, complain and return 0
let cand = 0
let ptr = Strings
until ptr eq 0 do
[
let tptr = ptr; ptr = @ptr
if tptr>>node.used ne 0 then loop //string used
if (tptr>>node.val)>>strec.type ne stComp then loop
let x = tptr>>node.x
if (x ls xmin)%(x gr bbxmax) then loop
let y = tptr>>node.y
if (y ls ymin)%(y gr ymax) then loop
test cand eq 0
ifso cand = tptr
ifnot
[
Errxy(xmin,ymin,"Multiple type names possible for component")
resultis 0
]
]
resultis cand
]



and GetBoardLoc(xmin,bbxmax,ymin,ymax) = valof
[
//look through all the locgroups try to
//find exactly one which is within the bounding box
//if there is more than one, complain and return 0
let cand = 0
let ptr = Locgroups
until ptr eq 0 do
[
let tptr = ptr; ptr = @ptr
if tptr>>node.used ne 0 then loop //string used
let x = tptr>>node.x
if (x ls xmin)%(x gr bbxmax) then loop
let y = tptr>>node.y
if (y ls ymin)%(y gr ymax) then loop
test cand eq 0
ifso cand = tptr
ifnot
[
Errxy(xmin,ymin,"Multiple board locations possible for component")
resultis 0
]
]

if cand eq 0 then
Errxy(xmin,ymin,"Can’t find bloc/group for component")
resultis cand
]