Interrupt timer for the C64

I used an interrupt timer recently to control the speed of my game loop. I started with a routine from the fantastic book Machine Language Routines for the Commodore 64 and 128. The routine below is a modified 8-bit version.

The timer routine works by storing it’s address at $0314 – the vector to IRQ interrupt. This vector is called 60 times per second and by default drives a routine which updates the clock, blinking cursor and other processes. By installing our own routine at this vector we can set up a counter to control the speed of a game loop.

Here is an example of the timer routine and interrupt setup:

set_intertupt_delay  
        sei             ;disable IRQ interrupts to change IRQ vector
                        ;then store the address of our routine into the IRQ
        lda #<delay_routine     ;insert our routine
        sta IRQVEC      
        lda #>delay_routine 
        sta IRQVEC+1     
        lda #DELAY      ;init DELCTR low byte first
        sta DELCTR
        cli             ;IRQ is set now so reenable it
        rts             ;finished setup

We start by disabling interrupts (sei). Then point the irq vector to our delay routine followed by initializing our counter. Now we can restore interrupts.

The delay routine is simple for this example:

delay_routine     
        lda DELCTR
        beq exit        ;when counter is zero stop decrementing
        dec DELCTR      ;decrement the counter
exit
        jmp IRQNOR      ;service the standard IRQ routines

The delay_routine is called once every 60th of a second. Each call the delay counter is decremented until it reaches zero. We also need to ensure that the default interrupt routines get called with the last jmp opcode.

Putting it all together, the following program draws each character from code 255 to zero with a delay between each. The code uses CBM prg studio’s format:

*=$0801

        BYTE    $0E, $08, $0A, $00, $9E, $20, $28,  $34, $30, $39, $36, $29, $00, $00, $00

IRQVEC = $314           ;pointer to a routine that is executed every 1/60 sec
IRQNOR = $ea31          ;irq interrupt entry point          
DELAY = 5               ;5/60 second delay
DELCTR  byte 0          ;counter storage

*=$1000

init    jsr set_intertupt_delay      ;set up the interrrupt delay
        ldy #255        ;starting character code
        sty $0400       ;screen char 0
loop    lda DELCTR               
        bne loop        
        lda #DELAY      ;delay reached 0, reset it
        sta DELCTR
        dey
        sty $0400       ;print to screen's first character
        bne loop        ;printer all the characters?
        rts             ;finished

                        ;place the delay routine into the IRQ vector
                        ;init the control flag and time interval
set_intertupt_delay  
        sei             ;disable IRQ interrupts to change IRQ vector
                        ;then store the address of our routine into the IRQ
        lda #<delay_routine     ;insert our routine
        sta IRQVEC      
        lda #>delay_routine 
        sta IRQVEC+1     
        lda #DELAY      ;init DELCTR low byte first
        sta DELCTR
        cli             ;IRQ is set now so reenable it
        rts             ;finished setup

delay_routine     
        lda DELCTR
        beq exit        ;when counter is zero stop decrementing
        dec DELCTR      ;decrement the counter
exit
        jmp IRQNOR      ;service the standard IRQ routines

Leave a Reply

Your email address will not be published. Required fields are marked *