codeslinger.co.uk

Gameboy - Getting Started.

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 ;

I highly recommend you do the same as it makes understanding what value range a variable can store easier to understand and to track down any overflow errors.

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( ) ;
}

So if the above function is called 60 times a second then RenderScreen is also called 60 times a second which is the same as the gameboy. The while loop ensures that the correct amount of instructions is being executed this frame (assuming of course that the function ExecuteNextOpcode is returning the correct clock cycles the next opcode took to execute). Also the timers and graphics are being passed how many clock cycles the opcode took so they can update at the same rate as the cpu. After every instruction the interupts need to be checked and if needed then processed.