
		LOAD		'Macros.dump'
		INCLUDE	'M16.Profile'

		INCLUDE	'SS.equ'

		INCLUDE	'Driver.equ'
		INCLUDE	'Heap.aii.i'
		INCLUDE	'Eval.aii.i'

;-----------------------------------------------
;
;	Imported addresses
;
;-----------------------------------------------

		IMPORT	D_AlertBox
		IMPORT	D_BeachBall
		IMPORT	D_MainZPage

		IMPORT	S_CurChangedList
		IMPORT	S_CurDefFormat
		IMPORT	S_CurEditFlag
		IMPORT	S_DepOnMeTreeTooBig
		IMPORT	S_EvalFormula
		IMPORT	S_GetCellIndex
		IMPORT	S_GetCellPtr
		IMPORT	S_GetCellTableEntry
		IMPORT	S_GetRealCell
		IMPORT	S_IsCellCircular
		IMPORT	S_NewCell
		IMPORT	S_NormalizeRange
		IMPORT	S_RedrawCellRange
		IMPORT	S_RemoveCell
		IMPORT	S_SetCellIndex
		IMPORT	S_SetCellTableEntry
		IMPORT	S_TraverseRange

;-----------------------------------------------
;
;	Forward addresses and entries
;
;-----------------------------------------------

	ENTRY	S_AddDepListToRecalc
	ENTRY	S_CalculateCell
	ENTRY	S_CheckForCirc
	ENTRY	S_FirstCallToCheckCircularity
	ENTRY	S_OrigCell

;-------------------------------------------------------------------------;
; S_AddCellToChangedList( Cell:l)
;
;	S_AddCellToChangedList will add a cell to the changed list.

S_AddCellToChangedList	PROC		EXPORT
					;Using	S_CurrentData

			input	Cell:l
			local	CellPtr:l,DepListPtr:l,LastPtr:l
			BEGIN

CellTableEntry	equ		CellPtr

			in		Cell:l
			out		CellTableEntry:l
			XCall	S_GetCellTableEntry

			ora		CellTableEntry
			jeq		Exit

			lda		CellTableEntry+2
			and		#S_CellTableFlags
			beq		realCell

	;	The cell to Mark changed, is not a real cell.
	;	We will Mark all cells in its DependOnMe list.

			lda		CellTableEntry+2
			bpl		fullList

			and		#$FFFF-S_CellTableFlags
			tax
			lda		CellTableEntry

			Call		S_AddCellToChangedList,in=(ax:l)
			brl		Exit

fullList
			and		#$FFFF-S_CellTableFlags
			sta		CellTableEntry+2

			H_GetBlockPtr	CellTableEntry,DepListPtr

			AddLong	DepListPtr,[DepListPtr],LastPtr			; size
			AddLong	DepListPtr,#S_DependList,DepListPtr

loop
			Call		S_AddCellToChangedList,in=([DepListPtr]:l)

			AddLong	DepListPtr,#S_DependInc,DepListPtr
			CmpLong	DepListPtr,LastPtr
			blt		loop

			brl		Exit

realCell
			H_GetBlockPtr	CellTableEntry,CellPtr

			MoveWord	[CellPtr]:#S_CellNext,a		; chk if in list
			ldy		#S_CellNext+2
			ora		[CellPtr],y
			bne		Exit						; no long refs = 0

			MoveLong	#S_EndOfList,[CellPtr]:#S_CellPrevious

			lda		S_CurChangedList
			ora		S_CurChangedList+2
			bne		exists

			MoveLong	#S_EndOfList,[CellPtr]:#S_CellNext
			bra		fixCurChangedList

exists
			MoveLong	S_CurChangedList,[CellPtr]:#S_CellNext

			in		S_CurChangedList:l
			out		CellPtr:l
			XCall	S_GetCellPtr

			MoveLong	Cell,[CellPtr]:#S_CellPrevious

fixCurChangedList
			MoveLong	Cell,S_CurChangedList

Exit
			RETURN
			ENDP

;-------------------------------------------------------------------------;
; S_RemoveCellFromChangedList( Cell:l,NextCell:l )
;
;	S_RemoveCellFromChangedList will remove a cell from the changed list.
; If the cell is not in the list, this routine will not work.

S_RemoveCellFromChangedList	PROC		EXPORT
						;Using	S_CurrentData

			input	Cell:l,PreviousCell:l,NextCell:l
			local	CellPtr:l
			BEGIN

	ProfileIn		3

			CmpLong	PreviousCell,#S_EndOfList
			bne		notHead

			CmpLong	NextCell,#S_EndOfList
			beq		clearList

			MoveLong	NextCell,S_CurChangedList
			bra		fixNext

clearList
			stz		S_CurChangedList
			stz		S_CurChangedList+2
			bra		Exit

notHead
			in		PreviousCell:l
			out		CellPtr:l
			XCall	S_GetCellPtr

			MoveLong	NextCell,[CellPtr]:#S_CellNext


	;	Before fixing the NextCell, make sure it is not the EndOfList.

			CmpLong	NextCell,#S_EndOfList
			beq		Exit

fixNext
			in		NextCell:l
			out		CellPtr:l
			XCall	S_GetCellPtr

			MoveLong	PreviousCell,[CellPtr]:#S_CellPrevious

Exit

	ProfileOut	3

			RETURN
			ENDP

;-----------------------------------------------------------------------------
;	S_RecalcList
;

S_NumOfRecalcBuckets	equ		64

S_RecalcList	DS.L		S_NumOfRecalcBuckets

;-----------------------------------------------------------------------------
;	S_ZeroRecalcList
;

S_ZeroRecalcList PROC	EXPORT

			BEGIN

			ldx		#S_NumOfRecalcBuckets<<2
			lda		#0

loop
			dex
			dex
			sta		S_RecalcList,x
			bne		loop

			RETURN
			ENDP

;-------------------------------------------------------------------------;
; S_AddCellToRecalcList( Cell:l )
;
;	S_AddCellToRecalcList will add a cell to the recalculate list.
;	The recalculate list is now implemented by a series of buckets.
;	The buckets will contain those cells that need to be recalculated
;	which have the lower n bits of the priority number equal to the
;	bucket number.


S_AddCellToRecalcList	PROC		EXPORT
					;Using	S_CurrentData

			input	Cell:l
			local	RecalcPtr:l,CellPtr:l,CellPriority:w
			local	NextCell:l,NextPtr:l,BucketOffset:w
			BEGIN

			in		Cell:l
			out		CellPtr:l
			XCall	S_GetCellPtr

			ora		CellPtr					; does this ever happen ?
			beq		toExit

			MoveWord	[CellPtr]:#S_CellFormat+2,a
			and		#S_CellCircular
			bne		toExit

			MoveWord	[CellPtr]:#S_CellNext,a		; checks if in a list
			ldy		#S_CellNext+2
			ora		[CellPtr],y
			beq		addToList					; no long refs = 0

toExit
			brl		Exit

	;	First figure out which bucket to add cell to and then do the
	;	insertion Sort on that bucket.

addToList
			MoveWord	[CellPtr]:#S_CellPriority,CellPriority
			and		#S_NumOfRecalcBuckets-1
			asl		a
			asl		a
			sta		BucketOffset
			tax

			lda		S_RecalcList,x
			ora		S_RecalcList+2,x
			bne		exists

			MoveLong	Cell,S_RecalcList:x
			MoveLong	#S_EndOfList,[CellPtr]:#S_CellNext

			brl		Exit

exists
			in		S_RecalcList:x:l
			out		RecalcPtr:l
			XCall	S_GetCellPtr

			CmpWord	[RecalcPtr]:#S_CellPriority,CellPriority
			blt		startLoop

			ldx		BucketOffset
			MoveLong	S_RecalcList:x,[CellPtr]:#S_CellNext
			MoveLong	Cell,S_RecalcList:x
			bra		Exit

loop
			MoveLong	NextPtr,RecalcPtr

startLoop
			MoveLong	[RecalcPtr]:#S_CellNext,NextCell

			CmpLong	NextCell,#S_EndOfList
			beq		FoundPlace

			in		NextCell:l
			out		NextPtr:l
			XCall	S_GetCellPtr

			CmpWord	[NextPtr]:#S_CellPriority,CellPriority
			blt		loop

FoundPlace
			MoveLong	NextCell,[CellPtr]:#S_CellNext
			MoveLong	Cell,[RecalcPtr]:#S_CellNext

Exit
			RETURN
			ENDP

;-------------------------------------------------------------------------;
; S_AddCellToDepList( CellToEnter:l,TargetCell:l )
;
;	S_AddCellToDepList will add a cell to another cell's dependency list.

S_AddCellToDepList	PROC		EXPORT
				;Using	S_CurrentData

			input	CellToEnter:l,TargetCell:l
			local	CellIndex:l,CellPtr:l,DependPtr:l
			local	DependSize:l,NewSize:l,DependList:l,LastPtr:l
			local	DependRangeList:l,NewRangeList:l,CellExists:w
			local	OtherCellInList:l
			error	ErrorFlag
			BEGIN

			stz		ErrorFlag
			stz		CellExists

			in		TargetCell:l
			out		CellIndex:l
			XCall	S_GetCellTableEntry

			ora		CellIndex
			beq		noDepsOnMe

			lda		CellIndex+2
			and		#S_CellTableFlags
			beq		targetExists

			MoveLong	CellIndex,DependList
			bra		depListExists

	;	The TagetCell is real, so we have to dereference it to get the
	;	depend list if there is one.

targetExists
			inc		CellExists

			H_GetBlockPtr	CellIndex,CellPtr

			MoveWord	[CellPtr]:#S_CellDependOnMe,DependList
			MoveWord	[CellPtr]:#S_CellDependOnMe+2,DependList+2

			ora		DependList
			bne		depListExists

	;	There are no previous DependOnMe's for this TargetCell
	;	We will Create a short list (type: Single)

noDepsOnMe
			MoveWord	CellToEnter,DependList

			lda		CellToEnter+2
			ora		#S_CellTableSingDep
			sta		DependList+2

			lda		CellExists
			jeq		noCell
			brl		storeInCell

	;	Previous DependOnMe's exists, but we don't know how many and
	;	which type they are.

depListExists
			lda		DependList+2
			jpl		fullListExists

			and		#$FFFF-S_CellTableFlags
			sta		OtherCellInList+2
			MoveWord	DependList,OtherCellInList

			lda		DependList+2
			and		#S_CellTableFlags
			cmp		#S_CellTableRangeDep
			beq		singleAndRange

	;	The previous list was only one single dependency reference.
	;	Check to see if it is the CellToEnter, and if not, make a
	;	full dep list.

			CmpLong	CellToEnter,OtherCellInList
			jeq		Exit

			MoveLong	#S_DependStructSize+S_DependInc,DependRangeList
			bra		createList

	;	The previous list was only one range dependency reference.
	;	Make a full dep list.

singleAndRange
			MoveLong	#S_DependStructSize,DependRangeList

createList
			H_NewBlock	#S_DependStructSize+S_DependInc,DependList,DependPtr,err=ErrorFlag
			jcs			Exit

			MoveLong	#S_DependStructSize+S_DependInc,[DependPtr]:#S_DependSize
			MoveLong	DependRangeList,[DependPtr]:#S_DependRangeList

			MoveLong	CellToEnter,[DependPtr]:#S_DependList
			MoveLong	OtherCellInList,[DependPtr]:#S_DependList+S_DependInc

			lda		DependList+2
			ora		#S_CellTableList
			sta		DependList+2

	;	Determine where to store the New DependList just created.

			lda		CellExists
			beq		noCell

			H_GetBlockPtr	CellIndex,CellPtr

storeInCell
			MoveLong	DependList,[CellPtr]:#S_CellDependOnMe
			brl		Exit

noCell
			in		TargetCell:l,DependList:l
			XCall	S_SetCellTableEntry,err=ErrorFlag
			brl		Exit


	;	A previous full DependOnMe list exists, insert the New cell if it
	;	is not already in the list

fullListExists
;			lda		DependList+2			; already in 'a'
			and		#-1-S_CellTableList
			sta		DependList+2

	;	Search the original list.

			H_GetBlockPtr	DependList,DependPtr

			MoveLong	[DependPtr]:#S_DependSize,DependSize
			MoveLong	[DependPtr]:#S_DependRangeList,DependRangeList

			AddLong	DependPtr,DependRangeList,LastPtr
			AddLong	DependPtr,#S_DependList,DependPtr
			bra		cmpPtrs

SearchLoop
			CmpLong	[DependPtr],CelltoEnter
			jeq		Exit

			AddLong	DependPtr,#S_DependInc,DependPtr

cmpPtrs
			CmpLong	DependPtr,LastPtr
			blt		SearchLoop

	;	Cell is not already in DependOnMe List	;

			AddLong	DependSize,#S_DependInc,NewSize
			AddLong	DependRangeList,#S_DependInc,NewRangeList

			H_ResizeBlock	DependList,NewSize,DependPtr,err=ErrorFlag
			bcs			Exit

			AddLong	DependPtr,DependRangeList,s
			AddLong	DependPtr,NewRangeList,s
			SubLong	DependSize,DependRangeList,s
			Tool		_BlockMove

			MoveLong	NewSize,[DependPtr]:#S_DependSize
			MoveLong	NewRangeList,[DependPtr]:#S_DependRangeList

			AddLong	DependRangeList,DependPtr,DependPtr
			MoveLong	CellToEnter,[DependPtr]

Exit
			RETURN
			ENDP

;-------------------------------------------------------------------------;
; S_RemoveCellFromDepList( CellToRemove:l,TargetCell:l )
;
;	S_RemoveCellFromDepList will remove a cell from another cell's dependency
;	list.

S_RemoveCellFromDepList	PROC		EXPORT
					;Using	S_CurrentData

			input	CellToRemove:l,TargetCell:l
			local	CellIndex:l
			local	CellPtr:l,DependList:l,DependPtr:l,NewDependList:l
			local	DependSize:l,NewSize:l,NextPtr:l,LastPtr:l
			local	DependRangeList:l,NewRangeList:l,CellExists:w
			error	ErrorFlag
			BEGIN

			stz		ErrorFlag
			stz		CellExists

			in		TargetCell:l
			out		CellIndex:l
			XCall	S_GetCellTableEntry

			ora		CellIndex
			beq		toExit

			lda		CellIndex+2
			and		#S_CellTableFlags
			beq		targetExists

			MoveLong	CellIndex,DependList
			bra		depListExists

	;	The TargetCell is real, so we have to de-reference it to get the
	;	DependOnMe list, if there is one.

targetExists
			inc		CellExists

			H_GetBlockPtr	CellIndex,CellPtr

			MoveWord	[CellPtr]:#S_CellDependOnMe,DependList
			MoveWord	[CellPtr]:#S_CellDependOnMe+2,DependList+2

			ora		DependList
			bne		depListExists

toExit
			brl		Exit

	;	Previous DependOnMe's exist, but we don't know how many and of
	;	of which type they are.

depListExists
			lda		DependList+2
			bpl		fullListExists

	;	Only one previous DependOnMe.

			and		#S_CellTableFlags
			cmp		#S_CellTableRangeDep
			beq		toExit

	;	It was a single reference, chk to see if it is the CellToRemove

			lda		DependList+2
			and		#$FFFF-S_CellTableFlags
			cmp		CellToRemove+2
			bne		toExit

			CmpWord	DependList,CellToRemove
			bne		toExit

	;	It was, replace it with no list.

			stzl		NewDependList
			brl		storeDepList

	;	A previous full DependOnMe list exists, chk the following.
	;	1)	Are there any single references.
	;	2)	Is there more than two references.
	;	3)	Is there two single references, or one single and one range reference.

fullListExists
			lda		DependList+2
			and		#-1-S_CellTableList
			sta		DependList+2

			H_GetBlockPtr	DependList,DependPtr

			CmpLong	[DependPtr]:#S_DependRangeList,#S_DependList
			jeq		Exit

			CmpLong	#S_DependStructSize+S_DependInc,[DependPtr]:#S_DependSize
			jne		moreThanTwo

			CmpLong	[DependPtr]:#S_DependRangeList,#S_DependList+S_DependInc
			bne		twoSingles

	;	There is one single and one range reference, if the single reference
	;	is the CellToRemove, then leave a short list (type: range)

			CmpLong	CellToRemove,[DependPtr]:#S_DependList
			jne		Exit

			MoveWord	[DependPtr]:#S_DependList+S_DependInc,NewDependList

			MoveWord	[DependPtr]:#S_DependList+S_DependInc+2,a
			ora		#S_CellTableRangeDep
			sta		NewDependList+2

			bra		killDepList

	;	There are two single references.  Check if either one is the CellToRemove.
	;	If so, leave the other in a short list. (type: single)

twoSingles
			CmpLong	CellToRemove,[DependPtr]:#S_DependList
			bne		notFirst

			MoveWord	[DependPtr]:#S_DependList+S_DependInc,NewDependList
			MoveWord	[DependPtr]:#S_DependList+S_DependInc+2,a

			bra		setSingle

notFirst
			CmpLong	CellToRemove,[DependPtr]:#S_DependList+S_DependInc
			jne		Exit

			MoveWord	[DependPtr]:#S_DependList,NewDependList
			MoveWord	[DependPtr]:#S_DependList+2,a

setSingle
			ora		#S_CellTableSingDep
			sta		NewDependList+2

killDepList
			H_DisposeBlock		DependList

	;	Determine where to store the NewDependList

storeDepList
			lda		CellExists
			beq		noCell

			MoveLong	NewDependList,[CellPtr]:#S_CellDependOnMe
			brl		Exit

noCell
			Call		S_SetCellTableEntry,in=(TargetCell:l,NewDependList:l)
			brl		Exit

	;	There are more than two DependOnMe references.  Check if the CellToRemove
	;	is in the single list, and remove it.

moreThanTwo
			MoveLong	[DependPtr]:#S_DependSize,DependSize
			MoveLong	[DependPtr]:#S_DependRangeList,DependRangeList

			AddLong	DependPtr,DependRangeList,LastPtr
			AddLong	DependPtr,#S_DependList,NextPtr

SearchLoop
			CmpLong	[NextPtr],CelltoRemove
			beq		found

			AddLong	NextPtr,#S_DependInc,NextPtr
			CmpLong	NextPtr,LastPtr
			blt		SearchLoop
			brl		Exit

found
			SubLong	DependSize,#S_DependInc,NewSize
			SubLong	DependRangeList,#S_DependInc,NewRangeList

			MoveLong	NewSize,[DependPtr]:#S_DependSize
			MoveLong	NewRangeList,[DependPtr]:#S_DependRangeList

			AddLong	NextPtr,#S_DependInc,s
			PushLong	NextPtr
			AddLong	DependPtr,DependSize,s
			SubLong	1:s,NextPtr,1:s
			Tool		_BlockMove

			H_ResizeBlock	DependList,NewSize		; resize smaller always works

Exit
			RETURN
			ENDP

;-------------------------------------------------------------------------;
; S_CalculateSheet
;
;	S_CalculateSheet will handle the calculation of all necessary cells.

S_CalculateSheet	PROC		EXPORT
				;Using	S_CurrentData

			local	RecalcIndex:l,RecalcPtr:l
			local	Cell:l,CellPtr:l,Next:l,HighFormat:w
			local	CurrentPriority:w,CurrentBucket:w,LastBucket:w
			local	LowestPriority:w,BestBucket:w
			error	ErrFlag
			BEGIN

			stz		ErrFlag

			lda		S_CurChangedList
			ora		S_CurChangedList+2
			jeq		Exit

	;	Put the changed list in the recalc list.  The recalc list is now
	;	a series of buckets, so that the sorting process will go much
	;	faster on long lists.

			Call		S_ZeroRecalcList

			MoveLong	S_CurChangedList,Next

changedLoop
			Call		D_BeachBall

			MoveLong	Next,Cell

			in		Cell:l
			out		CellPtr:l
			XCall	S_GetCellPtr

			MoveLong	[CellPtr]:#S_CellNext,Next

			lda		#0
			MoveWord	a,[CellPtr]:#S_CellNext
			MoveWord	a,[CellPtr]:#S_CellNext+2
			MoveWord	a,[CellPtr]:#S_CellPrevious
			MoveWord	a,[CellPtr]:#S_CellPrevious+2

			MoveWord	[CellPtr]:#S_CellFormat,a
			bmi		addDepList					; branch pad cells

			and		#S_CellTypeTextForm
			bne		addToRecalc					; branch all formulas

addDepList
			Call		S_AddDepListToRecalc,in=(Cell:l)
			bra		getNextCell

addToRecalc
			Call		S_AddCellToRecalcList,in=(Cell:l)

getNextCell
			CmpLong	Next,#S_EndOfList
			bne		changedLoop


			stz		S_CurChangedList
			stz		S_CurChangedList+2

	;	Start re-calculating	;

	;	Things to remember:	1)	The RecalcList must be maintained
	;							during calculation, so that New
	;							cells may be added to it.

			stz		CurrentPriority
			stz		CurrentBucket

startFindNext
			ldx		CurrentBucket

			SubWord	x,#4,a
			and		#(S_NumOfRecalcBuckets<<2)-1
			sta		LastBucket

			MoveWord	#-1,LowestPriority

loopN
			lda		S_RecalcList,x
			ora		S_RecalcList+2,x
			beq		contSearch

	;	There is a list here, check if the first cell is of the 
	;	right priority, if not, remember the lowest priority and
	;	continue the search.

			stx		CurrentBucket

			in		S_RecalcList:x:l
			out		CellPtr:l
			XCall	S_GetCellPtr

			CmpWord	[CellPtr]:#S_CellPriority,CurrentPriority
			beq		doThisCell

			ldx		CurrentBucket

			CmpWord	[CellPtr]:y,LowestPriority
			bge		contSearch

			sta		LowestPriority
			stx		BestBucket

contSearch
			cpx		LastBucket
			beq		lookedAtAll

			inc		CurrentPriority

			AddWord	x,#4,a
			and		#(S_NumOfRecalcBuckets<<2)-1
			tax
			bra		loopN

	;	We've looked at all of the buckets, and didn't find any
	;	of the right priority, on this pass.  If we saw any cells,
	;	we have remembered the lowest priority.

lookedAtAll
			CmpWord	LowestPriority,#-1
			jeq		Exit

			MoveWord	BestBucket,CurrentBucket
			tax
			
			in		S_RecalcList:x:l
			out		CellPtr:l
			XCall	S_GetCellPtr

			MoveWord	[CellPtr]:#S_CellPriority,CurrentPriority

	;	This is the next cell to recalculate.  We'll first fix up the
	;	list it is in, calculate it, and then add it's dependencies.
	;	Then we start the search loop over to find the next cell to
	;	calculate.

doThisCell
			ldx		CurrentBucket
			MoveLong	S_RecalcList:x,Cell
			MoveLong	[CellPtr]:#S_CellNext,S_RecalcList:x

			CmpLong	S_RecalcList:x,#S_EndOfList
			bne		recalcSet

			MoveWord	#0,S_RecalcList:x
			sta		S_RecalcList+2,x

recalcSet
			MoveLong	#0,[CellPtr]:#S_CellNext

			Call		S_CalculateCell,in=(Cell:l),err=ErrFlag
			bcs		calcMemError

			Call		S_AddDepListToRecalc,in=(Cell:l)

			lda		S_CurEditFlag
			and		#S_ViewFormulaBit
			bne		getNextCell2

			Call		S_RedrawCellRange,in=(Cell:l,Cell:l)

getNextCell2
			brl		startFindNext

	;	We had a memory error during recalculation.  This can happen
	;	when the result of a formula is a string, and there isn't
	;	enough memory in the heap to hold the string.

	;	We need to remember all the cells that need to be recalculated
	;	and recalculate them the next time S_CalculateSheet is called.

calcMemError
			Call		S_AddCellToRecalcList,in=(Cell:l)
			stz		CurrentBucket

errorLoop
			ldx		CurrentBucket

			lda		S_RecalcList,x
			ora		S_RecalcList+2,x
			beq		noneHere

			MoveLong	S_RecalcList:x,Cell

			in		Cell:l
			out		CellPtr:l
			XCall	S_GetCellPtr

			ldx		CurrentBucket
			MoveLong	[CellPtr]:#S_CellNext,S_RecalcList:x
			MoveLong	#0,[CellPtr]:#S_CellNext

			CmpLong	Cell,#S_EndOfList
			bne		addToCanged

			MoveWord	#0,S_RecalcList:x
			sta		S_RecalcList+2,x

addToCanged
			Call		S_AddCellToChangedList,in=(Cell:l)
			bra		errorLoop

noneHere
			AddWord	CurrentBucket,#4,a
			and		#(S_NumOfRecalcBuckets<<2)-1
			sta		CurrentBucket
			bne		errorLoop

Exit
			RETURN
			ENDP

;-------------------------------------------------------------------------;
; S_AddDepListToRecalc( Cell:l )
;
;	S_AddListToRecalc will add a cell's dependency list to the recalculate
;	list.

S_AddDepListToRecalc	PROC		EXPORT
					;Using	S_CurrentData

			input	Cell:l
			local	CellPtr:l,DepListPtr:l
			local	LastPtr:l,DependList:l
			BEGIN

			in		Cell:l
			out		CellPtr:l
			XCall	S_GetCellPtr

			ora		CellPtr
			jeq		Exit

			MoveWord	[CellPtr]:#S_CellDependOnMe,DependList
			MoveWord	[CellPtr]:#S_CellDependOnMe+2,DependList+2

			ora		DependList
			beq		Exit

			lda		DependList+2
			bpl		fullList

			and		#$FFFF-S_CellTableFlags
			pha
			PushWord	DependList
			Call		S_AddCellToRecalcList,in=(:l)
			bra		Exit

fullList
			and		#$FFFF-S_CellTableFlags
			sta		DependList+2

			H_GetBlockPtr	DependList,DepListPtr

			AddLong	DepListPtr,[DepListPtr],LastPtr			; size
			AddLong	DepListPtr,#S_DependList,DepListPtr

loop
			Call		S_AddCellToRecalcList,in=([DepListPtr]:l)

			AddLong	DepListPtr,#S_DependInc,DepListPtr
			CmpLong	DepListPtr,LastPtr
			blt		loop

Exit
			RETURN
			ENDP

;-------------------------------------------------------------------------;
; S_CalculateCell( Cell:l )
;
;	S_CalculateCell will handle the calculation of an individual cell.

S_CalculateCell	PROC		EXPORT
				;Using	S_CurrentData

			input	Cell:l
			local	CellIndex:l
			local	CellPtr:l,RefCellPtr:l,ContentPtr:l
			error	ErrFlag
			BEGIN

			stz		ErrFlag

			Call		D_BeachBall

			in		Cell:l
			out		CellIndex:l
			XCall	S_GetCellIndex

			H_GetBlockPtr	CellIndex,CellPtr

			MoveWord	[CellPtr]:#S_CellFormat,a
			bmi		Exit

			and		#S_CellTypeTextForm				; checks formula bit
			beq		Exit

			MoveWord	[CellPtr]:#S_CellFormat+2,a
			and		#S_CellCircular
			bne		Exit

			Call		S_EvalFormula,in=(CellIndex:l),err=ErrFlag

Exit
			RETURN
			ENDP

;----------------------------------------------------------------------------
;	S_CheckStack
;

S_CheckStack	PROC		EXPORT

			tsc
			pha
			AddWord	>D_MainZPage,#1024,a
			cmp		1,s					; carry set = limit ³ stack
			pla
			rtl

;----------------------------------------------------------------------------
;	S_TraverseDependOnMeTree
;
; This routine will start at the given cell and call a given routine on that
; cell and all cells in the dependency branch until/unless an abort signal is
; passed back from the traverse routine.  The traverse routine should be of
; the following form:
;
;	S_TraverseRoutine,in=(CellPtr:l),out=(ProceedCode:w),err=AbortTraverse
;

S_TraverseDependOnMeTree	PROC		EXPORT
					;Using	S_CurrentData
					;Using	S_PriorityData

			input	Cell:l,Routine:l
			local	CellPtr:l,DependList:l,DepListPtr:l,LastPtr:l
			local	OrigStack:w,NoRoomMsg:w,OverflowIndex:w,OverflowArray:l
			error	AbortTraverse
			BEGIN

			stz		AbortTraverse
			stz		NoRoomMsg
			stz		OverflowIndex
			stz		OverflowArray+2
			AddWord	>D_MainZPage,#256,OverflowArray

			tsc
			sta		OrigStack

			MoveWord	Routine,>Slot+1
			MoveWord	Routine+1,>Slot+2

startRoutine
			lda		#0
			pha
			pha
			pha
			pha

beginCell
			Call		S_CheckStack
			bcc		stackOK

			jsr		handleOverFlow
			brl		endCell

stackOK
			in		Cell:l
			out		CellPtr:l
			XCall	S_GetCellPtr

			ora		CellPtr
			jeq		endCell

	;	Make Call

			SpaceWord
			PushLong	CellPtr
slot			Call		>0
			sta		AbortTraverse
			PullWord
			jcs		cleanStack

	;	The result of the routine will tell if this branch of the tree
	;	should be traversed or skipped.

			jne		endCell

	;	Now loop on the DependOnMe list and make an iteration for each of the
	; 	the cells that depend on this cell.

			MoveWord	[CellPtr]:#S_CellDependOnMe,DependList
			MoveWord	[CellPtr]:#S_CellDependOnMe+2,DependList+2

			ora		DependList
			beq		endCell

			lda		DependList+2
			bpl		doFullList

			and		#$FFFF-S_CellTableFlags
			sta		DependList+2

			MoveLong	!DependList,DepListPtr
			stzl		LastPtr
			bra		dependLoop

doFullList
			and			#$FFFF-S_CellTableFlags
			tay
			ldx			DependList
			H_GetBlockPtr	xy,DepListPtr

			AddLong	DepListPtr,[DepListPtr],LastPtr
			AddLong	DepListPtr,#S_DependList,DepListPtr

	;	We want this routine to be iterative, not recursive, so as to use less
	;	stack space.

dependLoop
			PushLong	DepListPtr
			PushLong	LastPtr
			MoveLong	[DepListPtr],Cell
			brl		beginCell

endCell
			PullLong	LastPtr
			PullWord	DepListPtr
			PullWord	DepListPtr+2

			ora		DepListPtr
			beq		recoverOverflow

			AddLong	DepListPtr,#S_DependInc,DepListPtr
			CmpLong	DepListPtr,LastPtr
			blt		dependLoop
			bra		endCell

recoverOverflow
			lda		OverflowIndex
			beq		chkMsg

			SubWord	OverflowIndex,#4,OverflowIndex
			MoveLong	[OverflowArray]:OverflowIndex,Cell
			brl		startRoutine

	;	We jump to here if we want to abort the traversal. We need to
	;	clean up the stack.

cleanStack
			lda		OrigStack
			tcs

chkMsg
			lda		NoRoomMsg
			beq		Exit

			Call		D_AlertBox,in=(#OKBox:w,#S_DepOnMeTreeTooBig:l),out=(a:w)

Exit
			RETURN

handleOverFlow
			ldy		OverflowIndex
			cpy		#260
			bge		noRoom

			MoveLong	Cell,[OverflowArray]:y
			AddWord	OverflowIndex,#4,OverflowIndex
			rts

noRoom
			MoveWord	#1,NoRoomMsg
			rts

			ENDP

;-------------------------------------------------------------------------;
; S_CheckCellCircularity
;
;

S_CheckCellCircularity	PROC		EXPORT

			EXPORT	S_FirstCallToCheckCircularity
			EXPORT	S_OrigCell

			input	CellPtr:l
			output	ProceedCode:w
			error	AbortFlag
			BEGIN

			stz		AbortFlag

			lda		S_FirstCallToCheckCircularity
			bne		firstCall

			CmpLong	[CellPtr]:#S_CellId,S_OrigCell
			bne		noCircularity

			MoveWord	#-1,AbortFlag
			brl		exit

firstCall
			stz		S_FirstCallToCheckCircularity

noCircularity
			ldy		#S_CellFormat+2
			lda		[CellPtr],y
			and		#S_CellCircular+S_CellVisited
			bne		deadEnd

			lda		[CellPtr],y
			ora		#S_CellVisited
			sta		[CellPtr],y

			stz		ProceedCode
			bra		exit

deadEnd
			MoveWord	#1,ProceedCode

exit
			RETURN

S_FirstCallToCheckCircularity
			DS.W		1
S_OrigCell	DS.L		1

			ENDP

;-------------------------------------------------------------------------;
; S_SetCellCircular
;
;

S_SetCellCircular	PROC		EXPORT

			input	CellPtr:l
			output	ProceedCode:w
			error	AbortFlag
			BEGIN

			stz		AbortFlag

			ldy		#S_CellFormat+2
			lda		[CellPtr],y
			and		#S_CellCircular
			beq		setIt

			MoveWord	#1,ProceedCode
			bra		exit

setIt
			lda		[CellPtr],y
			and		#-1-S_CellVisited
			ora		#S_CellCircular
			sta		[CellPtr],y

	;	Clear the value of this cell	;

			ldy		#S_CellFormat
			lda		[CellPtr],y
			and		#S_CellType
			cmp		#S_CellTypeTextForm
			bne		storeZero

			lda		[CellPtr],y
			and		#$FFFF-S_CellType
			ora		#S_CellTypeFormula
			sta		[CellPtr],y

			H_DisposeBlock	[CellPtr]:#S_CellValue

storeZero
			lda		#0
			ldy		#S_CellValue
			ldx		#5

zeroLoop
			sta		[CellPtr],y
			iny
			iny
			dex
			bne		zeroLoop

	;	Draw the circ. cell	;

			MoveWord	[CellPtr]:#S_CellId+2,a
			tax
			MoveWord	[CellPtr]:#S_CellId,a
			Call		S_RedrawCellRange,in=(ax:l,ax:l)

			stz		ProceedCode

exit
			RETURN
			ENDP

;-------------------------------------------------------------------------;
; S_ClearCellVisited
;
;	The whole DependOnMe tree had visited bits set, before this routine
; started to clear them.

S_ClearCellVisited	PROC		EXPORT

			input	CellPtr:l
			output	ProceedCode:w
			error	AbortFlag
			BEGIN

			stz		AbortFlag

	;	If the visited bit is not set, then we have already cleared it,
	;	and all of the cells past this point in the DependOnMe tree have
	;	also been cleared.

			ldy		#S_CellFormat+2
			lda		[CellPtr],y
			and		#S_CellVisited
			beq		deadEnd

	;	Clear the bit.

			lda		[CellPtr],y
			and		#-1-S_CellVisited
			sta		[CellPtr],y

			stz		ProceedCode
			bra		exit

deadEnd
			MoveWord	#1,ProceedCode

exit
			RETURN
			ENDP

;-------------------------------------------------------------------------;
; S_SetCircularBits
;
;

S_SetCircularBits	PROC		EXPORT
				;Using	S_CurrentData

			input	Cell:l
			local	CellPtr:l
			BEGIN

			in		Cell:l
			out		CellPtr:l
			XCall	S_GetCellPtr

			ora		CellPtr
			beq		Exit

	;	Check if any of the cells in the formula are circular.
	;	If so, set all cells depending on this routine as circular.

			in		Cell:l,[CellPtr]:#S_CellContent:l
			out		a:w
			XCall	S_CheckForCirc
			bne		setCircularity


	;	Traverse DependOnMe checking for circularity	;

			MoveWord	#-1,S_FirstCallToCheckCircularity
			MoveLong	Cell,S_OrigCell

			in		Cell:l,#S_CheckCellCircularity:l
			XCall	S_TraverseDependOnMeTree
			bcc		clearVisited


	;	Traverse DependOnMe setting the circularity bits	;

setCircularity
			in		Cell:l,#S_SetCellCircular:l
			XCall	S_TraverseDependOnMeTree
			bra		Exit


	;	Traverse DependOnMe clearing the visited bits	;

clearVisited
			in		Cell:l,#S_ClearCellVisited:l
			XCall	S_TraverseDependOnMeTree

Exit
			RETURN
			ENDP

;-------------------------------------------------------------------------;
; S_ClearCellCircular
;
;	The whole DependOnMe tree had visited bits set, before this routine
; started to clear them.

S_ClearCellCircular	PROC		EXPORT

			input	CellPtr:l
			output	ProceedCode:w
			error	AbortFlag
			BEGIN

			stz		AbortFlag

	;	If the visited bit is not set, then we have already cleared it,
	;	and all of the cells past this point in the DependOnMe tree have
	;	also been cleared.

			ldy		#S_CellFormat+2
			lda		[CellPtr],y
			and		#S_CellCircular
			beq		deadEnd

	;	Should the bit be cleared?

			in		[CellPtr]:#S_CellId:l,[CellPtr]:#S_CellContent:l
			out		a:w
			XCall	S_CheckForCirc
			bne		deadEnd

	;	Clear the bit.

			ldy		#S_CellFormat+2
			lda		[CellPtr],y
			and		#-1-S_CellCircular
			sta		[CellPtr],y

			stz		ProceedCode
			bra		exit

deadEnd
			MoveWord	#1,ProceedCode

exit
			RETURN
			ENDP

;-------------------------------------------------------------------------;
; S_ResetCircularBits
;
;

S_ResetCircularBits	PROC		EXPORT
				;Using	S_CurrentData

			input	Cell:l
			BEGIN

			in		Cell:l,#S_ClearCellCircular:l
			XCall	S_TraverseDependOnMeTree

			RETURN
			ENDP

;-----------------------------------------------------------------------------
;	S_CheckForCirc( Cell:l, ContentIndex:l ) , CircFlag:w
;

S_CheckForCirc	PROC		EXPORT
			;Using	S_CurrentData

			input	Cell:l,ContentIndex:l
			output	CircFlag:w
			local	ContentPtr:l,NumCells:w,CurCell:w
			local	RefType:w,TargetCell:l,TargetCell2:l
			BEGIN

			stz		CircFlag

			H_GetBlockPtr	ContentIndex,ContentPtr

			lda		ContentPtr+2
			ora		ContentPtr
			jeq		Exit

			MoveWord	[ContentPtr]:#E_FormulaCells,NumCells
			MoveWord	#E_FormulaData,CurCell
			cmp		NumCells
			jge		Exit

loop
			MoveWord	[ContentPtr]:CurCell,a
			and		#$00FF
			sta		RefType

			and		#$0080
			jeq		singleRef

			iny
			in		[ContentPtr]:y:l,RefType:w,Cell:l
			out		TargetCell:l
			XCall	S_GetRealCell

			AddWord	CurCell,#5,CurCell
			tay

			MoveWord	[ContentPtr]:y,a
			and		#$00FF
			sta		RefType

			iny
			in		[ContentPtr]:y:l,RefType:w,Cell:l
			out		TargetCell2:l
			XCall	S_GetRealCell

			Call		S_NormalizeRange,in=(TargetCell:l,TargetCell2:l)

			in		#S_IsCellCircular:l	; 2 inputs already on stack
			XCall	S_TraverseRange

			bcc		nextCell
			bra		setCircFlag

singleRef
			iny
			in		[ContentPtr]:y:l,RefType:w,Cell:l
			out		TargetCell:l
			XCall	S_GetRealCell

			Call		S_IsCellCircular,in=(TargetCell:l)
			bcc		nextCell

setCircFlag
			MoveWord	#S_CellCircular,CircFlag
			bra		Exit

nextCell
			AddWord	CurCell,#5,CurCell
			cmp		NumCells
			bge		Exit
			brl		loop

Exit
			RETURN
			ENDP
			END

