:TITLE[Htfind.0mc, October 19, 1983  3:50 PM, van Melle];

* RV[Probe, IP[lspNargs]];
RV[Case, IP[lspDefx0]];
RV[Entry, IP[lspDefx1]];

@GcRef:	* TOS -> TOS if cnt went to zero, NIL otherwise

	Case ← 100000c, opcode[025];	* = explicit punt if collision
	T ← NextData[IBuf];
	Case ← (Case) + T;

	T ← Stack&-1;			* L0,1 ← TOS
	lspL1 ← T;
	T ← Stack&+1, loadpage[pgHtFind];
	lspL0 ← T, call[GcLookup];
	lu ← (Entry) and (htStkCnt), goto[TOSGetsNILifNeq];
			* Smash TOS with NIL unless Stackbit = refcnt = 0

onpage[pgHtFind];
* GcLookup subroutine L0,1 = Pointer, Case = operation; returns Entry, which
* is guaranteed to be valid in collision bit and count field if no collision
* Smashes lspLN, lspNargs, lspL5, AC2/Hi, and Case & Entry (lspDefx0,1).

GcLookup:
	UseCTask;
GcLookupWithCTask:
	T ← APC&APCTask, task;
	lspL5 ← T;			* Save return address
	T ← lspL0 ← rhmask[lspL0];	* mask out any garbage in hi 8 bits
	T ← lsh[lspL0, 7];
*	T ← lsh[lspL0, 7], skip[NoH2Bit8];
*	  goto[htAtom];		* This catches invalid addrs in the 200b range
				* Too bad I can't get the 100b ones so cheaply
	T ← (rsh[lspL1, 11]) or T;
	PFetch1[MDSTypeBaseBr, lspLN];	* get Type table entry
	AC2Hi ← (htMainSpace);		* Set up base register while waiting
	AC2 ← (htMainBase), task;
	T ← rsh[lspL1, 1];		* Location to make probe in hash table
	lu ← (lspLN) and (40000c), goto[htAtom, R<0];	* sign bit = no gcref
*	skip[alu=0];			* test "no gcref in microcode" bit
*	  goto[htPunt];			* Entry needs to be set

	PFetch1[AC2, Entry];		* Get Hash table entry
					* Cannot fault because table locked
	lu ← Entry, skip[Reven];	* Pointer to collision entry if odd
	  goto[htPunt];
	T ← lsh[lspL0, 1], goto[htNotEmpty, alu#0];	* Is entry empty?
	Entry ← T;			* Empty means Entry has count of 1
	Entry ← (Entry) or (ht1cnt), goto[htDisp];

htNotEmpty:
	T ← ldf[Entry, 7, 10];		* get hi bits of hash table entry
	lu ← (lspL0) xor T;		* compare with the pointer
	lu ← (Entry) + (ht2Cnt), skip[alu=0];
	  Entry ← 1c, goto[htPunt];	* Not same, is a collision
	goto[htPunt, carry];		* Cnt field is full or about to be, so punt it to Lisp

htDisp:
	dispatch[Case, 16, 2];
	lspLN ← (htStkCnt), disp[htProbe];

htProbe:
	T ← Entry ← (Entry) + (ht1Cnt), goto[htStore], disptable[3];	* [0]: addref
	T ← Entry ← (Entry) - (ht1Cnt), goto[htStore];		* [1]: delref
	T ← Entry ← (Entry) or (htStkBit), goto[htStore];	* [2]: stkref

htStore:				* store modified entry
	lspLN ← (lspLN) and T;		* Mask out all but Stk & Cnt fields
	lu ← (lspLN) xor (ht1Cnt);
	lspLN ← T, skip[alu#0];
	  lspLN ← 0c;			* Entry with cnt=1, stk=0 not stored
	T ← rsh[lspL1, 1];
	PStore1[AC2, lspLN], goto[htDone];
					* Update entry in hash table
htAtom:
	Entry ← (ht1Cnt), goto[htDone];	* atoms etc never in table
htDone:
	lspL0 ← rhmask[lspL0], call[retLBL];	* Mask out garbage from Punt
					* And task, because next return won't
	APC&APCTask ← lspL5, goto[retLBL];

htPunt:					* add case,,L0,1 to HT overflow table
	T ← lsh[Case, 10], goto[htCallUfn, R<0];
	lspL0 ← (lspL0) + T;		* OR case into hi L0
	T ← 100000c, call[.+1];		* Offset of overflow table
	PFetch1[AC2, lspLN];		* fetch entry
	lu ← lspLN;			* Zeros end the table
	skip[alu=0], Case ← (Case) or (GcOverflowPunt);	* signal punt for end of op
	  T ← (Form2[AllOnes]) + T, return;	* T ← T+2, return to PFetch1
	PStore2[AC2, lspL0], goto[htDone];	* Store entry

htCallUfn:				* Can't defer this punt
	lspUFN ← 025c, goto[ufnLBL];

* RplPtr.n(base, ptr): store TOS at n off TOS-1, preserving the hi byte at TOS-1

@RplPtr:
	loadpage[pgHStack], call[CheckElt2P4], opcode[24];

:IF[0];		*** old code
	loadpage[pgRplPtr];
	nop;
onpage[pgRplPtr];
	Stack&-2, call[PopL2];		* Set L3,2 to ptr
	T ← NextData[IBuf];		* get offset
	lspL2 ← (lspL2) + T;
	goto[RplPtrA1, no carry];
	  lspL3 ← (lspL3) + (400c);	* crossed segment
	goto[RplPtrA1];		* because just wrote hi base for next PFetch

PopL2:				* put TOS in L3,2 for addressing
				* leaves stack level alone
	T ← Stack&-1;
	lspL2 ← T;
	T ← rhmask[Stack&+1];
	lspL3 ← T;
	lspL3 ← (lsh[lspL3, 10]) + T + 1, return;

:ELSE;		**** new code
	Stack&-2;			* Point at base
	T ← NextData[IBuf];		* word offset
	T ← (Stack&-1) + T, loadpage[pgRplPtr];	* form low base
	lspL2 ← T, FreezeResult;
onpage[pgRplPtr];
	T ← lsh[Stack&+1, 10], skip[Carry'];	* form hi base
	  T ← (R400) + T;		* Segment cross
	lspL3 ← T;
	nop;				* wait for hi base to write
:ENDIF;

RplPtrA1:		* from Rplaca, Rplacd.
			* Stack points at PTR, new item is above that
			* L3,2 points at cell to replace in
	PFetch2[lspL2, lspL0, 0];	* get old contents.  CAN FAULT
	T ← lhmask[lspL0];
RplPtrA2:				* from Rplaca
	XBuf ← T, loadpage[pgHTFind];
	Case ← 1c, callp[GcLookup];	* Deleteref old value

	StkState ← rsh[StkState, 1];
	Stack&+2;			* point at new item
	T ← Stack&-1;
	lspL1 ← T;
	T ← XBuf;			* old hi byte,,0
	T ← (rhmask[Stack&-1]) or T;
	lspL0 ← T;			* L0,1 ← old hi byte,,new item
 
RplPtr1:			* From GVAR←.  L0,1 = words to store at L3,2
	PStore2[lspL2, lspL0, 0];	* Store new value, no fault

	loadpage[pgHTFind];		* Case ← 0 in rh, punt flag remains
	Case ← lhmask[Case], callp[GcLookup];	* Addref new value
	
GcExit:			* Come here at end of gcreffing instructions
			* normal finish, unless htpunt was called for
	lu ← (Case) and (GcPunts);
	lu ← (Case) and (CreateCellPunt), skip[alu#0];
	  NextOpCode;
	lspDefx1 ← (atomGCOVERFLOW), skip[alu=0];
	  lspDefx1 ← (atomGCGENPUNT);	* more general punt needed
	loadpage[pgFrame];
	lspNargs ← 1c, gotop[lspCallFn0];



* Gvar←, op 27: Set top value of atom alpha,beta to TOS

@SetGvar:
	loadpage[pgRplPtr], opcode[27];
	lspL3 ← (VALspace);
onpage[pgRplPtr];
	T ← NextData[IBuf];
	lspL2 ← T;
	T ← NextData[IBuf];
	lspL2 ← (lsh[lspL2, 10]) or T, task;	* L2 ← atom number to set
	lspL2 ← lsh[lspL2, 1];		* Point at value cell
SetGvar1:				* Here from SETF
	PFetch2[lspL2, lspL0, 0];	* fetch old top value; CAN FAULT

	loadpage[pgHTFind];
	Case ← 1c, callp[GcLookup];	* Deleteref old value
	
	T ← Stack&-1;
	lspL1 ← T;			* L0,1 ← TOS
	T ← Stack&+1;
	lspL0 ← T, goto[RplPtr1];

* Rplaca (LST ITEM)

@Rplaca:
	loadpage[pgHStack], call[CheckElt2P4], opcode[030];
	Stack&-2, call[lspTyp];		* check type of LST (below ITEM)
	loadpage[pgRplPtr];
	lu ← (lspType) - (listType);
onpage[pgRplPtr];
	skip[alu=0];			* ok if listp
	  lspUFN← 30c, goto[ufnLBL];
					* LST is now in L3,2 from lspTyp
	PFetch2[lspL2, lspL0, 0];	* look at old cdrcode,,car
	T ← lhmask[lspL0];		* cdrcode
	goto[RplPtrA2, alu#0];		* cdrcode#0 is normal, do Rplptr
	T ← lsh[lspL0, 10];		* indirect: this is hiloc shifted
	lspL3 ← T;
	T ← lspL1;			* loloc indirect
	lspl2 ← T, goto[RplPtrA1];	* go do RPLPTR with fetch


@Rplacd:
	lspUFN← (031c), goto[lspUfnxP4], opcode[031];	* Rplacd
%
@Rplacd:		* (RPLACD LST NEWCDR) => LST

	loadpage[pgHStack], call[CheckElt2P4], opcode[031];
	Stack&-2, call[lspTyp];		* get LST in L3,2, type in lspType
	loadpage[pgRplacd];
	lu ← (lspType) - (listtype);
onpage[pgRplacd];
	lspUFN ← 31c, skip[alu=0];
	  goto[ufnLBL];
RplacdFetch:
	Pfetch2[lspL2, lspL0, 0];	* fetch contents of cell
	T ← lhmask[lspL0];		* get cdrcode
	XBuf1 ← T, goto[Rplacd1, alu#0];
	T ← lspL0;
	lspL3 ← T;			* follow indirect...
	T ← lspL1;
	lspL2 ← T, goto RplacdFetch;

Rplacd1:				* contents of cell in 0,1
	goto[RplacdIndirect, alu>=0];	* indirect on page
	Stack&+2;			* Point Stack back at NEWCDR
	T ← Stack&-1;
	lspL1 ← T;
	T ← Stack&-1;			* Stack points at LST now
	lspL4 ← T, goto[RplacdNIL, alu=0];	* NEWCDR in 4,1
	lu ← (lspL3) xor T;		* compare hi words of LST, NEWCDR
	skip[alu=0];
	  goto[ufnLBL];			* NEWCDR not on LST's page
	T ← lhmask[lspL1];
	T ← (lhmask[lspL2]) xor T;	* compare lo parts of page numbers
	skip[alu=0];
	  goto[ufnLBL];			* NEWCDR not on LST's page
	T ← lsh[lspL1, 7];		* cell # of NEWCDR in left half
	T ← (rhmask[lspL0]) or T;	* new contents of LST, minus 200 bit
	XBuf2← T;			* save it
	T ← lspL4;
	lspL0 ← T, loadpage[pgHtFind];	* NEWCDR now in 0,1
	Case← 0c, callp[GcLookup];	* Addref NEWCDR

RplacdSmash:				* Old cdrcode in lh of XBuf1
					* almost new cdr code word in XBuf2
					* LST in L3,2
	T ← XBuf1 ← ldf[XBuf1, 1, 10];	* 2 * old cdrcode
	T ← (lhmask[lspL2]) + 2;	* point at old cdr
	lspL1 ← T;
	T ← lspL3;
	lspL0 ← T, loadpage[pgHtFind];	* old cdr now in 0,1
	Case ← (lhmask[Case]) + 1, callp[GcLookup]; 	* deleteref old cdr
	XBuf2 ← (XBuf2) or (100000c);	* set cdr on page bit
	PStore1[lspL2, XBuf2, 0];	* update LST's cdr code
	loadpage[pgRplPtr];
	StkState←rsh[StkState,1], gotop[GcExit];
				* done, having popped once, check for punt

RplacdNIL:				* Hiloc(NEWCDR) = 0
	lu ← lspL1;			* Loloc(NEWCDR)
	XBuf2 ← 0c, goto[RplacdSmash, alu=0];
	  goto[ufnLBL];			* NEWCDR not NIL, so punt
	
RplacdIndirect:				* indirect on page, cdrcode in XBuf1
	T ← ldf[XBuf1, 1, 10], loadpage[pgRplptr];	* 2 * old cdrcode
	lspL2 ←(lhmask[lspL2]) + T, gotop[RplptrA1];
				* point 3,2 at loc of cdr, go do RplPtr on it
	
%	



* GcScan1, op 173
@GcScan1:
	T ← 0c, goto[GcScanx], opcode[173];

* GcScan2, op 174
@GcScan2:
	T ← 100c, opcode[174];
	lspL0 ← (htStkBit);		* Pattern = Collision bit or Stack bit
 	lspL0 ← (lspL0) + 1, goto[GcScanx];

GcScanx:
	Saluf ← T, T ← Stack&-1;	* MB ← 0 for GcScan1, 1 for GcScan2
	lspGenBrHi ← (htMainSpace);
	lspGenBr ← (htMainBase);
	lspLN ← T, loadpage[pgGcScan];
	PFetch4[lspGenBr, XBuf];
onpage[pgGcScan];
GcScanRestart:
	Dispatch[lspLN, 16, 2];
	T ← lspLN ← (lspLN) and not (3c), disp[.+1];
	lu ← 0c, goto[GcSa0], disptable[4];	* == instr at GcSa0+1
	lu ← 0c, goto[GcSa1];			* == T ← XBuf, goto[GcSa0];
	lu ← 0c, goto[GcSa2];			* == T ← XBuf1, goto[GcSa1];
	lu ← 0c, goto[GcSa3];			* == T ← XBuf2, goto[GcSa2];

GcScanLoop:
	PFetch4[lspGenBr, XBuf], call[retLBL];	* get 4 words, task
	T ← XBuf3;
GcSa3:	T ← XBuf2, goto[GcSc3, alu#0];
GcSa2:	T ← XBuf1, goto[GcSc2, alu#0];
GcSa1:	T ← XBuf, goto[GcSc1, alu#0];
GcSa0:	lu ← lspLN, goto[GcSc0, alu#0];
	T ← lspLN ← (lspLN) - (4c), dblgoto[GcScanLoop, GcScanDone, alu#0];

GcSc3:	lspLN ← (lspLN) + (3c);
	T ← XBuf3, dblgoto[GcCheck2, GcCheck1, MB];
	
GcSc2:	lspLN ← (lspLN) + (2c);
	T ← XBuf2, dblgoto[GcCheck2, GcCheck1, MB];
	
GcSc1:	lspLN ← (lspLN) + (1c);
	T ← XBuf1, dblgoto[GcCheck2, GcCheck1, MB];
	
GcSc0:	T ← XBuf, dblgoto[GcCheck2, GcCheck1, MB];

GcCheck1:			* In GcScan1: Check for collision or stk=cnt=0
	lspL1 ← T;
	lu ← (lspL1) and (htStkCnt), skip[Reven];
	  T ← lspLN, goto[GcScanFound];		* Collision bit on
	skip[alu#0];
	  T ← lspLN, goto[GcScanFound];		* Stk=cnt=0
	goto[GcScanRestart];		* combine with GcCheck2+1? (senses are reversed)

GcCheck2:			* In GcScan2: Check for collision or stk
	lu ← (lspL0) and T;	* L0 set up the desired pattern at init
	T ← lspLN, dblgoto[GcScanFound, GcScanRestart, alu#0];

GcScanDone:
	T ← Stack ← 0c;		* TOS ← NIL when done
GcScanFound:
	Stack&+1 ← T, goto[nxiLBL];	* TOS ← offset of find


@ReclaimCell:	* (PTR) => newptr or NIL
*	lspUFN ← (172c), goto[lspUfnxP5], opcode[172];	* ReclaimCell

	loadpage[pgReclaim], opcode[172];
	nop;
onpage[pgReclaim];
	loadpage[pgTyp];
	callp[lspTyp];
	lu ← (lspType) - (listtype);
	lspUFN ← 172c, skip[alu=0];
	  goto[ufnLBL];			* punt if nlistp
	PFetch2[lspL2, lspL0, 0];	* contents of cell -> L0,1
	T ← lhmask[lspL0];		* get cdrcode
	skip[alu#0];
	  goto[ufnLBL];			* punt on full indirect (cdrcode=0)
%---------
	T ← lhmask[lspL0], skip[R<0];	* get cdrcode, skip if 200 bit on
	  goto[ufnLBL];			* punt on indirect cases
---------%

	XBuf ← T, call[FreeListCell];	* save cdr code, free cell L2,3
	loadpage[pgHtFind];
	Case ← 1c, call[GcLookup];	* deleteref car

	Stack&-1 ← 0c;			* TOS ← NIL
	Stack&+1 ← 0c, call[ReclaimCheck];  * Put car on stack if cnt is zero

	T ← ldf[XBuf, 1, 10];		* cdrcode lsh 1
	goto[ReclaimDone, alu=0], T ← lspL2← (lhmask[lspL2]) + T;
				* point at cdr cell, or finished if cdr nil
	lu ← XBuf, goto[ReclaimCdr1, R<0];	* Case 1 is cdr on page, easy
	PFetch2[lspL2, lspL0, 0];		* Get contents of cdr's cell
	call[FreeListCell];			* Free the cell itself
	T ← rhmask[lspL0], goto[ReclaimCdr2];	* cdr = contents of cell

ReclaimCdr1:
	lspL1 ← T;			* lo half of cdr
	T ← rsh[lspL3, 10];
ReclaimCdr2:
	lspL0 ← T, loadpage[pgHtFind];	* hi half
	call[GcLookup];			* deleteref cdr
	call[ReclaimCheck];		* put cdr on stack if cnt is zero
ReclaimDone:
	loadpage[pgRplPtr];
	gotop[GcExit];			* check for htpunt


ReclaimCheck:
	* Replace TOS with L0,1 if Entry is a zero-count ht entry

	lu ← (Entry) and (htstkcnt), skip [REven];
	  return;			* Collision entry, so unknown
	T ← lspL0, skip[alu=0];
	  return;			* Stkbit,,cnt nonzero
	Stack&-1 ← T;
	T ← lspL1;
	Stack&+1 ← T, return;


FreeListCell:
	* L3,2 points at cell to free.  L0 is contents of its first word.
	* Smashes L3,2 to point at pagebase.  Smashes L4, lh[L0], XBuf2,3

	T ← rhmask[lspL2];		* offset in page
	lspL2 ← lhmask[lspL2];		* point to page base
	Pfetch2[lspL2, XBuf2, 0];	* get count,,nextcell, nextpage
	lspL4 ← T;			* save offset
	lu ← XBuf3, skip[R>=0];		* nextpage = -1 is punt case.
		* NOTE: Assumes < 24-bit addrs, or no lists on negative pages
	  goto[ufnLBL];			* punt if this page not in free chain
	T ← lsh[XBuf2, 10];		* next cell, shifted to high byte
	lspL0 ← (rhmask[lspL0]) or T;	* make it cdr code at cell
	T ← lspL4;
	Pstore1[lspL2, lspL0];		* store it back in cell
	XBuf2 ← (XBuf2) + (400c);	* increment count
	XBuf2 ← (lhmask[XBuf2]) or T;	* make it count,,celloffset

:IF[BreakPoints];
	lu ← XBuf2, skip[REven];
	  breakpoint;
:ENDIF;
	Pstore1[lspL2, XBuf2, 0];	* store it back on page
	return;				* allow XBuf2 write


	:END[Htfind];