General Info:
All of the info below was taken straight from nocashs pan-docs.
CPU: 8-bit (similar to the z80 processor)
Clock-Speed: 4.194304MHz
Screen Resolution: 160x144
Vertical Sync: 59.73Hz
Internal Memory size: 0x10000
I have explained how to emulate the clock speed for accurate timing aswell as the vertical refresh rate in the Getting Started section of this site. The screen resolution is quite interesting to emulate. The way I did it was to use a 3D array. The first part is the screen x axis. The second is the y axis and the third is the colour (three elements for red, green and blue). This gives the following declaration:
BYTE m_ScreenData[160][144][3] ;
So if I wanted to set the very middle pixel of the screen to red I would do the following:
m_ScreenData[160/2][144/2][0] = 0xFF ;
m_ScreenData[160/2][144/2][1] = 0 ;
m_ScreenData[160/2][144/2][2] = 0 ;
BYTE m_Rom[0x10000] ;
I will discuss the internal memory in much more detail in the section called Memory Control and MapThe Registers:
The gameboy has 8 registers named A,B,C,D,E,F,H,L each are 8-Bits in size. However these registers are often paired to form 4 16-Bit registers.
The pairings are AF, BC, DE, HL. AF is less frequently used then the others because A is the accumulator and F is the flag register so working with them
as a pair is less frequent than the others. HL is used very frequently mainly for referring to game memory.
There are many ways to emulate the registers in code. One of the ways is to represent each of the register pairs with a word variable and supply a function
for retrieving the hi and lo bytes for the individual registers. Another way is the opposite and have 8 byte variables for each register and then supply a function
to combine these to form a word to represent the pairings. However I prefer to use unions to emulate the registers like so:
union Register
{
WORD reg ;
struct
{
BYTE lo ;
BYTE hi ;
};
};
Register m_RegisterAF ;
Register m_RegisterBC ;
Register m_RegisterDE ;
Register m_RegisterHL ;
The Flags:
As previously stated the F register doubles as the flag register. There are 4 flags the cpu uses (opposed to the real z80 which I believe has 8) called "the carry flag", "the half carry flag",
"the subtract flag" and "the zero flag". The carry flag gets set after a mathematical instruction which resulted in a value that cannot fit within the range of the data type. For example if the instruction
was to set register A to the value of 250 + 7 then A would be set to 257. However as register A is an unsigned byte it can only store values between 0 and 255. Which means A would wrap around on itself and become
1 and the carry flag would be set to show the result overflowed.
The half carry flag is similar to the carry flag except it is set when a mathematical instruction overflow from the lower nibble to the upper nibble (or
with 16 bit operations the lower byte to the upper byte). This flag is set by many instructions but is only used with the DAA instruction
The zero flag is set when a mathematical instruction results to 0. I.E. 4-4.
The Subtract flag is set when a mathematical operation is a subtraction. This is also only used with the DAA instrction.
I use the following definitions when referring to the flags:
#define FLAG_Z 7
#define FLAG_N 6
#define FLAG_H 5
#define FLAG_C 4
Program Counter and Stack Pointer:
The program counter is the address of the next opcode in memory to execute. Some opcodes require the next one or two bytes in memory to use as arguments. When this is
the case the program counter needs to skip these to point to the next actual opcode. The gameboy memory is 0x10000 bytes in size which means it has a range of 0 to 0xFFFF.
This is also the same range as an unsigned word data type which is perfect for representing the program counter. The program counter will change its value from jump instructions
along with call instructions and the execution of interupts. The program counter is initialized to 0x100
The stack is a place in memory where data gets loaded in to. The last piece of data put onto the stack will be the first retrieved from the stack. The stack pointer points to the
very top of the stack in memory, in other words the area of memory where the next piece of data to be added to the stack would occupy. Like the program counter the stack pointer
points to address is memory so a WORD data type would also represent the stack pointer well. However some of the opcodes use the hi and lo bytes of the stack pointer so it would
be easier to emulate the stack pointer the same way we do the registers. The stack pointer is initialized to 0xFFFE.
The above information gives us the following declarations:
WORD m_ProgramCouner ;
Regiser m_StackPointer
Initializing:
The following information is also found in nocashs-pan-docs When the emulator starts it must set the state of the registers, stackpointer, program counter and the special rom registers to the following:
m_ProgramCounter=0x100 ;
m_RegisterAF=0x01B0;
m_RegisterBC=0x0013;
m_RegisterDE=0x00D8;
m_RegisterHL=0x014D;
m_StackPointer=0xFFFE;
m_Rom[0xFF05] = 0x00 ;
m_Rom[0xFF06] = 0x00 ;
m_Rom[0xFF07] = 0x00 ;
m_Rom[0xFF10] = 0x80 ;
m_Rom[0xFF11] = 0xBF ;
m_Rom[0xFF12] = 0xF3 ;
m_Rom[0xFF14] = 0xBF ;
m_Rom[0xFF16] = 0x3F ;
m_Rom[0xFF17] = 0x00 ;
m_Rom[0xFF19] = 0xBF ;
m_Rom[0xFF1A] = 0x7F ;
m_Rom[0xFF1B] = 0xFF ;
m_Rom[0xFF1C] = 0x9F ;
m_Rom[0xFF1E] = 0xBF ;
m_Rom[0xFF20] = 0xFF ;
m_Rom[0xFF21] = 0x00 ;
m_Rom[0xFF22] = 0x00 ;
m_Rom[0xFF23] = 0xBF ;
m_Rom[0xFF24] = 0x77 ;
m_Rom[0xFF25] = 0xF3 ;
m_Rom[0xFF26] = 0xF1 ;
m_Rom[0xFF40] = 0x91 ;
m_Rom[0xFF42] = 0x00 ;
m_Rom[0xFF43] = 0x00 ;
m_Rom[0xFF45] = 0x00 ;
m_Rom[0xFF47] = 0xFC ;
m_Rom[0xFF48] = 0xFF ;
m_Rom[0xFF49] = 0xFF ;
m_Rom[0xFF4A] = 0x00 ;
m_Rom[0xFF4B] = 0x00 ;
m_Rom[0xFFFF] = 0x00 ;