Timer Explanation:
This section should not be confused with the timing explained in the Getting Started
section. Those timers were about getting the speed of the emulation right and synchronizing the cpu with graphics. The gameboy
has a timer in memory which counts up at a certain frequency and requests an interupt when it overflows. This is the timer this section is about.
The timer is located in memoy address 0xFF05 and will count up at a set frequency. The frequency it counts up at is set by the timer controller in
memory address 0xFF07. Whenever the timer overflows (remember the memory elements are all unsigned bytes so overflowing means going greater than 255)
it requests a timer interupt and then resets itself to the value of the timer modulator in memory address 0xFF06. This gives us the following definitions:
#define TIMA 0xFF05
#define TMA 0xFF06
#define TMC 0xFF07
Emulating Time:
There are 4 frequencies the timer controller (TMA) can set the timer (TIMA) to count up at. These are:
4096 Hz
262144 Hz
65536 Hz
16384 Hz
#define CLOCKSPEED 4194304 ;
m_TimerCounter = CLOCKSPEED/frequency ;
So if the current frequency is 4096Hz then our timer counter will be 1024 (CLOCKSPEED/4096). This means that "for every 1024 clock cycles our cpu does our timer should increment itself once". However if our frequency was 16384 then our counter would be 256 (CLOCKSPEED/16384) which means "For every 256 clock cycles our cpu does our timer should increment itself once".
If you look at the main emulation update loop which is called 60 times a second (discussed in the Getting Started section) you can see that I've already taken into account the timers with the UpdateTimers funcation and as you can see Im passing in the clock cycle for the last opcode.
void Emulator::Update( )
{
const int MAXCYCLES = 69905 ;
int cyclesThisUpdate = 0 ;
while (cyclesThisUpdate < MAXCYCLES)
{
int cycles = ExecuteNextOpcode( ) ;
cyclesThisUpdate+=cycles ;
UpdateTimers(cycles) ;
UpdateGraphics(cycles) ;
DoInterupts( ) ;
}
RenderScreen( ) ;
}
void Emulator::UpdateTimers( int cycles )
{
DoDividerRegister(cycles);
// the clock must be enabled to update the clock
if (IsClockEnabled())
{
m_TimerCounter -= cycles ;
// enough cpu clock cycles have happened to update the timer
if (m_TimerCounter <= 0)
{
// reset m_TimerTracer to the correct value
SetClockFreq( ) ;
// timer about to overflow
if (ReadMemory(TIMA) == 255)
{
WriteMemory(TIMA,ReadMemory(TMA)) ;
RequestInterupt(2) ;
}
else
{
WriteMemory(TIMA, ReadMemory(TIMA)+1) ;
}
}
}
}
The Timer Controller:
The timer controller (TMC) is a 3 bit register which controlls the timer (DUH!). Bit 1 and 0 combine together to specify which frequency the timer should increment at. This is the mapping:
00: 4096 Hz
01: 262144 Hz
10: 65536 Hz
11: 16384 Hz
bool Emulator::IsClockEnabled() const
{
return TestBit(ReadMemory(TMC),2)?true:false ;
}
else if (TMC == address)
{
BYTE currentfreq = GetClockFreq() ;
m_GameMemory[TMC] = data ;
BYTE newfreq = GetClockFreq();
if (currentfreq != newfreq)
{
SetClockFreq();
}
}
// remember the clock frequency is a combination of bit 1 and 0 of TMC
BYTE Emulator::GetClockFreq( )const
{
return ReadMemory(TMC) & 0x3 ;
}
///////////////////////////////////////////
void Emulator::SetClockFreq()
{
BYTE freq = GetClockFreq( ) ;
switch (freq)
{
case 0: m_TimerCounter = 1024 ; break ; // freq 4096
case 1: m_TimerCounter = 16 ; break ;// freq 262144
case 2: m_TimerCounter = 64 ; break ;// freq 65536
case 3: m_TimerCounter = 256 ; break ;// freq 16382
}
}
Divider Register:
The final timing related area that needs emulating is the Divider Register. It works very similar to the timers which is why I have included it in this section aswell as put the code to emulate it inside the UpdateTimers function. The way it works is it continually counts up from 0 to 255 and then when it overflows it starts from 0 again. It does not cause an interupt when it overflows and it cannot be paused like the timers. It counts up at a frequency of 16382 which means every 256 CPU clock cycles the divider register needs to increment. We need another int counter like m_TimerCounter to keep track of when it needs to increment, this is called m_DividerCounter which initially is set to 0 and constantly increments to 255 then starts again. The Divider Register is found at register address 0xFF04. The DoDividerRegister function called in UpdateTimers is emulated like so:
void Emulator::DoDividerRegister(int cycles)
{
m_DividerRegister+=cycles;
if (m_DividerCounter >= 255)
{
m_DividerCounter = 0 ;
m_Rom[0xFF04]++ ;
}
}
//trap the divider register
else if (0xFF04 == address)
m_Rom[0xFF04] = 0 ;