
		LOAD		'Macros.dump'

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

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

		IMPORT	S_AddCellToChangedList
		IMPORT	S_DeltaMove
		IMPORT	S_FixFormula
		IMPORT	S_FixFormula2
		IMPORT	S_FixFormula3
		IMPORT	S_FixFormulaFlag
		IMPORT	S_GetCellTableEntry
		IMPORT	S_InsertLeftPadCells
		IMPORT	S_InsertRightPadCells
		IMPORT	S_MergeDependLists
		IMPORT	S_MoveDestBR
		IMPORT	S_MoveDestTL
		IMPORT	S_MoveNewCell
		IMPORT	S_MoveOldCell
		IMPORT	S_MoveSrcBR
		IMPORT	S_MoveSrcTL
		IMPORT	S_RearrangePadCells
		IMPORT	S_RemoveCellFromChangedList
		IMPORT	S_ResetCircularity
		IMPORT	S_SetCellTableEntry
		IMPORT	S_SetCircularBits
		IMPORT	S_SplitDependList
		IMPORT	S_TraverseDependFormulas

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

		ENTRY	S_ClearChangedBits
		ENTRY	S_ClearChangedBits2

;--------------------------------------------------------------------------
; S_SwapCell
;
;	For the purposes of the comments in this routine, the following 
; terminology will be used.  User cell's are those cells that the user
; thinks of as cells.  (These include labels, values, and 2 types of formulas.
; The cell type of user cells are always positive, and of non-user cells are
; always negative.)  Pad cells are those cell structures in the cell table, used
; to indicate space for overlapping labels.  Real cells will be the union of
; User and Pad cells.  (This name is used because real cell structures exist.)
; Fake cells are cell table entries that consist of dependency lists.
; (Dependency lists always have some of the high 2 bits of the cell table entry
; non-zero for detection.) Empty cells are blank cell table entries.  (For the
; purposes of this routine, empty cells treated as Fake cells because the
; code works.)

S_SwapCell	PROC		EXPORT
			;Using	S_CurrentData
			;Using	S_MoveData
			;Using	S_SwapCellFixData

			input	SrcCell:l

			local	SrcCellTableEntry:l,SrcFakeFlag:w
			local	SrcFormat:l,SrcCellType:w
			local	SrcFormulaIndex:l,SrcDependIndex:l
			local	SrcSingDepList:l,SrcRngDepList:l
			local	NewSrcEntry:l,NewSrcDepList:l

			local	DestCell:l,DestCellTableEntry:l,DestFakeFlag:w
			local	DestFormat:l,DestCellType:w
			local	DestFormulaIndex:l,DestDependIndex:l
			local	DestSingDepList:l,DestRngDepList:l
			local	NewDestEntry:l,NewDestDepList:l

			local	CellPtr:l,PreviousCell:l,NextCell:l,ClrChangedBits:w
			error	ErrorFlag
			BEGIN

	;	Initialize flag variables

			stz		ErrorFlag
			stz		SrcFakeFlag
			stz		DestFakeFlag
			stz		ClrChangedBits

	;	Determine the other cell to swap with

			AddWord	SrcCell,S_DeltaMove,DestCell
			AddWord	SrcCell+2,S_DeltaMove+2,DestCell+2

	;						;
	;	Adjust the src cell		;
	;						;

			in		SrcCell:l
			out		SrcCellTableEntry:l
			XCall	S_GetCellTableEntry

			ora		SrcCellTableEntry
			beq		fakeSrc1					; branch empty cell

			lda		SrcCellTableEntry+2
			and		#S_CellTableFlags
			beq		realSrc1					; branch real cell

	;	The following works for both fake and empty src cells.

fakeSrc1
			inc		SrcFakeFlag
			MoveWord	#S_CellTypeEmpty,SrcCellType
			MoveWord	#0,SrcFormat+2

			MoveLong	SrcCellTableEntry,SrcDependIndex
			brl		adjustDestCell

realSrc1
			H_GetBlockPtr	SrcCellTableEntry,CellPtr

			MoveLong	[CellPtr]:#S_CellContent,SrcFormulaIndex
			MoveLong	[CellPtr]:#S_CellDependOnMe,SrcDependIndex
			MoveLong	[CellPtr]:#S_CellFormat,SrcFormat
			MoveLong	[CellPtr]:#S_CellNext,NextCell
			MoveLong	[CellPtr]:#S_CellPrevious,PreviousCell

			lda		SrcFormat+2
			and		#$FFFF-S_CellCircular
			MoveWord	a,[CellPtr]:#S_CellFormat+2

			lda		SrcFormat
			and		#S_CellType
			sta		SrcCellType

			bmi		chkChangedSrc				; branch pad cell

	;	Clear any circularity, we'll reset them at the end	;

userSrc1
			lda		SrcFormat+2
			and		#S_CellCircular
			beq		chkChangedSrc

			Call		S_ResetCircularity,in=(SrcCell:l)

	;	Remove SrcCell from changed list if it is already there.	;

chkChangedSrc
			lda		NextCell+2
			ora		NextCell
			beq		adjustDestCell

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

			in		SrcCell:l,PreviousCell:l,NextCell:l
			XCall	S_RemoveCellFromChangedList

	;						;
	;	Adjust the dest cell	;
	;						;

adjustDestCell
			in		DestCell:l
			out		DestCellTableEntry:l
			XCall	S_GetCellTableEntry

			ora		DestCellTableEntry
			beq		fakeDest					; branch empty cell

			lda		DestCellTableEntry+2
			and		#S_CellTableFlags
			beq		realDest1					; branch real cell

	;	The following works for both fake and empty Dest cells.

fakeDest
			inc		DestFakeFlag
			MoveWord	#S_CellTypeEmpty,DestCellType
			MoveWord	#0,DestFormat+2

			MoveLong	DestCellTableEntry,DestDependIndex
			brl		adjustDepends

realDest1
			H_GetBlockPtr	DestCellTableEntry,CellPtr

			MoveLong	[CellPtr]:#S_CellContent,DestFormulaIndex
			MoveLong	[CellPtr]:#S_CellDependOnMe,DestDependIndex
			MoveLong	[CellPtr]:#S_CellFormat,DestFormat
			MoveLong	[CellPtr]:#S_CellNext,NextCell
			MoveLong	[CellPtr]:#S_CellPrevious,PreviousCell

			lda		DestFormat+2
			and		#$FFFF-S_CellCircular
			MoveWord	a,[CellPtr]:#S_CellFormat+2

			lda		DestFormat
			and		#S_CellType
			sta		DestCellType

			bmi		chkChangedDest				; branch pad cell

	;	Clear any circularity, we'll reset them at the end	;

userDest
			lda		DestFormat+2
			and		#S_CellCircular
			beq		chkChangedDest

			Call		S_ResetCircularity,in=(DestCell:l)

	;	Remove DestCell from changed list if it is already there.

chkChangedDest
			lda		NextCell+2
			ora		NextCell
			beq		adjustDepends

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

			in		DestCell:l,PreviousCell:l,NextCell:l
			XCall	S_RemoveCellFromChangedList



	;	Adjust src cell's DependOnMe references

adjustDepends
			lda		SrcDependIndex
			ora		SrcDependIndex+2
			beq		adjustDestDep

	;	Set up the globals used in the FixFormula routines

			stz		S_FixFormulaFlag
			MoveLong	SrcCell,S_MoveOldCell
			MoveLong	DestCell,S_MoveNewCell

	;	The first changed bit allows for formulas with references to
	;	both cells involved, and the Fix routines will still work.

	;	The second changed bit allows for circular references to itself.

			lda		DestDependIndex
			ora		DestDependIndex+2
			beq		doFixFormFlag1

			MoveWord	#S_RecentlyChangedBit,S_FixFormulaFlag
			inc		ClrChangedBits

doFixFormFlag1
			lda		SrcFormat+2
			and		#S_CellCircular
			beq		doTraverse1

			lda		#S_RecentlyChangedBit2
			tsb		S_FixFormulaFlag
			inc		ClrChangedBits

doTraverse1
			in		SrcCell:l,SrcCellTableEntry:l
			in		#S_FixFormula:l,#S_FixFormula2:l
			XCall	S_TraverseDependFormulas

	;	Adjust dest cell's DependOnMe references

adjustDestDep
			lda		DestDependIndex
			ora		DestDependIndex+2
			beq		adjustFormulas

			stz		S_FixFormulaFlag
			MoveLong	DestCell,S_MoveOldCell
			MoveLong	SrcCell,S_MoveNewCell

			lda		DestFormat+2
			and		#S_CellCircular
			beq		doTraverse2

			MoveWord	#S_RecentlyChangedBit2,S_FixFormulaFlag
			inc		ClrChangedBits

doTraverse2
			in		DestCell:l,DestCellTableEntry:l
			in		#S_FixFormula:l,#S_FixFormula2:l
			XCall	S_TraverseDependFormulas


	;	Adjust src cell's formula	;

adjustFormulas
			lda		SrcCellType
			bmi		adjustDestFormula				; branch non-user cell

			and		#S_CellTypeTextForm
			beq		adjustDestFormula				; branch non-formula cell

			MoveLong	SrcCell,S_MoveOldCell
			MoveLong	DestCell,S_MoveNewCell

			in		SrcCell:l,DestCell:l,SrcFormulaIndex:l
			XCall	S_FixFormula3


	;	Adjust dest cell's formula	;

adjustDestFormula
			lda		DestCellType
			bmi		chkClrChangedBits				; branch non-user cell

			and		#S_CellTypeTextForm
			beq		chkClrChangedBits				; branch non-formula cell

			MoveLong	DestCell,S_MoveOldCell
			MoveLong	SrcCell,S_MoveNewCell

			in		DestCell:l,SrcCell:l,DestFormulaIndex:l
			XCall	S_FixFormula3


chkClrChangedBits
			lda		SrcFakeFlag
			beq		chkClrChgBits2			; branch real cell

			in		SrcCell:l
			out		:l
			XCall	S_GetCellTableEntry

			pla
			sta		SrcCellTableEntry
			sta		SrcDependIndex

			pla
			sta		SrcCellTableEntry+2
			sta		SrcDependIndex+2

			ora		SrcCellTableEntry
			beq		chkClrDest			; branch empty cell

chkClrChgBits2
			lda		ClrChangedBits
			beq		chkClrDest

			in		SrcCell:l,SrcCellTableEntry:l
			in		#S_ClearChangedBits:l,#S_ClearChangedBits2:l
			XCall	S_TraverseDependFormulas

			lda		SrcCellType
			bmi		chkClrDest			; branch non-user cell

			and		#S_CellTypeTextForm
			beq		chkClrDest			; branch non-formula cell

			Call		S_ClearChangedBits,in=(SrcCell:l,SrcFormulaIndex:l)

chkClrDest
			lda		DestFakeFlag
			beq		chkClrDest2			; branch real cell

			in		DestCell:l
			out		:l
			XCall	S_GetCellTableEntry

			pla
			sta		DestCellTableEntry
			sta		DestDependIndex

			pla
			sta		DestCellTableEntry+2
			sta		DestDependIndex+2

			ora		DestCellTableEntry
			beq		doSwap				; branch empty cell

chkClrDest2
			lda		ClrChangedBits
			beq		doSwap

			in		DestCell:l,DestCellTableEntry:l
			in		#S_ClearChangedBits:l,#S_ClearChangedBits2:l
			XCall	S_TraverseDependFormulas

			lda		DestCellType
			bmi		doSwap				; branch non-user cell

			and		#S_CellTypeTextForm
			beq		doSwap				; branch non-formula cell

			Call		S_ClearChangedBits,in=(DestCell:l,DestFormulaIndex:l)


	;	Break up both depend lists into their single cell and range parts.
	;	The old single part will be attached to New location's range part
	;	and vise - versa.

doSwap
			lda		SrcFakeFlag
			bne		splitSrcDeps			; branch fake or empty cell

			H_GetBlockPtr	SrcCellTableEntry,CellPtr

			MoveLong	[CellPtr]:#S_CellDependOnMe,SrcDependIndex

	;	Split up the depend list.	;

splitSrcDeps
			in		SrcDependIndex:l
			out		SrcSingDepList:l,SrcRngDepList:l
			XCall	S_SplitDependList


	;	Now break up the dest depend lists into its single cell and range parts.

			lda		DestFakeFlag
			bne		splitDestDeps			; branch fake or empty cell

			H_GetBlockPtr	DestCellTableEntry,CellPtr

			MoveLong	[CellPtr]:#S_CellDependOnMe,DestDependIndex

splitDestDeps
			in		DestDependIndex:l
			out		DestSingDepList:l,DestRngDepList:l
			XCall	S_SplitDependList

	;	Merge the Src's SingDepList with the Dest's RngDepList list

			in		SrcSingDepList:l,DestRngDepList:l
			out		NewDestDepList:l
			XCall	S_MergeDependLists

	;	Merge the Dest's SingDepList with the Src's RngDepList list

			in		DestSingDepList:l,SrcRngDepList:l
			out		NewSrcDepList:l
			XCall	S_MergeDependLists

	;	The following 3 labels represent the 3 ways to set up
	;	the NewSrcEntry used in this swap.

	;	1)	Nothing left in the Src cell.  Set the depend list here.
	;		The New src is fake.
	;	2)	Pad Cell left in the Src position.  Put the depend 
	;		in the pad cell.
	;	3)	The Dest was user and is moving to the Src postion.  Set the
	;		depend in the user cell.

			lda		DestCellType
			bpl		userSrc5			; branch all <-> user

			lda		SrcFakeFlag
			bne		fakeSrc5			; branch empty,fake <-> empty,fake,pad

			lda		SrcCellType
			bmi		padSrc5			; branch pad <-> empty,fake,pad

			lda		DestFakeFlag
			bne		fakeSrc5			; branch user <-> empty,fake

	;	A User Src is moving to a Pad Dest.  The pad must be disposed of.
	;	A fake Src is left behind.

;			Call			S_RemoveCell,in=(DestCell:l)
			H_DisposeBlock	DestCellTableEntry

	;	The following 9 cases get to the following code.
	;	empty,fake,user <-> empty,fake,pad

fakeSrc5
			MoveLong	NewSrcDepList,NewSrcEntry

			lda		SrcCellType
			jpl		userDest6			; branch user <-> empty,fake,pad

			lda		DestFakeFlag
			jne		fakeDest6			; branch empty,fake <-> empty,fake

			brl		padDest6			; branch empty,fake <-> pad

;-------------------------------------

	;	The following 3 cases get to the following code.
	;			pad <-> empty,fake,pad

padSrc5
			H_GetBlockPtr	SrcCellTableEntry,CellPtr

			MoveLong	NewSrcDepList,[CellPtr]:#S_CellDependOnMe
			MoveLong	SrcCellTableEntry,NewSrcEntry

			lda		DestFakeFlag
			bne		fakeDest6			; branch pad <-> empty,fake

			bra		padDest6			; branch pad <-> pad

;-------------------------------------

	;	The following 4 cases get to the following code.
	;			all <-> user

userSrc5
			H_GetBlockPtr	DestCellTableEntry,CellPtr

			MoveLong	SrcCell,[CellPtr]:#S_CellID
			MoveLong	NewSrcDepList,[CellPtr]:#S_CellDependOnMe
			MoveLong	DestCellTableEntry,NewSrcEntry

			lda		SrcFakeFlag
			bne		fakeDest6			; branch empty,fake <-> user

			lda		SrcCellType
			bpl		userDest6			; branch user <-> user

	;	A User Dest is moving to a Pad Src.  The pad must be disposed of.
	;	A fake Dest is left behind.

;			Call			S_RemoveCell,in=(SrcCell:l)
			H_DisposeBlock	SrcCellTableEntry

	;	The following 3 labels represent the 3 ways to set up
	;	the NewDestEntry used in this swap.

	;	1)	Nothing left in the Dest cell.  Set the depend list here.
	;		The New Dest is fake.
	;	2)	Pad Cell left in the Dest position.  Put the depend 
	;		in the pad cell.
	;	3)	The Src was real and is moving to the Dest postion.  Set the
	;		depend in the real cell.


	;	The following 9 cases get to the following code.
	;			empty,fake,pad <-> empty,fake,user

fakeDest6
			MoveLong	NewDestDepList,NewDestEntry
			bra		resetIndices

;-------------------------------------

	;	The following 3 cases get to the following code.
	;			empty,fake,pad <-> pad

padDest6
			H_GetBlockPtr	DestCellTableEntry,CellPtr

			MoveLong	NewDestDepList,[CellPtr]:#S_CellDependOnMe
			MoveLong	DestCellTableEntry,NewDestEntry
			bra		resetIndices

;-------------------------------------

	;	The following 4 cases get to the following code.
	;			user <-> all

userDest6
			H_GetBlockPtr	SrcCellTableEntry,CellPtr

			MoveLong	DestCell,[CellPtr]:#S_CellID
			MoveLong	NewDestDepList,[CellPtr]:#S_CellDependOnMe
			MoveLong	SrcCellTableEntry,NewDestEntry

;-------------------------------------

	;	The following 16 cases get to the following code.
	;			all <-> all

resetIndices
			Call		S_SetCellTableEntry,in=(SrcCell:l,NewSrcEntry:l)
			Call		S_SetCellTableEntry,in=(DestCell:l,NewDestEntry:l)


	;	If cells are labels, fix pad cells: if cells are formulas, fix
	;	circularity

			lda		SrcCellType
			bpl		chkSrcCirc

			lda		DestCellType
			bpl		chkDestCirc

			brl		chkSrcChanged

chkSrcCirc
			and		#S_CellTypeTextForm
			beq		chkDestCircCheck

			Call		S_SetCircularBits,in=(DestCell:l)

chkDestCircCheck
			lda		DestCellType
			bmi		fixSrcPadCells

chkDestCirc
			and		#S_CellTypeTextForm
			beq		fixSrcPadCells

			Call		S_SetCircularBits,in=(SrcCell:l)


	;	Remove old pad cells	;

fixSrcPadCells
			in		DestCell:l
			out		:l,:l
			XCall	S_RearrangePadCells,err=ErrorFlag

			pla
			plx

			cpx		S_MoveDestBR+2
			blt		chkLeft1

			stx		S_MoveDestBR+2

chkLeft1
			pla
			plx

			cpx		S_MoveDestTL+2
			bge		fixDestPadCells

			stx		S_MoveDestTL+2

fixDestPadCells
			lda		ErrorFlag
			jne		chkSrcChanged

			in		SrcCell:l
			out		:l,:l
			XCall	S_RearrangePadCells,err=ErrorFlag

			pla
			plx

			cpx		S_MoveSrcBR+2
			blt		chkLeft2

			stx		S_MoveSrcBR+2

chkLeft2
			pla
			plx

			cpx		S_MoveSrcTL+2
			bge		chkSrcLabel

			stx		S_MoveSrcTL+2

	;	Insert New pad cells	;

chkSrcLabel
			lda		ErrorFlag
			bne		chkSrcChanged

			lda		SrcCellType
			bne		chkDestLabel	; 0 = label

			Call		S_InsertRightPadCells,in=(DestCell:l),out=(ax:l)

			cpx		S_MoveDestBR+2
			blt		chkLeft3

			stx		S_MoveDestBR+2

chkLeft3
			Call		S_InsertLeftPadCells,in=(DestCell:l),out=(ax:l)

			cpx		S_MoveDestTL+2
			bge		chkDestLabel

			stx		S_MoveDestTL+2

chkDestLabel
			lda		DestCellType
			bne		chkSrcChanged				; 0 = label

			Call		S_InsertRightPadCells,in=(SrcCell:l),out=(ax:l)

			cpx		S_MoveSrcBR+2
			blt		chkLeft4

			stx		S_MoveSrcBR+2

chkLeft4
			Call		S_InsertLeftPadCells,in=(SrcCell:l),out=(ax:l)

			cpx		S_MoveSrcTL+2
			bge		chkSrcChanged

			stx		S_MoveSrcTL+2


chkSrcChanged
			lda		DestCellTableEntry
			ora		DestCellTableEntry+2
			beq		chkDestChanged

			Call		S_AddCellToChangedList,in=(SrcCell:l)

chkDestChanged
			lda		SrcCellTableEntry
			ora		SrcCellTableEntry+2
			beq		Exit

			Call		S_AddCellToChangedList,in=(DestCell:l)

Exit			RETURN
			ENDP

;---------------------------------------------------------------------------
;
;	These routines clear the S_CellChangedBit of the cell reference's
; refbytes.

S_ClearChangedBits	PROC		EXPORT
				;Using	S_CurrentData

			input	Cell:l,FormulaIndex:l
			local	FormulaPtr:l
			local	Start:w,Stop:w
			BEGIN

			H_GetBlockPtr	FormulaIndex,FormulaPtr

			MoveWord	[FormulaPtr]:#E_FormulaCells,Stop
			MoveWord	#E_FormulaData,Start
			bra		chkEnd

dependLoop
			MoveWord	[FormulaPtr]:Start,a
			and		#$FFFF-S_RecentlyChangedBit-S_RecentlyChangedBit2
			sta		[FormulaPtr],y

			AddWord	Start,#5,Start

chkEnd
			cmp		Stop
			blt		dependLoop

			RETURN	#0
			ENDP

;----------------------------------------------------------------------------
;
;

S_ClearChangedBits2	PROC		EXPORT

			input	Cell:l,FormulaIndex:l
			output	Moved:w
			BEGIN

			stz		Moved

			Call		S_ClearChangedBits,in=(Cell:l,FormulaIndex:l)

			RETURN	Moved
			ENDP
			END

