Type definitions:
Like all machines the gameboy uses data types of different sizes, mainly bytes (8 bits) and words (16 bits). The z80-like instruction set also uses signed bytes and signed words. So to make understanding of the code easier I have typedef'd these to give the following:
typedef unsigned char BYTE ;
typedef char SIGNED_BYTE ;
typedef unsigned short WORD ;
typedef signed short SIGNED_WORD ;
Loading In The Game:
The maximum size of a gameboy rom is 0x200000 bytes so if we have an array of this size then this is where we can store the game. This array will represent
the gameboy cartride.
As you will read in the section called "Memory Control and Map" the internal gameboy memory only stores the first 0x8000 bytes of the cartride in memory and the rest is left
in the cartridge (the emulator will have to swap data in and out the cartride to memory as needed)
Declaration of cartridge memory:
BYTE m_CartridgeMemory[0x200000] ;
Loading a game into cartridge memoy:
memset(m_CartridgeMemory,0,sizeof(m_CartridgeMemory)) ;
FILE *in;
in = fopen( "Tetris.gb", "rb" );
fread(m_CartridgeMemory, 1, 0x200000, in);
fclose(in);
The Emulation Loop:
Unlike the chip8 the gameboy timing is well documented and it is crucial that the speed of the gameboy is emulated correctly. The gameboy document
that I'm hosting gives the timing in clock cycles for each opcode. So if we know how many clock cycles each instruction takes on the original gameboy hardware we can
add this to a running counter to keep track of timing. One of the main benefits in keeping a track of how long each instruction takes is we can synchronize this with the
graphic emulation and the gameboy clock timers to emulate these at the same rate as the cpu.
I use SDL for my graphic emulation and this comes with functionality to limit the frame rate of the emulator. I have chosen to limit the frame rate to 60 frames per second, the
reason for this is the gameboy refreshes the screen 60 times a second. According to game pan docs site the amount of clock cycles
the gameboy can exectue every second is 4194304 which means that if each frame we update the emulator 60 times a second the each frame will execute 69905(4194304/60) clock cycles. This will
ensure the emulator is run at the correct speed.
void Emulator::Update( )
{
const int MAXCYCLES = 69905 ;
int cyclesThisUpdate = 0 ;
while (cyclesThisUpdate < MAXCYCLES)
{
int cycles = ExecuteNextOpcode( ) ;
cyclesThisUpdate+=cycles ;
UpdateTimers(cycles) ;
UpdateGraphics(cycles) ;
DoInterupts( ) ;
}
RenderScreen( ) ;
}