Timer Obscure Behaviour

From GbdevWiki
Jump to: navigation, search

Timer Global Circuit

Timer simplified.png

Relation between Timer and Divider register

This is a schematic of the circuit involving TAC and DIV:

Timer tac bug dmg.png

Notice how the values that are connected to the inputs of the multiplexer are the values of those bits, not the carry of those bits. This is the reason of a few things:

- When writing to DIV, the whole counter is reseted, so the timer is also affected.

- When writing to DIV, if the current output is '1' and timer is enabled, as the new value after reseting DIV will be '0', the falling edge detector will detect a falling edge and TIMA will increase.

- When writing to TAC, if the previously selected multiplexer input was '1' and the new input is '0', TIMA will increase too. This doesn't happen when the timer is disabled, but it also happens when disabling the timer (the same effect as writing to DIV). The following code explains the behaviour in DMG and MGB.

clocks_array[4] = {1024, 16, 64, 256}

old_clocks = clocks_array[old_TAC&3]
new_clocks = clocks_array[new_TAC&3]

old_enable = old_TAC & BIT(2)
new_enable = new_TAC & BIT(2)

sys_clocks = 16 bit system counter

IF old_enable == 0 THEN
    glitch = 0 (*)
    IF new_enable == 0 THEN
        glitch = (sys_clocks & (old_clocks/2)) != 0
        glitch = ((sys_clocks & (old_clocks/2)) != 0) && ((sys_clocks & (new_clocks/2)) == 0)
    END IF

The sentence marked with a (*) has a different behaviour in GBC (AGB and AGS seem to have strange behaviour even in the other statements). When enabling the timer and maintaining the same frequency it doesn't glitch. When disabling the timer it doesn't glitch either. When another change of value happens (so timer is enabled after the write), the behaviour depends on a race condition, so it cannot be predicted for every device.

Timer Overflow Behaviour

When TIMA overflows, the value from TMA is loaded and IF timer flag is set to 1, but this doesn't happen immediately. Timer interrupt is delayed 1 cycle (4 clocks) from the TIMA overflow. The TMA reload to TIMA is also delayed. For one cycle, after overflowing TIMA, the value in TIMA is 00h, not TMA. This happens only when an overflow happens, not when the upper bit goes from 1 to 0, it can't be done manually writing to TIMA, the timer has to increment itself.

For example (SYS is the system internal counter divided by 4 for easier understanding, each increment of the graph is 1 cycle, not 1 clock):

Timer overflows:

              [A] [B]
SYS  FD FE FF |00| 01 02 03
TIMA FF FF FF |00| 23 23 23
TMA  23 23 23 |23| 23 23 23
IF   E0 E0 E0 |E0| E4 E4 E4

Timer doesn't overflow:

SYS  FD FE FF 00 01 02 03
TIMA 45 45 45 46 46 46 46
TMA  23 23 23 23 23 23 23
IF   E0 E0 E0 E0 E0 E0 E0

- During the strange cycle [A] you can prevent the IF flag from being set and prevent the TIMA from being reloaded from TMA by writing a value to TIMA. That new value will be the one that stays in the TIMA register after the instruction. Writing to DIV, TAC or other registers won't prevent the IF flag from being set or TIMA from being reloaded.

- If you write to TIMA during the cycle that TMA is being loaded to it [B], the write will be ignored and TMA value will be written to TIMA instead.

- If TMA is written the same cycle it is loaded to TIMA [B], TIMA is also loaded with that value.

- This is a guessed schematic to explain the priorities with registers TIMA and TMA:

Timer tima tma detailed.png

TMA is a latch. As soon as it is written, the output shows that value. That explains that when TMA is written and TIMA is being incremented, the value written to TMA is also written to TIMA. It doesn't affect the IF flag, though.