Back in the early 90s I failed writing (and understanding) a CIA timer interrupt. This bugged me ever since.
Now with the help of the internet and especially some posts of this board finally I got it done. For this reason I want to share these bits. May it help others.
This code lacks of proper error checking and it could be more elegant. It only tries to allocate the timers from CIA-B.
Code:
; a os friendly CIA timer interrupt
; roughly translated from the example in the reference manual
; main loop switches the power led in a 1s interval
; tab-size = 4
include "exec/types.i"
include "exec/funcdef.i" ; keep code simple and
include "exec/exec.i" ; the includes!
include "exec/exec_lib.i"
include "exec/io.i"
include "exec/nodes.i"
include "exec/tasks.i"
include "exec/interrupts.i"
include "resources/cia.i"
include "hardware/cia.i"
execbase equ 4
ciab equ $bfd000
AddICRVector equ -6
RemICRVector equ -12
AbleICR equ -18
SetICR equ -24
CLEAR equ 0
section code
_start:
bsr myTimerInstall
tst.l d0 ; 0 = successful
bne.b .error_exit
move.l t10ms,d7 ; we start
.loop1:
move.l t10ms,d0
sub.l d7,d0 ; substract now from start
cmp.l #100,d0 ; 100*10ms = 1s
blt.b .next
bchg #1,$bfe001
move.l t10ms,d7 ; a new start
.next:
btst #6,$bfe001
bne.b .loop1
bsr myTimerRemove
.clean_exit:
moveq #0,d0
rts
.error_exit:
moveq #20,d0
rts
;-------------------------------------------------------------------------------
; CIA-A ($bfe001) serial/keyboard and timer.device
; CIA-B ($bfd000) both timer are free
; CIA-B is interrupt Level 6!
; one tick = 1.396825ms (clock interval is 279.365 nanoseconds)
; .715909 Mhz NTSC; .709379 Mhz PAL
; PAL 7093,79 ~ 7094 = 10ms | NTSC 7159,09 ~ 7159 = 10ms
TIMER_INTERVAL equ 7094
;TIMER_INTERVAL equ 16000
myTimerInstall:
move.l execbase.w,a6
moveq #0,d0
lea ciab_name,a1 ; "ciab.resource"
jsr _LVOOpenResource(a6) ; obtain ciab resource
tst.l d0 ; 0 = error
beq .exit
move.l d0,cia_resource
move.l d0,a6
moveq #2,d5 ; which timer
moveq #CIAICRB_TB,d0 ; timer B
lea myTimerInterruptStruct,a1 ; my interrupt struct
lea myTimerInterruptName,a5 ; store name
move.l a5,LN_NAME(a1)
lea myTimerInterrupt,a5 ; store ptr to my routine
move.l a5,IS_CODE(a1)
jsr AddICRVector(a6) ; install interrupt (not starting)
tst.l d0 ; 0 = successful
beq.b .set_timer
moveq #1,d5
moveq #CIAICRB_TA,d0 ; try timer A
lea myTimerInterruptStruct,a1
jsr AddICRVector(a6)
tst.l d0 ; 0 = succesful
bne.b .error_exit
; now set timer
.set_timer:
move.w d5,cia_timer ; which timer do we got?
lea ciab,a5 ; $bfd000
cmp.w #1,d5 ; 1 = timer A | 2 = timer B
bgt.b .set_timer_b
.set_timer_a:
move.b #TIMER_INTERVAL&$ff,ciatalo(a5) ; low byte
move.b #TIMER_INTERVAL>>8,ciatahi(a5) ; high byte
bra.b .exit
.set_timer_b:
move.b #TIMER_INTERVAL&$ff,ciatblo(a5) ; low byte
move.b #TIMER_INTERVAL>>8,ciatbhi(a5) ; high byte
.exit:
bsr StartTimer
moveq #0,d0
rts
.error_exit:
moveq #-1,d0
rts
;-------------------------------------------------------------------------------
StartTimer
move.l cia_resource,a6 ; ciab.resource
lea ciab,a5 ; $bfd000
move.w cia_timer,d0
cmp.w #1,d0
bgt.b .timer_b
.timer_a:
bclr #CIACRAB_RUNMODE,ciacra(a5) ; continous
bset #CIACRAB_START,ciacra(a5)
move.w #CLEAR|CIAICRF_TA,d0
jsr SetICR(a6)
move.w #CIAICRF_SETCLR|CIAICRF_TA,d0
jsr AbleICR(a6)
bra.b .exit
.timer_b:
bclr #CIACRBB_RUNMODE,ciacrb(a5) ; continous
bset #CIACRBB_START,ciacrb(a5)
move.w #CLEAR|CIAICRF_TB,d0
jsr SetICR(a6)
move.w #CIAICRF_SETCLR|CIAICRF_TB,d0
jsr AbleICR(a6)
.exit:
rts
;-------------------------------------------------------------------------------
StopTimer
move.l cia_resource,a6 ; ciab.resource
lea ciab,a5 ; $bfd000
move.w cia_timer,d0
cmp.w #1,d0
bgt.b .timer_b
.timer_a:
move.w #CLEAR|CIAICRF_TA,d0 ; stop timer A
jsr AbleICR(a6)
move.b #CIACRAF_START,d0
not.b d0
and.b d0,ciacra(a5) ; clear start bit in CRA
bra.b .exit
.timer_b:
move.w #CLEAR|CIAICRF_TB,d0 ; stop timer B
jsr AbleICR(a6)
move.b #CIACRAF_START,d0
not.b d0
and.b d0,ciacrb(a5) ; clear start bit in CRB
.exit:
rts
;-------------------------------------------------------------------------------
; timer should be stopped before
myTimerRemove:
bsr StopTimer
move.l cia_resource,a6 ; ciab.resource
lea ciab,a5 ; $bfd000
move.w cia_timer,d0
cmp.w #1,d0
bgt.b .timer_b
.timer_a:
moveq #CIAICRB_TA,d0 ; try timer A
lea myTimerInterruptStruct,a1 ; my interrupt struct
jsr RemICRVector(a6)
bra.b .exit
.timer_b:
moveq #CIAICRB_TB,d0 ; try timer A
lea myTimerInterruptStruct,a1 ; my interrupt struct
jsr RemICRVector(a6)
.exit:
rts
;-------------------------------------------------------------------------------
; FINALLY, it works - 29-06-13
myTimerInterrupt:
;tst.w 8(a1)
;beq.b .exit
movem.l d2-d7/a2-a6,-(sp)
add.l #1,t10ms
movem.l (sp)+,d2-d7/a2-a6
moveq #1,d0
.exit:
rts
section data
cia_resource: dc.l 0
cia_timer: dc.w 0 ; 1 = timer A, 2 = timer B
ciab_name:
CIABNAME
cnop 0,4
myTimerInterruptName:
dc.b "HPN timer",0,0
cnop 0,4
myTimerInterruptStruct:
dc.l 0 ; LN_SUCC
dc.l 0 ; LN_PRED
dc.b NT_INTERRUPT ; LN_TYPE
dc.b 127 ; LN_PRI
dc.l 0 ; LN_NAME ID String c-string
dc.l 0 ; IS_DATA
dc.l 0 ; IS_CODE
t10ms: dc.l 0 ; every 10ms a tick