// Micro symbol table routines
// last edited December 26, 1979  8:42 AM
// Copyright Xerox Corporation 1979

	get "micdecl.d"

external
[		// O.S.
	SetBlock; Zero
	Puts	// for tracing
		// defined here for MICSLOW
	@endhc; @hlast; @hprev; @sw0
]

structure
  [	lhw bit 8		// Left half of word
	rhw bit 8		// Right half of word
   ]
structure
  [	blank bit 15
	odd bit 1		// Least significant bit
   ]


compileif slink ne 0 then
	[ SlinkNe0 = 0 ]	// *** Lookup assumes slink=0 ***


static [
// Storage for scinit, scnext, and nsnext
	@scep		// Scanner current entry pointer
	@schp		// Scanner hash chain pointer
	@scwn		// Scanner word number
	@scmask		// Scanner word mask
	@scval		// Scanner value for which to scan
	@sche		// Scanner hash chain end pointer
	@scstop		// Scanner chain stop value
	@nshp		// New symbol hash pointer
	@nshe		// New symbol hash end pointer
	@nsbp		// New symbol current block pointer
// Storage for lookup, putin, and newdef
	@svec	// dummy vector for end of hash chains
	@endhc	// ditto -sname
	@symbase	// real address of hash vector
	@hsize	// Hash table size
	@hlast
	@hprev
	@saddr	// address of unpacked symbol
	@scc	// length of unpacked symbol
	@sw0	// first word of packed name
	@nsbase	// real address of new symbol hash vector
	@nsmask	// new symbol hash mask
// Statistics
// *** Assumes statics are allocated in order
	zenter; zenter1	// # of putins
  ]


let lenname(ep) = valof
// Returns length of name of symbol table entry.  Names are
//	delimited by a zero in the left or right halfword.
  [	let ptr = ep + sname
	while ptr>>rhw ne 0 do
		ptr = ptr + 1
	resultis ((ptr-ep) lshift 1) + (ptr>>lhw ne 0 ? -1, -2)
   ]

and initsym(siz, nsiz; numargs na) be
// Initialize the symbol table
  [	if na ne 0 then	// Allocating
	  [	symtab = alloc(siz) - fstop
		symvec = alloc(1) - fstop
		nstab = alloc(nsiz) - fstop
	   ]
	symbase = symtab + fstop
	svec = symvec + fstop
	endhc = svec-sname
	hsize = bsize(symbase)
	if na ne 0 then
	   SetBlock(symbase, endhc, hsize)
	nsbase = nstab + fstop
	nsmask = bsize(nsbase)-1
	Zero(nsbase, nsmask+1)
   ]

and dumpsym() be
// Relativize the symbol table for dumping
[	scinit(0, 0, 0)
	until schp eq sche do
	[ let ep = schp
	  until ep eq scstop do
	  [ let ep1 = ep!slink
	    ep!slink = ep1 - fstop
	    ep = ep1
	  ]
	  schp = schp+1
	]
]

and recsym() be
// De-relativize the symbol table after dumping or reloading
[	scinit(0, 0, 0)
	until schp eq sche do
	[ let ep = schp
	  until ep eq scstop do
	  [ let ep1 = ep!slink + fstop
	    ep!slink = ep1
	    ep = ep1
	  ]
	  schp = schp+1
	]
]

and scinit(wn,mask,val) be
// Initialize for symbol table scan.  All entries in the symbol
//	table will have the word displaced from their ep
//	by wn words anded with mask.  If the resulting
//	value is val, they will be reported by scnext().
  [	scwn, scmask, scval = wn, mask, val
	schp = symbase-slink
	sche = schp+hsize
	scstop = endhc
	scep = schp
	nshp = nsbase
	nshe = nshp+nsmask+1
	nsbp = nshp
   ]

and scnext() = valof
// Traverse symbol table reporting all expressions that meet
//	the criteria described in scinit.  Major traversal is
//	through the array of hash chain heads, minor is through
//	the chains themselves.
  [	 [ scep = scep!slink
	   if scep eq scstop then
	    [	schp = schp+1
		if schp eq sche resultis 0
		scep = schp
		loop
	     ]
	   if (scep!scwn & scmask) eq scval resultis scep
	 ] repeat
   ]

and nsnext() = valof
// Traverse table of new (redefined since /R) symbols that meet
//   the criteria set by scinit.  Order is as in scnext.
[	[ nsbp = @nsbp
	  if nsbp eq 0 then
	  [ nshp = nshp+1
	    if nshp eq nshe resultis 0
	    nsbp = nshp
	    loop
	  ]
	  let ep = nsbp!1
	  if (ep!scwn & scmask) eq scval resultis ep
	] repeat
]

and lookup(paddr, pcc) = valof
// Just a dummy
[	lookup = fastlookup
	resultis fastlookup(paddr, pcc)
]

and fastlookup(paddr, pcc) = valof
// Lookup symbol in symbol table.  If present, return ep;
//	if not, return zero and leave values around to
//	allow putin to work.
  [	saddr, scc = paddr, pcc
	let sw1, sw2, sw3, sw4, sw5, sw6, sw7 =
	  nil, nil, nil, nil, nil, nil, nil
	let compare = nil
	let chain = nil	// Faster branch if in local variable
	let ptr = nil
	switchon pcc into
	[ case 15: compare = cw7; sw7 = paddr!14 lshift 8; goto p6
	  case 14: compare = cw7; sw7 = 0; goto p6
	  case 13: compare = cw6; sw6 = paddr!12 lshift 8; goto p5
	  case 12: compare = cw6; sw6 = 0; goto p5
	  case 11: compare = cw5; sw5 = paddr!10 lshift 8; goto p4
	  case 10: compare = cw5; sw5 = 0; goto p4
	  case 9: compare = cw4; sw4 = paddr!8 lshift 8; goto p3
	  case 8: compare = cw4; sw4 = 0; goto p3
	  case 7: compare = cw3; sw3 = paddr!6 lshift 8; goto p2
	  case 6: compare = cw3; sw3 = 0; goto p2
	  case 5: compare = cw2; sw2 = paddr!4 lshift 8; goto p1
	  case 4: compare = cw2; sw2 = 0; goto p1
	  case 3: compare = cw1; sw1 = paddr!2 lshift 8; goto p0
	  case 2:	// Special check for packed symbol
	          if paddr!1 ls 40b then
	            resultis (paddr!1 eq symc? @paddr+fstop, 0)
	          compare = cw1; sw1 = 0; goto p0
	  case 1: compare = cw0; sw0 = @paddr lshift 8; goto pn
	  case 0: compare = cw0; sw0 = 0; goto pn
	]
	// Long name
	compare = complong
p7:	sw7 = (paddr!14 lshift 8) + paddr!15
p6:	sw6 = (paddr!12 lshift 8) + paddr!13
p5:	sw5 = (paddr!10 lshift 8) + paddr!11
p4:	sw4 = (paddr!8 lshift 8) + paddr!9
p3:	sw3 = (paddr!6 lshift 8) + paddr!7
p2:	sw2 = (paddr!4 lshift 8) + paddr!5
p1:	sw1 = (paddr!2 lshift 8) + paddr!3
p0:	sw0 = (@paddr lshift 8) + paddr!1
pn:	@svec = sw0
	chain = schain
	hlast = ((rv paddr + paddr!(pcc-1) + pcc) rem hsize) + symbase
	ptr = hlast	// *** assumes slink eq 0
schain:	// Traverse through hash chain.
	 [ let ptr1 = @ptr
	   if sw0 eq ptr1!sname then
	    [ hprev = ptr
	      ptr = ptr1
	      if ptr ne endhc goto compare
	      resultis 0
	    ]
	   ptr = @ptr1
	   if sw0 eq ptr!sname then
	    [ hprev = ptr1
	      if ptr ne endhc goto compare
	      resultis 0
	    ]
	 ] repeat
complong:		// Very long name, must use loop
	 [ let tptr = ptr+(sname+8)
	   let sptr = paddr+16
	   let tend = ptr+sname+(pcc rshift 1)
	   while tptr ne tend do
	    [ if (@sptr lshift 8 + sptr!1) ne @tptr goto chain
	      tptr, sptr = tptr+1, sptr+2
	    ]
	   if @tptr ne (pcc<<odd eq 0? 0, @sptr lshift 8) goto chain
	 ]
cw7:	if ptr!(sname+7) ne sw7 goto chain
cw6:	if ptr!(sname+6) ne sw6 goto chain
cw5:	if ptr!(sname+5) ne sw5 goto chain
cw4:	if ptr!(sname+4) ne sw4 goto chain
cw3:	if ptr!(sname+3) ne sw3 goto chain
cw2:	if ptr!(sname+2) ne sw2 goto chain
cw1:	if ptr!(sname+1) ne sw1 goto chain
cw0:
	// Move symbol to head of chain
	@hprev = @ptr
	@ptr = @hlast
	@hlast = ptr
	resultis ptr
   ]

and putin(ptype) = valof
// Puts a symbol in the symbol table.  Depends on Lookup
//	having immediately preceded it.
  [	if scc eq 0 then
	  errx("Can't define 0-length symbol", true)
	if (scc eq 2) & (saddr!1 ls 40b) then
	  test saddr!1 eq symc
	  ifso error("Putin error")
	  ifnot errx("Attempt to define @V as symbol", true, @saddr)
	let chp = saddr+scc
	[ [ chp = chp-1
	    if (@chp gr $7) % ((@chp ls $0) & ((@chp ne $-) % (chp ne saddr))) goto putok
	  ] repeatwhile chp ne saddr
	  errx("Attempt to define @B as symbol", true, saddr, scc)
putok:	]
	DoubleAdd1(lv zenter)
	if tracesyms then [ Puts(erlchan, $←) ]
	let wc = scc rshift 1
	let dsize =		// Size of dope
	  typesizes!(ptype ls 0? adrtype,
	   ptype gr maxtype? mactype,
	   ptype)
	let bsize = wc + 2 + dsize
	let badd = get1(bsize)
	let ptr = badd + dsize
	ptr!(wc+sname) = 0
	pak(ptr+sname, saddr, scc)
	// Link symbol onto beginning of chain
	ptr!slink = @hlast
	@hlast = ptr
	ptr!stype = ptype
	resultis ptr
   ]

and newdef(ep, add) = valof
// If add=false, return true iff ep is new since /R
// If add=true, mark ep as new since /R
[	if ep-fstop ls ofbot resultis true
	let hp = (ep & nsmask) + nsbase
	while @hp ne 0 do
	[ if (@hp)!1 eq ep resultis true
	  hp = @hp
	]
	unless add resultis false
	let bp = alloc(2)
	@bp, bp!1 = 0, ep
	@hp = bp
	resultis true
]