;***************************************************************************
;
;                     Cia Timer Example
;          Converted from the Dev CD example Cia_Interval.c
;                      by Anton Reinauer.
;
;***************************************************************************

; You need Amigalibs.res in options

DEFTYPE.w

#LOCOUNT=$ff  ; (max $FF) Change these bytes to make the Timer
#HICOUNT=$00  ; (max $FF) fire faster or slower.

#STOPA_AND= #CIACRAF_TODIN | #CIACRAF_PBON | #CIACRAF_OUTMODE | #CIACRAF_SPMODE  ; CIA, A timer Stop mask (and sets it up)
#STOPB_AND= #CIACRBF_ALARM | #CIACRBF_PBON | #CIACRBF_OUTMODE                    ; CIA, B timer Stop mask (and sets it up)
#STARTA_OR= #CIACRAF_START          ; CIA, A timer Start mask
#STARTB_OR= #CIACRBF_START          ; CIA, B timer Start mask
ciaa.l=$bfe001 ; Cia-A hardware base
ciab.l=$bfd000 ; Cia-B   ""      ""

PreferCIA_A=True     ; do we try for a timer on CIA-A first?

NEWTYPE .freetimer
ciabase.l     ; Cia library base
timerbit.l    ; Timer A or B?
cia.l         ; CIA hardware base
ciacr.l       ; pointer to CIA control register
cialo.l       ; pointer to CIA low byte register
ciahi.l       ; pointer to CIA high byte register
timerint.Interrupt  ; Our Interrupt structure
stopmask.b          ; mask for stopping interrupt (and setting correct modes)
startmask.b         ; mask for starting interrupt
End NEWTYPE

DEFTYPE .freetimer ft  ; our timer structure
interrupt_count.l=0    ; out timer count variable

;******************     Functions   ***************************

.StartTimer
Statement StartTimer{}      ; this starts timer firing
  SHARED ft

  *cia.CIA=ft\cia

  If ft\timerbit=#CIAICRB_TA       ; if Timer A
    ft\ciacr=&*cia.CIA\ciacra      ; control register
    ft\cialo=&*cia.CIA\ciatalo     ; low byte register
    ft\ciahi=&*cia.CIA\ciatahi     ; high byte register

    ft\stopmask=#STOPA_AND
    ft\startmask=#STARTA_OR
  Else                             ; Else Timer B
    ft\ciacr=&*cia.CIA\ciacrb      ; control register
    ft\cialo=&*cia.CIA\ciatblo     ; low byte register
    ft\ciahi=&*cia.CIA\ciatbhi     ; high byte register

    ft\stopmask=#STOPB_AND
    ft\startmask=#STARTB_OR
  EndIf

  Disable_

  a.b=Peek.b(ft\ciacr)     ; grab original mask
  a= a  & ft\stopmask      ; AND it with stopmask
  Poke.b  ft\ciacr,a       ; stop timer and set correct modes

  Enable_

  Poke.b ft\cialo,#LOCOUNT   ; } Set how fast timer fires
  Poke.b ft\ciahi,#HICOUNT   ; }

  Disable_

  a.b=Peek.b(ft\ciacr)     ; grab original mask
  a= a  | ft\startmask     ; OR it with startmask
  Poke.b ft\ciacr,a        ; start timer firing

  Enable_

End Statement

.FreeTimer
Statement FreeTimer{a.l,b,c.l}         ; this deallocates the timer resource and stops interrupt
  MOVE.l a6,-(a7)           ; save A6

  MOVE.l d0,a6              ; cia_resource  A6
  CLR.l  d0
  MOVE.w d1,d0              ; iCRBit  D0   - Timer A or B
  MOVE.l d2,a1              ; pointer to interrupt structure  A1

  JSR -12(a6)               ; jump to RemICRVector_ in CIA lib

  MOVE.l (a7)+,a6           ; restore A6
  AsmExit
End Statement

.TryTimer                   ; this attempts to allocate a specific timer with AddICRVector_
Function.l TryTimer{a.l,b,c.l}      ; attempt to allocate timer
  MOVE.l a6,-(a7)           ; save A6

  MOVE.l d0,a6              ; cia_resource  A6
  CLR.l  d0
  MOVE.w d1,d0              ; iCRBit  D0   - Timer A or B
  MOVE.l d2,a1              ; pointer to interrupt structure  A1

  JSR -6(a6)                ; jump to AddICRVector_ in CIA reesource
                            ; d0 returns 0 if the call succeeds, or pointer to interrupt structure of owner task if it fails.
  MOVE.l (a7)+,a6           ; restore A6
  AsmExit
End Function

.FindFreeTimer             ; this attempts to find a free timer out of the 4 available (2 (A & B) on each CIA chip)
Function FindFreeTimer{preferA}
  SHARED ft,ciaa,ciab

  ciaabase.l= OpenResource_("ciaa.resource")  ; pointer to the CIA A resource (lib)
  ciabbase.l= OpenResource_("ciab.resource")  ; pointer to the CIA B resource (lib)

  If preferA=True        ; try for a CIA-A timer first ?
    ft\ciabase=ciaabase  ; resource (library) address
    ft\cia=ciaa          ; hardware address
  Else
    ft\ciabase=ciabbase  ; resource (library) address
    ft\cia=ciab          ; hardware address
  EndIf

  If TryTimer{ft\ciabase,#CIAICRB_TA,&ft\timerint}=False      ; try for Timer A
    ft\timerbit=#CIAICRB_TA
    Function Return True
  Else
    If TryTimer{ft\ciabase,#CIAICRB_TB,&ft\timerint}=False    ; try for Timer B
      ft\timerbit=#CIAICRB_TB
      Function Return True
    EndIf
  EndIf

  If preferA=False       ; try for a CIA-A timer first ?
    ft\ciabase=ciaabase  ; library address
    ft\cia=ciaa          ; hardware address
  Else
    ft\ciabase=ciabbase  ; library address
    ft\cia=ciab          ; hardware address
  EndIf

  If TryTimer{ft\ciabase,#CIAICRB_TA,&ft\timerint}=False    ; try for Timer A
    ft\timerbit=#CIAICRB_TA
    Function Return True
  Else
    If TryTimer{ft\ciabase,#CIAICRB_TB,&ft\timerint}=False  ; try for Timer B
      ft\timerbit=#CIAICRB_TB
      Function Return True
    EndIf
  EndIf

  Function Return False
End Function


;********************  Main  **********************************

.Main

  ; Set up our OS Interrupt structure for the CIA resource
  ft\timerint\is_Node\ln_Type=#NT_INTERRUPT
  ft\timerint\is_Node\ln_Pri=0
  a.s="Blitz_CIA_Example"              ; name of our code (owner of Interrupt)
  ft\timerint\is_Node\ln_Name=&a.s     ; pointer to name

  ft\timerint\is_Data=&interrupt_count ; pointer to our variable
  ft\timerint\is_Code=?Interrupt       ; pointer to our interrupt
                                       ; code

  If FindFreeTimer{PreferCIA_A}=True
    If ft\cia=ciaa
      Print "CIA-A timer "
    Else
      Print "CIA-B timer "
    EndIf

    If ft\timerbit=#CIAICRB_TA
      NPrint "A allocated"
    Else
      NPrint "B allocated"
    EndIf

    StartTimer{}    ; we found a free timer- start it running

    For i.l= 1 To 50
      WaitTOF_
      NPrint Str$(interrupt_count)     ; print out value of Interrupt count
    Next

   ; Delay_(500)         ; use to test CPU use- and comment out the above loop
   ; NPrint Str$(interrupt_count)

    FreeTimer{ft\ciabase,ft\timerbit,&ft\timerint}   ; de-allocate the Timer so other programs can use it.

  Else
    NPrint "No CIA timer availiable."
  EndIf

;****************************************************************

MouseWait

End


;****************************************************************

.Interrupt            ; this is the routine which gets executed each time the Timer fires.
  MOVE.l a0,-(a7)        ; save register

  MOVE.l (a1),a0         ; our data (or pointer to it) is in A1 already
  ADD.l #1,a0            ; add 1 to the interrupt_count variable
  MOVE.l a0,(a1)         ; save it back out

  MOVE.l (a7)+,a0        ; restore register
RTS    ; for some reason it's an RTS- the docs say exceptions should end in RTEs - don't ask me!



