Page retrieved from the Internet Archive Wayback Machine

GameBoy Timer

The GameBoy timer is an excellent way to set and dynamically change the speed of your application's main loop. Here's a tutorial on how to use the timer effectively for this purpose. The techniques here are demonstrated in both hello-window.asm and hello-angle.asm.

notes on the timer

Application

A good way to use the timer is to have the timer interrupt trigger the main loop to cycle one iteration. Then you can adjust the speed between timer interrupts to set the number of iterations in your main loop:

  1. add a call to a timer interrupt routine where the timer interrupt is called:

    SECTION "Timer_Overflow",HOME[$0050]
            jp      TimerInterrupt          ; flag the timer interrupt 
  2. add some constants which describe the number of times you wish for the interrupt to be generated:

    TimerHertz      EQU     TACF_START|TACF_4KHZ
    TimerClockDiv   EQU     256-30                          ; divide clock by 30 
    • in this example, the timer interrupt will be generated 4096/30 times each second
  3. whereever you already load rIE and enable interrupts in your code, add IEF_TIMER to the list of interrupts you are enabling. For example, if you wanted to enable the vblank and timer interrupts, you would have in your initialization code:

            ld      a, IEF_VBLANK|IEF_TIMER
            ld      [rIE],a                 ; ENABLE VBLANK AND TIMER INTERRUPT
            ei                              ; LET THE INTS FLY 
  4. if you don't already have one, add a variable in your code whose bits can be used as flags to know what interrupts have been generated:

            LoByteVar       IFlags  ; flag var for interrupts 
  5. add your timer interrupt routine to your code. The routine's only purpose is to set a flag in IFlags and to reset the timer:

    ; TimerInterrupt is the routine called when a timer interrupt occurs.
    TimerInterrupt: 
            push    af                      ; save a
            ld      a,TimerClockDiv         ; load number of counts of timer
            ld      [rTMA],a                
            ld      a,TimerHertz            ; load timer speed
            ld      [rTAC],a
            ld      a,IEF_TIMER
            ld      [IFlags],a
            pop     af                      ; restore a. Everything has been preserved.
            reti 
  6. add a manual call to the timer interrupt routine after you have enabled interrupts and before your main loop. This will turn the timer on:

            call    TimerInterrupt          ; turn on the timer 
  7. start your application's main loop with logic to look at the flag to know whether or not the timer interrupt occurred. The loop:
    • halts, until an interrupt occurs
    • when an interrupt does occur, it looks at the IFlags variable to see if the interrupt the occurred was the timer interrupt
    • if the interrupt was not a timer interrupt, it loops back to halt, waiting again for an interrupt.
    • if the interrupt was a timer interrupt, it resets the timer flag in IFlags variable and continues with the main loop.
    • the code for all of this might look like:

      MainLoop:
              halt
              nop
              ld      a,[IFlags]              ; sit and wait
              cp      IEF_TIMER               ; unless the timer caused the interrupt
              jr      nz,MainLoop
              and     ~IEF_TIMER              ; reset the timer flag to catch the next
              ld      [IFlags],a              ; timer interrupt
      ...the rest of your loop goes here. We only get here if there has been a timer interrupt 

Tips on the Main Loop

The Big Lie

Unless the timer interrupt is the only interrupt you have enabled, it isn't actually true that you can be sure how many timer interrupts will be generated in a second. This is because when the GameBoy is servicing an interrupt, interrupts are disabled. I think if the timer overflows while another interrupt (such as VBLANK) is being serviced, the timer interrupt is "lost" but I have not actually confirmed this. The alternative would be that perhaps the timer interrupt is generated immediately on return from the VBLANK interrupt...but...I doubt it. Anyway, for our non-time-critical uses (we aren't doing precision timing of digital electronics here) we can think of the timer interrupt as being consistent and predictable.

ECE238Spr08/tutorials/Timer (last edited 2008-04-15 23:19:25 by JohnHarrison)