;***************************************************************************
;
;                     Cia Timer Example
;                 (with OS legal StartTimer)
;          Converted from the Dev CD example Cia_Interval.c
;                      by Anton Reinauer.
;
;***************************************************************************

;           You need Amigalibs.res in options
; Note that using AbleICR_ and SetICR_ to start the timer, only
; fires the Timer once! You'd have to hit the hardware to set it
; to continuous mode, so nullifying the whole point of doing it
; OS legal in the first place!

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{a.l,b} ; this starts timer firing using the CIA resource
  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

  TST.w  d0
  BNE bit1
  MOVE #$81,d0              ; set bit 0 -Timer A, and bit 7 -enable timer
  BRA bit2

  bit1:
  MOVE #$82,d0              ; set bit 1 -Timer B, and bit 7 -enable timer

  bit2:
  MOVE d0,d2
  JSR -18(a6)               ; jump to AbleICR_ in CIA lib

  MOVE d2,d0                ; restore mask
  JSR -24(a6)               ; jump to SetICR_ in CIA lib

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

  AsmExit
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{ft\ciabase,ft\timerbit}    ; 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!



