Skip to content
RetrogamingDeep Dive Published Updated 8 min readViews unavailable

Game Boy DIV and TIMA: Divider Edges, Overflow Windows, and Timer Glitches

Model the Game Boy's hidden divider, falling-edge timer input, DIV/TAC write effects, delayed TIMA reload, and model-specific validation boundaries.

The Game Boy timer is easy to approximate and surprisingly easy to get subtly wrong. The visible DIV register is not an independent counter that software increments. It exposes part of a continuously running divider, and TIMA reacts to transitions in a selected divider signal. Writes to divider/control registers can therefore change the timer input immediately. Overflow also involves a short reload window, not a single atomic “wrap and copy” operation.

This built-in timer is unrelated to the battery-backed real-time clock in an MBC3 cartridge. The cartridge RTC has its own oscillator and persistence behavior; $FF04 through $FF07 belong to the handheld’s internal timer circuitry. Confusing them leads to bad save-RTC diagnoses and to emulator designs that tie a CPU peripheral to wall-clock time.

One divider, several observable consequences

The system clock feeds an internal divider. The upper bits are observable through DIV at $FF04; writing any value to that address resets the underlying divider state rather than storing the byte written. The divider continues to run even when TIMA is disabled. It also participates in other hardware timing, including audio-related divider behavior, so resetting it can change more than one subsystem’s phase.

TAC at $FF07 selects a divider tap and enables or disables the timer counter. The selected input is commonly described by its period: 4096, 262144, 65536, or 16384 increments per second in normal DMG-like operation, with model and speed caveats. The Game Boy: Complete Technical Reference distinguishes T-cycles from M-cycles and discusses the divider as part of the clock network. In CGB double-speed mode, CPU M-cycles and the divider’s externally visible rate must not be mixed without specifying the clock unit.

The important model is a signal edge, not a periodic host timer. The selected divider bit feeds a timer input; when the effective input transitions from high to low, TIMA increments. This means the same counter can react to a divider reset or a change in TAC, because those operations can create the transition. Merely incrementing TIMA every N host milliseconds cannot reproduce those side effects.

Why a falling-edge model works

Represent the selected input as a Boolean signal computed from the internal divider and timer enable. Preserve its previous state. Whenever the divider advances or software changes TAC, calculate the new state; if the old state was high and the new state is low, generate one timer increment. A simplified model looks like this:

old_input = timer_input(divider, tac)
apply_register_write_or_divider_tick()
new_input = timer_input(divider, tac)

if old_input == 1 and new_input == 0:
    increment_tima()

This is a conceptual signal model, not a complete CPU-cycle implementation. Register bus timing, write phases, model-specific quirks, and the delayed overflow state still matter. In particular, a write to DIV can reset the divider and create an extra increment if that reset changes the selected input from high to low. A write to TAC can also produce a transition because the selected tap or enable changes. Pan Docs explicitly warns that writing to TAC may increase TIMA once.

An implementation that handles the register write by assigning DIV = 0 but forgets to recompute the timer input will pass ordinary counter-frequency tests and fail software that deliberately synchronizes on the write. Conversely, adding an unconditional increment on every DIV write is also wrong: the increment depends on the before/after signal, not on the address alone.

Overflow is a multi-cycle state transition

When TIMA overflows, it does not simply become TMA in the same abstract step. Hardware documentation describes an intermediate period before the modulo value is transferred and the timer interrupt is requested. The exact cycle labels used by emulator references differ according to whether a trace names T-cycles, M-cycles, or CPU bus phases, so keep one explicit clock convention in code and tests.

During this interval, software can access TIMA and TMA. A write to TIMA in the documented cancellation window can suppress the pending reload/interrupt behavior on supported models; a write to TMA that coincides with transfer has ordering rules, and Pan Docs notes that the old TMA value is transferred in the same-M-cycle case. These are exactly the cases hidden by an atomic overflow routine.

Use a small timer state machine: normal counting, overflow pending, reload/interrupt phase, then normal counting again. Record when each transition occurs relative to CPU reads and writes. Do not update both TIMA and the interrupt flag together simply because an integer increment wrapped to zero.

The detail is valuable for real software, not just synthetic tests. Games use timer interrupts for music sequencing, serial protocols, and frame-independent tasks. If the reload is early by a machine cycle, an interrupt handler may observe a different counter value or phase. The symptom may be a subtly wrong tempo rather than a boot failure.

DIV is also a phase-reset interface

Software often resets DIV to establish a known phase. Since DIV reflects the divider’s upper portion, clearing the visible byte also clears lower divider bits that software cannot read directly. That reset can affect the next selected timer edge and can align or disturb another divider-driven function. It is useful to treat the write as a state transition across the entire divider network.

When reproducing audio quirks, avoid assuming that the timer and APU are independent if both consume divider-derived signals. Pan Docs and the technical reference distinguish the CPU timer from the MBC3 RTC and document the divider’s relation to speed mode. An emulator should centralize divider advancement so that timer and sound consumers observe a consistent phase. Separate duplicated counters can drift by one tick after a reset.

STOP and CGB speed switching need their own path. Pan Docs documents that DIV is reset when executing STOP and during a speed switch, and its current page calls out an unresolved nuance around the post-switch wait. Do not invent a universal delay rule: encode the behavior backed by hardware-specific tests for the model being emulated, and state the limitation for undocumented cases.

Diagnostics that expose the real state

For a failing test, log each T-cycle or M-cycle with the internal divider, DIV, TIMA, TMA, TAC, selected timer input before and after the event, pending overflow phase, and IF timer bit. Include the CPU bus operation and exact access phase. A log that shows only the four registers misses the hidden divider bits that determine the next falling edge.

Use focused cases:

  • Select each TAC rate, let the timer run for several full periods, and compare the overflow count.
  • Write DIV while the selected timer input is high and while it is low; verify that only the relevant transition creates an extra increment.
  • Change the TAC select bits and enable bit in both signal states.
  • Overflow TIMA, then write TIMA or TMA at each phase around reload and interrupt request.
  • Repeat in DMG, CGB normal-speed, and CGB double-speed configurations.
  • Reset or switch speed while recording the divider and audio phase.
  • Save/load a state in the middle of the overflow window and compare the next interrupt cycle.

Use community test suites such as mooneye-test-suite and model-specific hardware results as evidence, but report exactly which tests ran and which model they target. A pass in a CGB-focused suite does not establish DMG silicon behavior, and a test that checks only final register contents may not prove precise interrupt ordering.

Safe implementation boundaries

Do not run a wall-clock thread for TIMA. Advance timer state from the emulated CPU clock, even if the frontend calls the core in audio or video batches. Centralize the clock ratio and expose both T-cycles and M-cycles in names or types so a function cannot silently receive the wrong unit. Use unsigned arithmetic for the hidden divider, and derive the chosen bit without relying on accidental overflow of a signed counter.

Keep the MBC3 RTC as a different device with its own emulated-time and save-persistence policy. A cartridge RTC may need to advance across shutdown according to emulator settings; the internal divider should resume from serialized hardware state. They can both influence game clocks but have different reset, battery, and timing contracts.

Acceptance criteria

A timer implementation should pass frequency tests and edge-trigger tests separately. Its diagnostics should make the hidden divider and pending reload phase observable. Register writes must be tested as signal changes, not just value assignments. The same save state should reproduce the same next timer edge and interrupt. When exact hardware behavior remains unsettled, document a per-model caveat instead of hiding it behind one “Game Boy timer” claim.

Once modeled as a divider network with observable edges, timer bugs become explainable. A lost tick can be traced to a write-generated falling edge, the wrong tap, a reload-window race, or a speed-mode conversion. That is much more actionable than increasing a timer frequency until one game sounds closer.

Related:

Sources:

Comments