antoine-source/appleworksgs/SS/Src/CellEval.aii
2023-03-04 03:45:20 +01:00

1 line
30 KiB
Plaintext
Executable File
Raw Permalink Blame History

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 <20> 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