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