gammpei's blog

RSS feed My GitHub

How to write a Game Boy emulator – Part 10: The timer

Posted on 2018-03-25

This post is part of a blog series about writing a Game Boy emulator.

AntonioND has written the best explanation I've seen for the timer in The Cycle-Accurate Game Boy Docs . I copied the diagram below from his docs.

Schematics of the timer (source).

Everything starts with the system clock. The system clock is a 16-bit counter that is incremented every clock cycle.

There are four memory locations associated with the timer:

AddressAbbreviationName
0xFF04DIVDivider register
0xFF05TIMATimer counter
0xFF06TMATimer modulo
0xFF07TACTimer control

DIV always returns the highest 8 bits of the system clock. If DIV is written to, the system clock gets reset to 0.

TIMA is the timer counter. TAC controls how often TIMA gets incremented in relation to the system clock. When TIMA overflows, the timer interrupt gets requested, and TMA is copied into TIMA.

TMA is the timer modulo. When TIMA overflows, TMA is copied into TIMA.

TAC controls how often TIMA gets incremented in relation to the system clock. The highest 5 bits of TAC are always 1. Bits 0 and 1 (TAC.Freq) control the speed at which TIMA is incremented. Bit 2 (TAC.Enable) determines if TIMA gets incremented.

This is where the diagram comes in handy. TAC.Freq selects one of four bits in the system clock:

TAC.FreqBit in the system clock
0b009
0b013
0b105
0b117

The selected bit is ANDed with TAC.Enable. The resulting bit is fed into a falling edge detector. When the detector detects that the bit goes from 1 to 0 (the falling edge), it increments TIMA.

Test roms

There's a chicken-and-egg problem with the timer. You can't really test the timer without implementing instruction timing, and you can't really test instruction timing without the timer.

At this point, your emulator should pass this test rom:

.gb file https://gekkio.fi/files/mooneye-gb/latest/tests/acceptance/timer/div_write.gb
SHA-256 hash 47f1b9e1ddff588aa44fd042a2f3ee2beec6e138f88dcb58009ee3e840d75035
Source code https://github.com/Gekkio/mooneye-gb/blob/8cebbc21b34e1347b608c9a5334362ebe1859150/tests/acceptance/timer/div_write.s
Expected output

The code

I have chosen to keep my implementation of the timer simple for the moment. It does not try to implement all the obscure behaviors described in AntonioND's docs.

func (st *st) addCycles(cycles int) {
	for i := 0; i < cycles; i++ {
		st.timing.cycles++

		// Update timer.
		st.timing.systemClock++
		TAC := st.readMem(0xFF07) // TAC: Timer control
		TAC_Freq := TAC & 0x03
		TAC_Enable := getBit(TAC, 2)
		systemClockBit := getBit_u16(st.timing.systemClock, [4]uint{9, 3, 5, 7}[TAC_Freq])
		timerBit := systemClockBit && TAC_Enable
		if st.timing.delayedTimerBit && !timerBit { // Falling edge.
			TIMA := st.readMem(0xFF05) // TIMA: Timer counter
			TIMA++
			if TIMA == 0x00 { // If the timer overflowed.
				st.requestInterrupt(2)    // Request timer interrupt.
				TMA := st.readMem(0xFF06) // TMA: Timer modulo
				TIMA = TMA
			}
			st.writeMem(0xFF05, TIMA)
		}
		st.timing.delayedTimerBit = timerBit
	}
}

See the code/commit on GitHub.