        TTL   => TickEvents

; There are two (centisecond) ticker SWIs :
; SWI CallAfter calls the given address once, after the given number of ticks
; SWI CallEvery  "     "    "    "      every N centiseconds

; In : R0 is (signed) number of centiseconds
;      R1 is address to call
;      R2 is value of R12 to pass to code

CallAfter_Code   ROUT
       MOV       R10, #0
TickTockCommon
       Push     "R0-R3, lr"
       CMP       R0, #0
       BLE       %FT99

       MOV       R3, #TickNodeSize
       BL        ClaimSysHeapNode
       BVS       %FT97

       MOV       R3, R2
       LDMFD     stack, {r0-r2}
       STR       R1,  [R3, #TickNodeAddr]
       STR       R10, [R3, #TickNodeRedo]
       STR       R2,  [R3, #TickNodeR12]
       MOV       R1, R3

       BL        InsertTickerEvent

       Pull     "R0-R3, lr"
       ExitSWIHandler

99     ADR       R0, ErrorBlock_BadTime
97
       Pull     "R0-R3, lr"
       B        SLVK_SetV

       MakeErrorBlock BadTime

CallEvery_Code
       MOV       R10, R0
       B         TickTockCommon

; Data structure :
; chain of nodes
;
;       +----------------------------------+
;       |   Link to next node or 0         |
;       +----------------------------------+
;       |   Reload flag                    |
;       +----------------------------------+
;       |   Address to call                |
;       +----------------------------------+
;       |   Value of R12                   |
;       +----------------------------------+
;       |   No of ticks to go before call  |
;       +----------------------------------+
;
; The head node's no of ticks is decremented until 0
;  Subsequent nodes contain the no of ticks to wait when they reach the
;  chain head.
; If the reload flag is non-0, the node is reinserted at that number of ticks
; down the chain after every use.

             ^  0
TickNodeLink #  4        ;
TickNodeRedo #  4        ; These are together
TickNodeAddr #  4        ;
TickNodeR12  #  4        ;  so can LDM them

TickNodeLeft #  4

TickNodeSize #  0

InsertTickerEvent   ROUT
; R1 is node pointer, R0 ticks to wait
; R10-R12 corrupted

          Push    "R0"
          MOV      r10, pc
          ORR      r10, r10, #I_bit
          TEQP     r10, #0
          LDR      R10, =TickNodeChain

01        MOV      R11, R10
          LDR      R10, [R11, #TickNodeLink]
          CMP      R10, #0
          BEQ      %FT02                  ; end of chain
          LDR      R12,  [R10, #TickNodeLeft]
          SUBS     R0, R0, R12
          BGE      %BT01
          ADD      R0, R0, R12
          SUB      R12, R12, R0
          STR      R12,  [R10, #TickNodeLeft]

02        STR      R1, [R11, #TickNodeLink]
          STR      R0,  [R1, #TickNodeLeft]
          STR      R10, [R1, #TickNodeLink]

          Pull    "R0"
          MOVS     PC, lr

ProcessTickEventChain  ROUT
; R0-R3, R10-R12 corruptible
          LDR      R3, =TickNodeChain

          LDR      R1, [R3, #MetroGnome-TickNodeChain]
          ADD      R1, R1, #1
          STR      R1, [R3, #MetroGnome-TickNodeChain]

          LDR      R1, [R3, #TickNodeLink]
          CMP      R1, #0
          MOVEQ    PC, lr
          LDR      R2, [R1, #TickNodeLeft]
          SUBS     R2, R2, #1
          STR      R2, [R1, #TickNodeLeft]
          MOVPL    PC, lr                 ; nothing to call yet.

          Push    "lr"

01        LDMIA    R1, {R2, R10, R11, R12}   ; load next ptr, redo state,
                                            ;     address and R12val
          STR      R2, [R3]                 ; delink from chain
          MOV      R14, PC                  ; in case callee goes bang!
          MOV      PC, R11                  ; call

          LDR      R0, [R1, #TickNodeRedo]
          CMP      R0, #0
          BLGT     InsertTickerEvent
          BGT      %FT10
          TEQP     PC, #SVC_mode+I_bit
          MOV      R0, #HeapReason_Free
          Push    "R2, lr"
          MOV      R2, R1
          LDR      R1, =SysHeapStart
          SWI      XOS_Heap
          Pull    "R2, lr"
          TEQP     PC, #IRQ_mode+I_bit
10        CMP      R2, #0
          BEQ      %FT02
          LDR      R0, [R2, #TickNodeLeft]
          CMP      R0, #0
          MOVLE    R1, R2
          BLE      %BT01
02
          Pull    "PC"


RemoveTickerEvent_Code
; R0 is address of code to remove, R1 the R12 value
          TEQP     PC, #SVC_mode+I_bit
          LDR      R10, =TickNodeChain
01        LDR      R11, [R10]
          CMP      R11, #0
          ExitSWIHandler EQ
          LDR      R12, [R11, #TickNodeAddr]
          CMP      R12, R0
          LDREQ    R12, [R11, #TickNodeR12]
          CMPEQ    R12, R1
          MOVNE    R10, R11
          BNE      %BT01

          Push    "R0-R2, lr"
          MOV      R2, R11
          LDR      R11, [R11, #TickNodeLink]
          STR      R11, [R10]
          MOV      R0, #HeapReason_Free
          LDR      R1, =SysHeapStart
          SWI      XOS_Heap
          Pull    "R0-R2, lr"
          B        %BT01

ReadMetroGnome
          MOV      R0, #0
          LDR      R0, [R0, #MetroGnome]
          ExitSWIHandler

          LNK      NewReset
