9
\$\begingroup\$

I've been coding a Gameboy emulator in C for some time(seems like a common project for people). It's reasonably functional and has some MBC1 support, but has some minor graphical glitches still. I've coded on and off for a long time, but consider myself still very much an amateur and have no professional experience.

The code is very much a work in progress. I realize the error-handling is lax. I'm just looking for some general critiques. I would like to try and start a career in software development, but don't have the benefit of a degree. Would something like this be reasonable for a portfolio and to put on a resume? If not, are there parts of the code that stick out and say "unprofessional"?

This is the emulation code of the project. The rest of the code, including I/O and screen drawing support, is on GitHub.

#include <stdio.h>
#include <conio.h>
#include <sdl.h>
#include <windows.h>

#include "define.h"

struct Emulation
{
    struct MemoryMap
    {
        unsigned char romBank0[0x4000];     // Primary rom bank from 0x0000-0x3FFF.
        unsigned char romBank1[0x4000];     // Secondary rom bank from 0x4000-0x7FFF (Switchable on MBC carts).
        unsigned char videoRam[0x2000];     // The Gameboy's 8 KB video RAM from 0x8000-0x9FFF.
        unsigned char ramBank[0x2000];      // An 8 KB switchable RAM bank that is supported by some cartridges from 0xA000-0xBFFF.
        unsigned char internRam[0x2000];    // The Gameboy's 8 KB internal RAM from 0xC000-0xDFFF, this is also echoed from 0xE000-FDFF.
        unsigned char sprite[0xA0];         // Sprite Attribute Memory (OAM) from 0xFE00-0xFE9F.
                                            // The address space from FEA0-FEFF is unusable.
        unsigned char ioRegs[0x4C];         // I/O registers from 0xFF00-0xFF4B.  FF03, FF08-FF0E, FF15, FF1F, and FF27-FF2F are unused.
                                            // The address space from FF4C-FF7F is unusable.
        unsigned char highRam[0x7F];        // The Gameboy's high RAM from 0xFF80-FFFE.
        unsigned char intrpFlags;           // The interrupt enable(IE) flags at 0xFFFF.
    } memory;

    struct Cartridge
    {
        int systemType;
        int mbcType;
        unsigned char romBankRegister;  // This will store the currently selected MBC ROM bank.
        unsigned char dataBuffer[16777216];  // Set a maximum cartridge size at 16 megabytes.
    } cart;

    struct CPU
    {
        union Registers
        {
            struct { unsigned short int AF, BC, DE, HL, SP, PC; };
            struct { unsigned char F, A, C, B, E, D, L, H, SPL, SPH, PCL, PCH; };
        } regs;
    } cpu;

    struct State
    {
        boolean eiDelay;  // EI instruction enables interrupts one instruction after its execution.
        boolean intrpEnable;  // Master interrupt enable switch.
        boolean halted;
        boolean haltInstructionRepeat;
        boolean running;
        boolean stopped;
    } state;

    struct Cycles
    {
        unsigned short int internalCounterCycles;  // Cycle counter for DIV and TIMA register.
        unsigned short int previousInternalCounterCycles;  // Cycle counter for DIV and TIMA register.
        unsigned short int timaIncCounterCycles;  // A cycle counter for incrementing the TIMA register.
        unsigned int frameCycles;
        unsigned int statCycles;  // Cycle counter for STAT register.
        unsigned char opCycles[0x100];  // Store the number of clock cycles for every instruction
        unsigned char opCBCycles[0x100];  // Store cycles for CB bit instructions
    } cycles;

    struct IO
    {
        struct Display
        {
            unsigned char bgBuffer[256][256];
        };
    } io;
} emu;

// Standard Gameboy opcode clock cycles.
unsigned char GB_CycleTable[0x100] = { //0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F
                                 /*0x00*/  4,  12,   8,   8,   4,   4,   8,   4,  20,   8,   8,   8,   4,   4,   8,   4,
                                 /*0x10*/  0,  12,   8,   8,   4,   4,   8,   4,  12,   8,   8,   8,   4,   4,   8,   4,
                                 /*0x20*/  8,  12,   8,   8,   4,   4,   8,   4,   8,   8,   8,   8,   4,   4,   8,   4,
                                 /*0x30*/  8,  12,   8,   8,  12,  12,  12,   4,   8,   8,   8,   8,   4,   4,   8,   4,
                                 /*0x40*/  4,   4,   4,   4,   4,   4,   8,   4,   4,   4,   4,   4,   4,   4,   8,   4,
                                 /*0x50*/  4,   4,   4,   4,   4,   4,   8,   4,   4,   4,   4,   4,   4,   4,   8,   4,
                                 /*0x60*/  4,   4,   4,   4,   4,   4,   8,   4,   4,   4,   4,   4,   4,   4,   8,   4,
                                 /*0x70*/  8,   8,   8,   8,   8,   8,   0,   8,   4,   4,   4,   4,   4,   4,   8,   4,
                                 /*0x80*/  4,   4,   4,   4,   4,   4,   8,   4,   4,   4,   4,   4,   4,   4,   8,   4,
                                 /*0x90*/  4,   4,   4,   4,   4,   4,   8,   4,   4,   4,   4,   4,   4,   4,   8,   4,
                                 /*0xA0*/  4,   4,   4,   4,   4,   4,   8,   4,   4,   4,   4,   4,   4,   4,   8,   4,
                                 /*0xB0*/  4,   4,   4,   4,   4,   4,   8,   4,   4,   4,   4,   4,   4,   4,   8,   4,
                                 /*0xC0*/  8,  12,  12,  16,  12,  16,   8,  16,   8,  16,  12,   0,  12,  24,   8,  16,
                                 /*0xD0*/  8,  12,  12,   0,  12,  16,   8,  16,   8,  16,  12,   0,  12,   0,   8,  16,    
                                 /*0xE0*/ 12,  12,   8,   0,   0,  16,   8,  16,  16,   4,  16,   0,   0,   0,   8,  16,
                                 /*0xF0*/ 12,  12,   8,   4,   0,  16,   8,  16,  12,   8,  16,   4,   0,   0,   8,  16
};

// Gameboy bit operation clock cycles
unsigned char GB_CBCycleTable[0x100] = {//0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F
                                 /*0x00*/    8,   8,   8,   8,   8,   8,  16,   8,   8,   8,   8,   8,   8,   8,  16,   8,
                                 /*0x10*/    8,   8,   8,   8,   8,   8,  16,   8,   8,   8,   8,   8,   8,   8,  16,   8,
                                 /*0x20*/    8,   8,   8,   8,   8,   8,  16,   8,   8,   8,   8,   8,   8,   8,  16,   8,
                                 /*0x30*/    8,   8,   8,   8,   8,   8,  16,   8,   8,   8,   8,   8,   8,   8,  16,   8,
                                 /*0x40*/    8,   8,   8,   8,   8,   8,  12,   8,   8,   8,   8,   8,   8,   8,  12,   8,
                                 /*0x50*/    8,   8,   8,   8,   8,   8,  12,   8,   8,   8,   8,   8,   8,   8,  12,   8,
                                 /*0x60*/    8,   8,   8,   8,   8,   8,  12,   8,   8,   8,   8,   8,   8,   8,  12,   8,
                                 /*0x70*/    8,   8,   8,   8,   8,   8,  12,   8,   8,   8,   8,   8,   8,   8,  12,   8,
                                 /*0x80*/    8,   8,   8,   8,   8,   8,  16,   8,   8,   8,   8,   8,   8,   8,  16,   8,
                                 /*0x90*/    8,   8,   8,   8,   8,   8,  16,   8,   8,   8,   8,   8,   8,   8,  16,   8,
                                 /*0xA0*/    8,   8,   8,   8,   8,   8,  16,   8,   8,   8,   8,   8,   8,   8,  16,   8,
                                 /*0xB0*/    8,   8,   8,   8,   8,   8,  16,   8,   8,   8,   8,   8,   8,   8,  16,   8,
                                 /*0xC0*/    8,   8,   8,   8,   8,   8,  16,   8,   8,   8,   8,   8,   8,   8,  16,   8,
                                 /*0xD0*/    8,   8,   8,   8,   8,   8,  16,   8,   8,   8,   8,   8,   8,   8,  16,   8,
                                 /*0xE0*/    8,   8,   8,   8,   8,   8,  16,   8,   8,   8,   8,   8,   8,   8,  16,   8,
                                 /*0xF0*/    8,   8,   8,   8,   8,   8,  16,   8,   8,   8,   8,   8,   8,   8,  16,   8
};

//Blank 8-bit table for possible future use
//unsigned char GB_BitCycleTable[0x100] = {//0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F
//  /*0x00*/     ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,
//  /*0x10*/     ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,
//  /*0x20*/     ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,
//  /*0x30*/     ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,
//  /*0x40*/     ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,
//  /*0x50*/     ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,
//  /*0x60*/     ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,
//  /*0x70*/     ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,
//  /*0x80*/     ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,
//  /*0x90*/     ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,
//  /*0xA0*/     ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,
//  /*0xB0*/     ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,
//  /*0xC0*/     ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,
//  /*0xD0*/     ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,
//  /*0xE0*/     ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,
//  /*0xF0*/     ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,    ,

unsigned char opcodeDescription[256][32];

// An array that holds the pixel data that will actually be drawn to the screen.
unsigned char screenData[0x5A00];

//----------------------------------------//
// Gameboy status arrays.                 //
//----------------------------------------//
unsigned char joyState[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

//----------------------------------------//
// Miscellaneous variables                //
//----------------------------------------//
unsigned long long int FPS = 0;

SDL_TimerID FPSTimerID;

// This will check if the read address is readable and return the data from the proper location.
unsigned char ReadMemory(unsigned short int address)
{
    if ((address >= 0x0000) && (address <= 0x3FFF))
        return emu.memory.romBank0[address];
    else if ((address >= 0x4000) && (address <= 0x7FFF))
        return emu.memory.romBank1[address - 0x4000];
    else if ((address >= 0x8000) && (address <= 0x9FFF))
        return emu.memory.videoRam[address - 0x8000];
    else if ((address >= 0xA000) && (address <= 0xBFFF))
        return emu.memory.ramBank[address - 0xA000];
    else if ((address >= 0xC000) && (address <= 0xDFFF))
        return emu.memory.internRam[address - 0xC000];
    else if ((address >= 0xE000) && (address <= 0xFDFF))  // Echo Ram.  Returns the same value as the C000-DDFF space.
        return emu.memory.internRam[address - 0xE000];
    else if ((address >= 0xFE00) && (address <= 0xFE9F))  // Read from the sprite OAM RAM.
        return emu.memory.sprite[address - 0xFE00];
    //else if ((address >= 0xFEA0) && (address <= 0xFEFF))  // Restricted memory space.
    //  return 0xFF;
    else if ((address >= 0xFF00) && (address <= 0xFF4B))  // Read from an I/O register.
    {
        return emu.memory.ioRegs[address - 0xFF00];
    }
    //else if ((address >= 0xFF4C) && (address <= 0xFF7F))  // Restricted memory space.
    //  return 0xFF;
    else if ((address >= 0xFF80) && (address <= 0xFFFE))
        return emu.memory.highRam[address - 0xFF80];
    else if (address == 0xFFFF)
        return emu.memory.intrpFlags;
    else
        return 0xFF;
}

// This will check whether a write to memory is valid and if any special location is written
void WriteMemory(unsigned short int address, unsigned char data)
{
    // Make sure the instruction isn't trying to write to the ROM bank areas.
    if ((address >= 0x0000) && (address <= 0x7FFF))
    {
        // See if this is a special memory write.
        if ((address >= 0x2000) && (address <= 0x3FFF))
            if ((emu.cart.mbcType >= 1) && (emu.cart.mbcType <= 3))
            {
                // 0 or ROM banks that are a multiple of 0x20 refer to the next ROM bank (0x20 = 0x21, 0x40 = 0x41).
                // The low 5 bits of the 7-bit ROM bank are selected here.
//              if ((data % 20) == 0)
//                  data |= 1;
                // This will combine the lower 5 bits of the written data with the ROM bank register.
                emu.cart.romBankRegister = (emu.cart.romBankRegister & 0xE0) | (data & 0x1F);
                emu.cart.romBankRegister = data;
                memcpy(&emu.memory.romBank1[0], &emu.cart.dataBuffer[emu.cart.romBankRegister * 0x4000], 0x4000);
            }
        else if ((address >= 0x4000) && (address <= 0x5FFF))
            if ((emu.cart.mbcType >= 1) && (emu.cart.mbcType <= 3))
            {
                // 0 or ROM banks that are a multiple of 0x20 refer to the next ROM bank (0x20 = 0x21, 0x40 = 0x41).
                // The high 2 bits of the 7-bit ROM bank are selected here.
                emu.cart.romBankRegister = (emu.cart.romBankRegister & 0x1F) | (data & 0x60);
                memcpy(&emu.memory.romBank1[0], &emu.cart.dataBuffer[emu.cart.romBankRegister * 0x4000], 0x4000);
            }
    }

    else if ((address >= 0x8000) && (address <= 0x9FFF))
        emu.memory.videoRam[address - 0x8000] = data;
    else if ((address >= 0xA000) && (address <= 0xBFFF))
        emu.memory.ramBank[address - 0xA000] = data;
    else if ((address >= 0xC000) && (address <= 0xDFFF))
        emu.memory.internRam[address - 0xC000] = data;
    else if ((address >= 0xE000) && (address <= 0xFDFF))  // Echo Ram.  Writes the value to the C000-DDFF space.
        emu.memory.internRam[address - 0xE000] = data;
    else if ((address >= 0xFE00) && (address <= 0xFE9F))  // Write to the sprite OAM RAM.
        emu.memory.sprite[address - 0xFE00] = data;
    else if ((address >= 0xFEA0) && (address <= 0xFEFF))  // Restricted memory space.
        return;
    else if ((address >= 0xFF00) && (address <= 0xFF4B))  // Write to an I/O register.
    {
        if (address == 0xFF00)
            IOregister_P1 = 0xCF + (data & 0x30);  // Only bits 4 and 5 of the P1 register are writable.

        else if (address == 0xFF01)
            IOregister_SB = data;

        else if (address == 0xFF02)
        {
            // If a serial transfer was attempted, set the received data to 0xFF since no second Gameboy is present.
            // The interrupt is only triggered if the system has set itself as the master Gameboy.
            if ((data & BIT_7) && (data & BIT_0))
                IOregister_IF |= BIT_3;
            IOregister_SB = 0xFF;
        }

        // Reset the internal counter if DIV is written to.
        else if (address == 0xFF04)
        {
            emu.cycles.internalCounterCycles = 0;
            emu.cycles.previousInternalCounterCycles = 0;
            emu.cycles.timaIncCounterCycles = 0;
        }

        else if (address == 0xFF05)
            IOregister_TIMA = data;

        else if (address == 0xFF06)
            IOregister_TMA = data;

        else if (address == 0xFF07)
        {
            IOregister_TAC = 0xF8 + (data & 0x07);  // Only the low 3 bits of TAC can be written.

            emu.cycles.timaIncCounterCycles = 0;
            //emu.cycles.previousInternalCounterCycles = 0;
        }
        else if (address == 0xFF0F)
            IOregister_IF = 0xE0 + (data & 0x1F);  // Only the low 5 bits of IF can be written.

        else if (address == 0xFF10)
            IOregister_NR10 = 0x80 + (data & 0x7F);  // The 7th bit always returns 1.

        else if (address == 0xFF40)
        {
            IOregister_LCDC = data;

            if (!(IOregister_LCDC & BIT_7))
            {
                // If the LCD is turned off, STAT mode, LY, and triggered display interrupts are all reset, but the LY/LYC compare bit and enabled STAT interrupt are retained.
                IOregister_STAT &= (BIT_0_OFF & BIT_1_OFF);
                IOregister_LY = 0;
                WYTemp = IOregister_WY;
                //              IOregister_IF &= (BIT_0_OFF & BIT_1_OFF);
                emu.cycles.statCycles = 0;
            }
        }

        else if (address == 0xFF41)
            IOregister_STAT = (BIT_7 | (data & 0x78)) | IOregister_STAT & (BIT_1 | BIT_0);  // Make sure the mode flag is not affected and the 7th bit always returns 1.

        else if (address == 0xFF46)
        {
            IOregister_DMA = data;
            for (int i = 0; i < 0xA0; i++)
                emu.memory.sprite[i] = ReadMemory((IOregister_DMA << 8) + i);  // If data is written to the OAM register, begin an OAM transfer.
        }
        else
            emu.memory.ioRegs[address - 0xFF00] = data;
    }
    //else if ((address >= 0xFF4C) && (address <= 0xFF7F))  // Restricted memory space.
    //  return;
    else if ((address >= 0xFF80) && (address <= 0xFFFE))
        emu.memory.highRam[address - 0xFF80] = data;
    else if (address == 0xFFFF)
        IOregister_IE = 0xE0 + (data & 0x1F);  // Only the low 5-bits of IE can be written.
}

//----------------------------------------//
// This instruction will add a given value//
// plus the carry flag to register A.     //
//----------------------------------------//
void z80_ADC_A_immediate()
{
    unsigned char value;
    value = ReadMemory(emu.cpu.regs.PC);

    // If there is a carry from bit 3, set flag H, otherwise
    // reset it.
    if (((emu.cpu.regs.A & 0xF) + (value & 0xF) + FLAG_C) > 0xF)
        emu.cpu.regs.F |= FLAG_H_ON;
    else
        emu.cpu.regs.F &= FLAG_H_OFF;

    // If there will be a carry from bit 7, set flag C, otherwise
    // reset it.
    if ((emu.cpu.regs.A + value + FLAG_C) > 0xFF)
    {
        emu.cpu.regs.A += value + FLAG_C;
        emu.cpu.regs.F |= FLAG_C_ON;
    }
    else
    {
        emu.cpu.regs.A += value + FLAG_C;
        emu.cpu.regs.F &= FLAG_C_OFF;
    }

    // If result is 0, set flag Z, otherwise reset it.
    if (emu.cpu.regs.A == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    // Flag N is reset.
    emu.cpu.regs.F &= FLAG_N_OFF;

    // Increment Program Counter to skip read value.
    emu.cpu.regs.PC++;
}

//----------------------------------------//
// This instruction will add an 8-bit     //
// register plus the carry flag to        //
// register A.                            //
//----------------------------------------//
void z80_ADC_A_reg8(unsigned char *reg)
{
    // If there will be a carry from bit 3, set flag H, otherwise
    // reset it.
    if (((emu.cpu.regs.A & 0xF) + (*reg & 0xF) + FLAG_C) > 0xF)
        emu.cpu.regs.F |= FLAG_H_ON;
    else
        emu.cpu.regs.F &= FLAG_H_OFF;

    // If there will be a carry from bit 7, set flag C, otherwise
    // reset it.
    if ((emu.cpu.regs.A + *reg + FLAG_C) > 0xFF)
    {
        emu.cpu.regs.A += *reg + FLAG_C;
        emu.cpu.regs.F |= FLAG_C_ON;
    }
    else
    {
        emu.cpu.regs.A += *reg + FLAG_C;
        emu.cpu.regs.F &= FLAG_C_OFF;
    }

    // If result is 0, set flag Z, otherwise reset it.
    if (emu.cpu.regs.A == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    // Flag N is reset.
    emu.cpu.regs.F &= FLAG_N_OFF;
}

//----------------------------------------//
// This instruction will add a given value//
// to register A.                         //
//----------------------------------------//
void z80_ADD_A_immediate()
{
    unsigned char value;
    value = ReadMemory(emu.cpu.regs.PC);

    // If there will be a carry from bit 3, set flag H, otherwise
    // reset it.
    if (((emu.cpu.regs.A & 0xF) + (value & 0xF)) > 0xF)
        emu.cpu.regs.F |= FLAG_H_ON;
    else
        emu.cpu.regs.F &= FLAG_H_OFF;

    // If there will be a carry from bit 7, set flag C, otherwise
    // reset it.
    if ((emu.cpu.regs.A + value) > 0xFF)
        emu.cpu.regs.F |= FLAG_C_ON;
    else
        emu.cpu.regs.F &= FLAG_C_OFF;

    // Add *reg to register A.
    emu.cpu.regs.A += value;

    // If result is 0, set flag Z, otherwise reset it.
    if (emu.cpu.regs.A == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    // Flag N is reset.
    emu.cpu.regs.F &= FLAG_N_OFF;

    emu.cpu.regs.PC++;
}

//----------------------------------------//
// This instruction will add an 8-bit     //
// register's value to register A.        //
//----------------------------------------//
void z80_ADD_A_reg8(unsigned char *reg)
{
    // If there will be a carry from bit 3, set flag H, otherwise
    // reset it.
    if (((emu.cpu.regs.A & 0xF) + (*reg & 0xF)) > 0xF)
        emu.cpu.regs.F |= FLAG_H_ON;
    else
        emu.cpu.regs.F &= FLAG_H_OFF;

    // If there will be a carry from bit 7, set flag C, otherwise
    // reset it.
    if ((emu.cpu.regs.A + *reg) > 0xFF)
        emu.cpu.regs.F |= FLAG_C_ON;
    else
        emu.cpu.regs.F &= FLAG_C_OFF;

    // Add *reg to register A.
    emu.cpu.regs.A += *reg;

    // If result is 0, set flag Z, otherwise reset it.
    if (emu.cpu.regs.A == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    // Flag N is reset.
    emu.cpu.regs.F &= FLAG_N_OFF;
}

//----------------------------------------//
// This instruction will add the value of //
// a 16-bit register to register HL.      //
//----------------------------------------//
void z80_ADD_HL_reg16(unsigned short int *reg)
{
    // If there will be a carry from bit 11, set flag H.
    if (((emu.cpu.regs.HL & 0xFFF) + (*reg & 0xFFF)) > 0xFFF)
        emu.cpu.regs.F |= FLAG_H_ON;
    else
        emu.cpu.regs.F &= FLAG_H_OFF;

    // If there will be a carry from bit 15, set flag C.
    if ((emu.cpu.regs.HL + *reg) > 0xFFFF)
        emu.cpu.regs.F |= FLAG_C_ON;
    else
        emu.cpu.regs.F &= FLAG_C_OFF;

    // Add *reg to HL.
    emu.cpu.regs.HL += *reg;

    // Flag N is reset.
    emu.cpu.regs.F &= FLAG_N_OFF;
}

//----------------------------------------//
// This instruction will add a given,     //
// signed 8-bit value to the              //
// Stack pointer.                         //
//----------------------------------------//
void z80_ADD_SP_immediate()
{
    unsigned char value;
    value = ReadMemory(emu.cpu.regs.PC);

    // The flags are determined with an unsigned value.

    // Turn on flag H if there is a carry from bit 3.
    if (((emu.cpu.regs.SP & 0xF) + (value & 0xF)) > 0xF)
        emu.cpu.regs.F |= FLAG_H_ON;
    else
        emu.cpu.regs.F &= FLAG_H_OFF;

    // Turn on flag C if the result is above 0xFFFF, or below 0x0000.
    if (((emu.cpu.regs.SP & 0xFF) + value) > 0xFF)
        emu.cpu.regs.F |= FLAG_C_ON;
    else
        emu.cpu.regs.F &= FLAG_C_OFF;

    emu.cpu.regs.SP += (signed char)value;

    // Flag Z and N are reset.
    emu.cpu.regs.F &= (FLAG_Z_OFF & FLAG_N_OFF);

    emu.cpu.regs.PC++;
}

//----------------------------------------//
// This instruction will logical AND a    //
// given value with register A.           //
//----------------------------------------//
void z80_AND_immediate()
{
    // Logically AND register A with an immediate value.
    emu.cpu.regs.A &= ReadMemory(emu.cpu.regs.PC);

    // If result is 0, set flag Z, otherwise reset it.
    if (emu.cpu.regs.A == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    // Flag_H is set.
    emu.cpu.regs.F |= FLAG_H_ON;

    // Flags N and C are reset.
    emu.cpu.regs.F &= (FLAG_N_OFF & FLAG_C_OFF);

    emu.cpu.regs.PC++;
}

//----------------------------------------//
// This instruction will logical AND a    //
// given value with an 8-bit register.    //
//----------------------------------------//
void z80_AND_reg8(unsigned char *reg)
{
    // Logically AND register A with *reg.
    emu.cpu.regs.A &= *reg;

    // If result is 0, set flag Z, otherwise reset it.
    if (emu.cpu.regs.A == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    // Flag_H is set.
    emu.cpu.regs.F |= FLAG_H_ON;

    // Flags N and C are reset.
    emu.cpu.regs.F &= (FLAG_N_OFF & FLAG_C_OFF);
}

//----------------------------------------//
// This instruction will test a bit of an //
// 8-bit register.                        //
//----------------------------------------//
void z80_BIT_bit_reg8(unsigned char bit, unsigned char *reg)
{
    // If result is 0, set flag Z, otherwise reset it.
    if ((*reg & bit) == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    // Flag N is reset.
    emu.cpu.regs.F &= FLAG_N_OFF;

    // Flag H is set.
    emu.cpu.regs.F |= FLAG_H_ON;
}

//----------------------------------------//
// This instruction will push the current //
// value of the Program Counter onto the  //
// stack and jump to a new address, but   //
// only if the given condition is met.    //
//----------------------------------------//
int z80_CALL_condition_immediate(unsigned char condition)
{
    unsigned short int callAddress;
    unsigned int conditionTrue;

    callAddress = (ReadMemory(emu.cpu.regs.PC) + (ReadMemory(emu.cpu.regs.PC + 1) << 8));
    conditionTrue = 0;

    switch (condition)
    {
    case 0x01:
    {
        if (FLAG_Z == 0)
            conditionTrue = 1;
    }
    break;
    case 0x02:
    {
        if (FLAG_Z == 1)
            conditionTrue = 1;
    }
    break;
    case 0x03:
    {
        if (FLAG_C == 0)
            conditionTrue = 1;
    }
    break;
    case 0x04:
    {
        if (FLAG_C == 1)
            conditionTrue = 1;
    }
    break;
    }

    emu.cpu.regs.PC += 2;

    if (conditionTrue == 1)
    {
        // Push address of next instruction onto the stack.
        emu.cpu.regs.SP--;
        WriteMemory(emu.cpu.regs.SP, emu.cpu.regs.PCH);
        emu.cpu.regs.SP--;
        WriteMemory(emu.cpu.regs.SP, emu.cpu.regs.PCL);

        emu.cpu.regs.PC = callAddress;
        return 12;  // return additional cycles if jump was executed.
    }
    else
        return 0;
}

//----------------------------------------//
// This instruction will push the current //
// value of the Program Counter onto the  //
// stack and jump to a new address.       //
//----------------------------------------//
void z80_CALL_immediate()
{
    unsigned short int callAddress;

    callAddress = ReadMemory(emu.cpu.regs.PC) + (ReadMemory(emu.cpu.regs.PC + 1) << 8);

    emu.cpu.regs.PC += 2;

    // Push address of next instruction onto the stack.
    emu.cpu.regs.SP--;
    WriteMemory(emu.cpu.regs.SP, emu.cpu.regs.PCH);
    emu.cpu.regs.SP--;
    WriteMemory(emu.cpu.regs.SP, emu.cpu.regs.PCL);

    // Load new address into Program Counter.
    emu.cpu.regs.PC = callAddress;
}

//----------------------------------------//
// This instruction will complement(flip) //
// the carry flag.                        //
//----------------------------------------//
void z80_CCF()
{
    // Complement flag C.
    emu.cpu.regs.F ^= FLAG_C_ON;

    // Flags N and H are reset.
    emu.cpu.regs.F &= (FLAG_N_OFF & FLAG_H_OFF);
}

//----------------------------------------//
// This instruction will set flags as if  //
// a given value with subtracted from     //
// register A.                            //
//----------------------------------------//
void z80_CP_immediate()
{
    unsigned char value;
    value = ReadMemory(emu.cpu.regs.PC);

    if (emu.cpu.regs.A == value)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    if ((emu.cpu.regs.A & 0xF) < (value & 0xF))
        emu.cpu.regs.F |= FLAG_H_ON;
    else
        emu.cpu.regs.F &= FLAG_H_OFF;

    if (emu.cpu.regs.A < value)
        emu.cpu.regs.F |= FLAG_C_ON;
    else
        emu.cpu.regs.F &= FLAG_C_OFF;

    emu.cpu.regs.F |= FLAG_N_ON;

    emu.cpu.regs.PC++;
}

//----------------------------------------//
// This instruction will set flags as if  //
// a register was subtracted from         //
// register A.                            //
//----------------------------------------//
void z80_CP_reg8(unsigned char *reg)
{
    if (emu.cpu.regs.A == *reg)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    if ((emu.cpu.regs.A & 0xF) < (*reg & 0xF))
        emu.cpu.regs.F |= FLAG_H_ON;
    else
        emu.cpu.regs.F &= FLAG_H_OFF;

    if (emu.cpu.regs.A < *reg)
        emu.cpu.regs.F |= FLAG_C_ON;
    else
        emu.cpu.regs.F &= FLAG_C_OFF;

    emu.cpu.regs.F |= FLAG_N_ON;
}

//----------------------------------------//
// This instruction will flip all of      //
// register A's bits.                     //
//----------------------------------------//
void z80_CPL()
{
    // Exclusive OR register A with 0xFF, this will flip the bits.
    emu.cpu.regs.A ^= 0xFF;

    // Flags N and H are set.
    emu.cpu.regs.F |= (FLAG_N_ON | FLAG_H_ON);
}

//----------------------------------------//
// This instruction will convert register //
// A to its packed-BCD representation.    //
//----------------------------------------//
void z80_DAA()
{
    unsigned short int result;

    result = emu.cpu.regs.A;

    // Check if flag N is on indicating the last operation was a subtraction.
    if (FLAG_N == 1)
    {
        if (FLAG_H == 1)
            result = (result - 0x06) & 0xFF;
        if (FLAG_C == 1)
            result -= 0x60;
    }
    // Otherwise, convert for an addition.
    else
    {
        if (((result & 0xF) > 0x09) || (FLAG_H == 1))
            result += 0x06;
        if ((result > 0x9F) || (FLAG_C == 1))
            result += 0x60;
    }

    // Set the carry flag if the BCD value of the result is greater than 99.
    if ((result & 0x100) == 0x100)
        emu.cpu.regs.F |= FLAG_C_ON;
//  else
//      emu.cpu.regs.F &= FLAG_C_OFF;

    emu.cpu.regs.A = (unsigned char)(result & 0xFF);

    // If the result was 0, turn the Z flag on.
    if (emu.cpu.regs.A == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    // Flag H is turned off.
    emu.cpu.regs.F &= FLAG_H_OFF;
}

//----------------------------------------//
// This instruction will decrease the     //
// value at memory location (HL) by 1.    //
//----------------------------------------//
void z80_DEC_location_HL()
{
    WriteMemory(emu.cpu.regs.HL, ReadMemory(emu.cpu.regs.HL) - 1);

    // Turn on flag N since operation is a subtraction.
    emu.cpu.regs.F |= FLAG_N_ON;

    if (ReadMemory(emu.cpu.regs.HL) == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    if ((ReadMemory(emu.cpu.regs.HL) & 0xF) == 0xF)
        emu.cpu.regs.F |= FLAG_H_ON;
    else
        emu.cpu.regs.F &= FLAG_H_OFF;
}

//----------------------------------------//
// This instruction decreases a given     //
// 16-bit register's value by 1.          //
//----------------------------------------//
void z80_DEC_reg16(unsigned short int *reg)
{
    *reg -= 1;
}

//----------------------------------------//
// This instruction decreases a given     //
// 8-bit register's value by 1.           //
//----------------------------------------//
void z80_DEC_reg8(unsigned char *reg)
{
    // If the lo nibble of the register is 0, then there will be a borrow from bit 4.
    if ((*reg & 0xF) == 0)
        emu.cpu.regs.F |= FLAG_H_ON;
    else
        emu.cpu.regs.F &= FLAG_H_OFF;

    // Decrement *reg
    *reg -= 1;

    // If result is 0, set flag Z, otherwise reset it.
    if (*reg == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    // Turn on flag N.
    emu.cpu.regs.F |= FLAG_N_ON;
}

//----------------------------------------//
// This instruction turns off the         //
// Interrupt Master Enable flag.          //
//----------------------------------------//
void z80_DI()
{
    IntrpMasterEnable = 0;
}

//----------------------------------------//
// This instruction turns on the          //
// Interrupt Master Enable flag.          //
//----------------------------------------//
void z80_EI()
{
    IntrpMasterEnable = 1;
}

//----------------------------------------//
// This instruction will halt the GB CPU  //
// until an interrupt occurs.             //
//----------------------------------------//
void z80_HALT()
{
    // Make sure interrupts are enabled before allowing the CPU to halt.
    if (IntrpMasterEnable == 1)
        emu.state.halted = 1;
    else
    {
        if ((IOregister_IE & IOregister_IF & 0x1F) != 0)
            emu.state.haltInstructionRepeat = 1;
        else
            emu.state.halted = 1;
    }
}

//----------------------------------------//
// This instruction will increase the     //
// value at memory location (HL) by 1.    //
//----------------------------------------//
void z80_INC_location_HL()
{
    // See if there will be a carry from bit 3, if there was, set flag H.
    if ((ReadMemory(emu.cpu.regs.HL) & 0xF) == 0xF)
        emu.cpu.regs.F |= FLAG_H_ON;
    else
        emu.cpu.regs.F &= FLAG_H_OFF;

    // Add 1 to the value at the address in register HL.
    WriteMemory(emu.cpu.regs.HL, ReadMemory(emu.cpu.regs.HL) + 1);

    // If result is 0, set flag Z, otherwise reset it.
    if (ReadMemory(emu.cpu.regs.HL) == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    // Turn off flag N.
    emu.cpu.regs.F &= FLAG_N_OFF;
}

//----------------------------------------//
// This instruction increases a given     //
// 16-bit register's value by 1.          //
//----------------------------------------//
void z80_INC_reg16(unsigned short int *reg)
{
    *reg += 1;
}

//----------------------------------------//
// This instruction increases a given     //
// 8-bit register's value by 1.           //
//----------------------------------------//
void z80_INC_reg8(unsigned char *reg)
{
    // Turn the H flag on if incrementing the register carries from bit 3.
    if ((*reg & 0xF) == 0xF)
        emu.cpu.regs.F |= FLAG_H_ON;
    else
        emu.cpu.regs.F &= FLAG_H_OFF;

    // Add 1 to *reg.
    *reg += 1;

    // If result is 0, set flag Z, otherwise reset it.
    if (*reg == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    // Turn off flag N.  Flag C is not affected.
    emu.cpu.regs.F &= FLAG_N_OFF;
}

void z80_JP_location_HL()
{
    emu.cpu.regs.PC = emu.cpu.regs.HL;
    //emu.cpu.regs.PCL = ReadMemory(emu.cpu.regs.HL);
    //emu.cpu.regs.PCH = ReadMemory(emu.cpu.regs.HL + 1);
}

//----------------------------------------//
// This instruction will relocate the     //
// Program Counter(PC) to a given         //
// immediate address.                     //
//----------------------------------------//
void z80_JP_immediate()
{
    unsigned short int readAddress;

    readAddress = emu.cpu.regs.PC;

    emu.cpu.regs.PCL = ReadMemory(readAddress);
    readAddress++;
    emu.cpu.regs.PCH = ReadMemory(readAddress);
}

//----------------------------------------//
// This instruction will relocate the     //
// Program Counter(PC) to a given address,//
// but only if the given condition is met.//
//----------------------------------------//
int z80_JP_condition_immediate(unsigned char condition)
{
    unsigned short int jumpAddress;
    boolean conditionTrue;

    jumpAddress = (ReadMemory(emu.cpu.regs.PC) + ((ReadMemory(emu.cpu.regs.PC + 1) << 8)));
    conditionTrue = 0;

    switch (condition)
    {
    // If the Z flag is off, jump to the address.
    case 0x01:
    {
        if (FLAG_Z == 0)
            conditionTrue = 1;
    }
    break;
    // If the Z flag is on, jump to the address.
    case 0x02:
    {
        if (FLAG_Z == 1)
            conditionTrue = 1;
    }
    break;
    // If the C flag is off, jump to the address.
    case 0x03:
    {
        if (FLAG_C == 0)
            conditionTrue = 1;
    }
    break;
    // If the C flag is on, jump to the address.
    case 0x04:
    {
        if (FLAG_C == 1)
            conditionTrue = 1;
    }
    break;
    }

    if (conditionTrue == 1)
    {
        emu.cpu.regs.PC = jumpAddress;
        return 4;  // Add four additional cycles if jump succeeds.
    }
    else
    {
        emu.cpu.regs.PC += 2;
        return 0;
    }
}


//----------------------------------------//
// This instruction will add a signed     //
// 8-bit offset to the Program Counter    //
// only if the given condition is met.    //
//----------------------------------------//
int z80_JR_condition_offset(unsigned char condition)
{
    boolean conditionTrue;

    conditionTrue = 0;

    // condition decides the jump condition to look for.
    switch(condition)
    {
    case 0x01:          //JR NZ, offset
    {
        if (FLAG_Z == 0)
            conditionTrue = 1;
    }
    break;
    case 0x02:          //JR Z, offset
    {
        if (FLAG_Z == 1)
            conditionTrue = 1;
    }
    break;
    case 0x03:          //JR NC, offset
    {
        if (FLAG_C == 0)
            conditionTrue = 1;
    }
    break;
    case 0x04:          //JR C, offset
    {
        if (FLAG_C == 1)
            conditionTrue = 1;
    }
    break;
    }

    if (conditionTrue)
    {
        // Relative jump within 128 bytes.
        emu.cpu.regs.PC += (signed char)ReadMemory(emu.cpu.regs.PC);
        emu.cpu.regs.PC++;
        return 4;
    }
    else
        emu.cpu.regs.PC++;

    return 0;
}

//----------------------------------------//
// This instruction will add a signed     //
// 8-bit offset to the Program Counter.   //
//----------------------------------------//
void z80_JR_offset()
{
    // Relative jump within 128 bytes.
    emu.cpu.regs.PC += (signed char)ReadMemory(emu.cpu.regs.PC);
    emu.cpu.regs.PC++;
}

//----------------------------------------//
// This instruction will load the value at//
// a given memory location into A.        //
//----------------------------------------//
void z80_LD_A_location_immediate()
{
    unsigned short int readAddress;
    readAddress = ReadMemory(emu.cpu.regs.PC) + (ReadMemory(emu.cpu.regs.PC + 1) << 8);

    emu.cpu.regs.A = ReadMemory(readAddress);
    emu.cpu.regs.PC += 2;
}

//----------------------------------------//
// This instruction loads the value at    //
// memory location 0xFF00 plus register C //
// into register A.                       //
//----------------------------------------//
void z80_LD_A_0xFF00_C()
{
    // Load the value at 0xFF00 + register C into register A.
    emu.cpu.regs.A = ReadMemory(0xFF00 + emu.cpu.regs.C);
}

//----------------------------------------//
// This instruction will load the value   //
// at memory location 0xFF00 plus an 8-bit//
// value into register A.                 //
//----------------------------------------//
void z80_LD_A_0xFF00_immediate()
{
    unsigned char value;
    value = ReadMemory(emu.cpu.regs.PC);

    // Load the value at 0xFF00 + value into register A.
    emu.cpu.regs.A = ReadMemory(0xFF00 + value);

    emu.cpu.regs.PC++;
}

//----------------------------------------//
// This instruction will load the value in//
// memory at (reg16) into register A.     //
//----------------------------------------//
void z80_LD_A_location_reg16(unsigned short int *reg)
{
    // Load A with the value at the address in *reg.
    emu.cpu.regs.A = ReadMemory(*reg);
}

//----------------------------------------//
// Load the Stack Pointer plus a given,   //
// signed 8-bit value into memory         //
// location (HL).                         //
//----------------------------------------//
void z80_LD_HL_SP_immediate()
{
    unsigned char value;
    value = ReadMemory(emu.cpu.regs.PC);

    // The flags are determined with an unsigned value.

    // Flag H is set if there is a carry from bit 3.
    if (((emu.cpu.regs.SP & 0xF) + (value & 0xF)) > 0xF)
        emu.cpu.regs.F |= FLAG_H_ON;
    else
        emu.cpu.regs.F &= FLAG_H_OFF;

    // Flag C is set if there is a carry from bit 7.
    if ((emu.cpu.regs.SP & 0xFF) + value > 0xFF)
        emu.cpu.regs.F |= FLAG_C_ON;
    else
        emu.cpu.regs.F &= FLAG_C_OFF;

    emu.cpu.regs.HL = emu.cpu.regs.SP + (signed char)value;

    // Reset flags Z and N.
    emu.cpu.regs.F &= (FLAG_Z_OFF & FLAG_N_OFF);

    // Increment Program Counter.
    emu.cpu.regs.PC++;
}

//----------------------------------------//
// This instruction will load a given     //
// value into memory location (HL).       //
//----------------------------------------//
void z80_LD_location_HL_immediate()
{
    unsigned char value;
    value = ReadMemory(emu.cpu.regs.PC);

    // Load the address in HL with the immediate value
    WriteMemory(emu.cpu.regs.HL, value);

    // Increment the Program Counter to skip over the 8-bit value.
    emu.cpu.regs.PC++;
}

//----------------------------------------//
// This instruction will load the value of//
// an 8-bit register into memory location //
// (HL).                                  //
//----------------------------------------//
void z80_LD_location_HL_reg8(unsigned char *reg)
{
    // Load *reg into the address in register HL
    WriteMemory(emu.cpu.regs.HL, *reg);
}

//----------------------------------------//
// This instruction will load the value of//
// register A into memory at a given      //
// location.                              //
//----------------------------------------//
void z80_LD_location_immediate_A()
{
    unsigned short int writeAddress;
    writeAddress = ReadMemory(emu.cpu.regs.PC) + (ReadMemory(emu.cpu.regs.PC + 1) << 8);

    // Load register A into the location.
    WriteMemory(writeAddress, emu.cpu.regs.A);

    // Skip over the 16-bit address.
    emu.cpu.regs.PC += 2;
}

//----------------------------------------//
// This will load the Stack Pointer into a//
// given memory location.                 //
//----------------------------------------//
void z80_LD_location_SP()
{
    unsigned short int writeAddress;
    writeAddress = ReadMemory(emu.cpu.regs.PC) + (ReadMemory(emu.cpu.regs.PC + 1) << 8);

    WriteMemory(writeAddress, emu.cpu.regs.SPL);
    writeAddress++;
    WriteMemory(writeAddress, emu.cpu.regs.SPH);

    // Skip over the 16-bit address.
    emu.cpu.regs.PC += 2;
}

//----------------------------------------//
// This instruction will load a 16-bit    //
// register with a given value.           //
//----------------------------------------//
void z80_LD_reg16_value(unsigned char *hiReg, unsigned char *loReg)
{
    // Load the 16-bit value into the registers.
    *loReg = ReadMemory(emu.cpu.regs.PC);
    emu.cpu.regs.PC++;
    *hiReg = ReadMemory(emu.cpu.regs.PC);
    emu.cpu.regs.PC++;
}

//----------------------------------------//
// This instruction will load the address //
// at a 16-bit pointer register with the  //
// value in register A.                   //
//----------------------------------------//
void z80_LD_location_reg16_A(unsigned short int *reg)
{
    WriteMemory(*reg, emu.cpu.regs.A);
}

//----------------------------------------//
// This instruction will load an 8-bit    //
// register with a given value.           //
//----------------------------------------//
void z80_LD_reg8_value(unsigned char *reg)
{
    // Load *reg with the 8-bit value immediately after it.
    *reg = ReadMemory(emu.cpu.regs.PC);

    // Increment the program counter to skip the value.
    emu.cpu.regs.PC++;
}

//----------------------------------------//
// This instruction will load an 8-bit    //
// register with the value at memory      //
// location (HL).                         //
//----------------------------------------//
void z80_LD_reg8_location_HL(unsigned char *reg)
{
    // Load the value at the address in HL into *reg.
    *reg = ReadMemory(emu.cpu.regs.HL);
}

//----------------------------------------//
// This instruction will load an 8-bit    //
// register with the value of another     //
// 8-bit register.                        //
//----------------------------------------//
void z80_LD_reg8_reg8(unsigned char *reg1, unsigned char *reg2)
{
    // Load the value in *reg2 into *reg1.
    *reg1 = *reg2;
}

//----------------------------------------//
// This instruction will load the Stack   //
// Pointer with the value of register HL. //
//----------------------------------------//
void z80_LD_SP_HL()
{
    // Load register HL into Stack Pointer.
    emu.cpu.regs.SP = emu.cpu.regs.HL;
}

//----------------------------------------//
// This instruction loads register A into //
// memory location 0xFF00 plus register C.//
//----------------------------------------//
void z80_LD_0xFF00_C_A()
{
    WriteMemory(0xFF00 + emu.cpu.regs.C, emu.cpu.regs.A);
}

//----------------------------------------//
// This instruction will load the value   //
// of register A into memory at the       //
// location of 0xFF00 plus an 8-bit value.//
//----------------------------------------//
void z80_LD_0xFF00_immediate_A()
{
    // Write the value of register A into memory.
    WriteMemory(0xFF00 + ReadMemory(emu.cpu.regs.PC), emu.cpu.regs.A);

    // Increment Program Counter
    emu.cpu.regs.PC++;
}

// Load A with data at location HL and decrement HL.
void z80_LDD_A_HL()
{
    emu.cpu.regs.A = ReadMemory(emu.cpu.regs.HL);
    emu.cpu.regs.HL--;
}

// Load location HL with data from register A and decrement HL.
void z80_LDD_HL_A()
{
    WriteMemory(emu.cpu.regs.HL, emu.cpu.regs.A);
    emu.cpu.regs.HL--;
}

// Load A with data at location HL and increment HL.
void z80_LDI_A_HL()
{
    emu.cpu.regs.A = ReadMemory(emu.cpu.regs.HL);
    emu.cpu.regs.HL++;
}

// Load location HL with data from register A and increment HL.
void z80_LDI_HL_A()
{
    WriteMemory(emu.cpu.regs.HL, emu.cpu.regs.A);
    emu.cpu.regs.HL++;
}

//----------------------------------------//
// This instruction will set(turn on) a   //
// given bit of an 8-bit register.        //
//----------------------------------------//
void z80_SET_bit_reg8(unsigned char bit, unsigned char *reg)
{
    *reg |= bit;
}

//----------------------------------------//
// This instruction shifts bit 7 of an    //
// 8-bit register left into the carry flag//
// A 0 is shifted into bit 0.             //
//----------------------------------------//
void z80_SLA_reg8(unsigned char *reg)
{
    // Put *reg's old bit 7 data in Carry flag.
    if (*reg & BIT_7)
        emu.cpu.regs.F |= FLAG_C_ON;
    else
        emu.cpu.regs.F &= FLAG_C_OFF;

    // Shift *reg left once.  0 is shifted in from right.
    *reg <<= 1;

    // If result is 0, set flag Z, otherwise reset it.
    if (*reg == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    // Flags N and H are reset.
    emu.cpu.regs.F &= (FLAG_N_OFF & FLAG_H_OFF);
}

//----------------------------------------//
// This instruction shifts bit 0 of an    //
// 8-bit register right into the carry    //
// flag.  Bit 7 doesn't change.           //
//----------------------------------------//
void z80_SRA_reg8(unsigned char *reg)
{
    // Put *reg's old bit 0 data in Carry flag.
    if (*reg & BIT_0)
        emu.cpu.regs.F |= FLAG_C_ON;
    else
        emu.cpu.regs.F &= FLAG_C_OFF;

    // Shift *reg right once.  0 is shifted in from left.  If bit 7
    // is set, make sure it stays set.
    if (*reg & BIT_7)
        *reg = (*reg >> 1) + BIT_7;
    else
        *reg >>= 1;

    // If result is 0, set flag Z, otherwise reset it.
    if (*reg == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    // Flags N and H are reset.
    emu.cpu.regs.F &= (FLAG_N_OFF & FLAG_H_OFF);
}

//----------------------------------------//
// This instruction shifts bit 0 of an    //
// 8-bit register right into the carry    //
// flag.  A 0 is shifted into bit 7.      //
//----------------------------------------//
void z80_SRL_reg8(unsigned char *reg)
{
    // Put *reg's old bit 0 data in Carry flag.
    if (*reg & BIT_0)
        emu.cpu.regs.F |= FLAG_C_ON;
    else
        emu.cpu.regs.F &= FLAG_C_OFF;

    // Shift *reg right once.  0 is shifted in from right.
    *reg >>= 1;

    // If result is 0, set flag Z, otherwise reset it.
    if (*reg == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    // Flags N and H are reset.
    emu.cpu.regs.F &= (FLAG_N_OFF & FLAG_H_OFF);
}

//----------------------------------------//
// This halts the GB CPU until a button is//
// pressed.                               //
//----------------------------------------//
void z80_STOP()
{
    // Don't allow STOP if interrupts are disabled.
    //if (emu.state.intrpEnable == 1)
        emu.state.stopped = 1;
    // Skip over the extra 0x00
    emu.cpu.regs.PC++;
}

//----------------------------------------//
// This instruction will subtract a given //
// value from register A.                 //
//----------------------------------------//
void z80_SUB_immediate()
{
    unsigned char value;
    value = ReadMemory(emu.cpu.regs.PC);

    // Check for a borrow from bit 4.
    if ((emu.cpu.regs.A & 0xF) < (value & 0xF))
        emu.cpu.regs.F |= FLAG_H_ON;
    else
        emu.cpu.regs.F &= FLAG_H_OFF;

    // Set flag C if result will be below 0.
    if ((emu.cpu.regs.A - value) < 0)
        emu.cpu.regs.F |= FLAG_C_ON;
    else
        emu.cpu.regs.F &= FLAG_C_OFF;

    emu.cpu.regs.A -= value;

    // If result is 0, set flag Z, otherwise reset it.
    if (emu.cpu.regs.A == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    // Set N since the operation was a subtraction.
    emu.cpu.regs.F |= FLAG_N_ON;

    emu.cpu.regs.PC++;
}

//----------------------------------------//
// This instruction will subtract an 8-bit//
// register's value from register A.      //
//----------------------------------------//
void z80_SUB_reg8(unsigned char *reg)
{
    // Check for a borrow from bit 4.
    if ((emu.cpu.regs.A & 0xF) < (*reg & 0xF))
        emu.cpu.regs.F |= FLAG_H_ON;
    else
        emu.cpu.regs.F &= FLAG_H_OFF;

    // Set flag C if result will be below 0.
    if ((emu.cpu.regs.A - *reg) < 0)
        emu.cpu.regs.F |= FLAG_C_ON;
    else
        emu.cpu.regs.F &= FLAG_C_OFF;

    emu.cpu.regs.A -= *reg;

    // If result is 0, set flag Z, otherwise reset it.
    if (emu.cpu.regs.A == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    // Set N since the operation was a subtraction.
    emu.cpu.regs.F |= FLAG_N_ON;
}

//----------------------------------------//
// This instruction swaps the higher and  //
// lower 4-bits of an 8-bit register.     //
//----------------------------------------//
void z80_SWAP_reg8(unsigned char *reg)
{
    // Swap the upper and lower nibbles of *reg.
    *reg = (((*reg & 0xF0) >> 4) | ((*reg & 0x0F) << 4));

    // If the result was 0, set flag Z, otherwise reset it.
    if(*reg == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    // Other flags are reset.
    emu.cpu.regs.F &= (FLAG_N_OFF & FLAG_H_OFF & FLAG_C_OFF);
}

//----------------------------------------//
// This will exclusive OR a given value   //
// and register A.                        //
//----------------------------------------//
void z80_XOR_immediate()
{
    // Logically Exclusive OR register A and an immediate value.
    emu.cpu.regs.A ^= ReadMemory(emu.cpu.regs.PC);

    // If the result was 0, set flag Z, otherwise reset it.
    if (emu.cpu.regs.A == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    // Other flags are reset.
    emu.cpu.regs.F &= (FLAG_N_OFF & FLAG_H_OFF & FLAG_C_OFF);

    // Increment Program Counter.
    emu.cpu.regs.PC++;
}

//----------------------------------------//
// This will exclusive OR an 8-bit        //
// register and register A.               //
//----------------------------------------//
void z80_XOR_reg8(unsigned char *reg)
{
    // Logically Exclusive OR register A and *reg.
    emu.cpu.regs.A ^= *reg;

    // If the result was 0, set flag Z, otherwise reset it.
    if (emu.cpu.regs.A == 0)
        emu.cpu.regs.F |= FLAG_Z_ON;
    else
        emu.cpu.regs.F &= FLAG_Z_OFF;

    // Other flags are reset.
    emu.cpu.regs.F &= (FLAG_N_OFF & FLAG_H_OFF & FLAG_C_OFF);
}

//----------------------------------------//
// This function will initialize the GB   //
// to it's startup state.
//----------------------------------------//
int EmulationInitialize(unsigned char *fileBuffer, unsigned int fileSize)
{
    //emu.cart.dataBuffer = (unsigned char *)malloc(fileSize);
    //if (!emu.cart.dataBuffer)
    //  return -1;
    memcpy(&emu.cart.dataBuffer[0], &fileBuffer[0], fileSize);  // Copy ROM file data to cartridge buffer 
    if (emu.cart.dataBuffer[0x0143] == 0x80)
        emu.cart.systemType = SYSTEM_CGB;
    if (emu.cart.dataBuffer[0x0146] == 0x03)
        emu.cart.systemType = SYSTEM_SGB;
    if (emu.cart.dataBuffer[0x0146] == 0x00)
        emu.cart.systemType = SYSTEM_GB;

    if ((emu.cart.systemType != SYSTEM_GB) && (emu.cart.systemType != SYSTEM_SGB)) // Only original Gameboy supported for now.
        return -1;

    emu.cart.mbcType = emu.cart.dataBuffer[0x147];
    //----------------------------------------//
    // Set the CPU and IO register startup    //
    // values.                                //
    //----------------------------------------//
//  if (emu.cart.systemType == SYSTEM_GB)
//  {
        IOregister_NR52 = 0xF1;
        emu.cpu.regs.AF = 0x01B0;
//  }
//  else
//      emu.cpu.regs.AF = 0x00B0;
    emu.cpu.regs.BC = 0x0013;
    emu.cpu.regs.DE = 0x00D8;
    emu.cpu.regs.HL = 0x014D;
    emu.cpu.regs.SP = 0xFFFE;
    emu.cpu.regs.PC = 0x0100;

    IOregister_P1 =     0xCF;
    IOregister_SB =     0x00;
    IOregister_SC =     0x7E;
    IOregister_DIV =    0xAB;
    IOregister_TIMA =   0x00;
    IOregister_TMA =    0x00;
    IOregister_TAC =    0x00;
    IOregister_IF =     0xE1;
    IOregister_NR10 =   0x80;
    IOregister_NR11 =   0xBF;
    IOregister_NR12 =   0xF3;
    IOregister_NR14 =   0xBF;
    IOregister_NR21 =   0xF3;
    IOregister_NR12 =   0xF3;
    IOregister_NR22 =   0x00;
    IOregister_NR24 =   0xBF;
    IOregister_NR30 =   0x7F;
    IOregister_NR31 =   0xFF;
    IOregister_NR32 =   0x9F;
    IOregister_NR33 =   0xBF;
    IOregister_NR41 =   0xFF;
    IOregister_NR42 =   0x00;
    IOregister_NR43 =   0x00;
    IOregister_NR44 =   0xBF;
    IOregister_NR50 =   0x77;
    IOregister_NR51 =   0xF3;
    IOregister_LCDC =   0x91;
    IOregister_STAT =   0x81;
    IOregister_SCY =    0x00;
    IOregister_SCX =    0x00;
    IOregister_LY =     0x90;
    IOregister_LYC =    0x00;
    IOregister_DMA =    0x00;
    IOregister_BGP =    0xFC;
    IOregister_OBP0 =   0xFF;
    IOregister_OBP1 =   0xFF;
    IOregister_WY =     0x00;
    IOregister_WX =     0x07;
    IOregister_IE =     0x00;

    IntrpMasterEnable = 1;

    // Assume standard Gameboy opcode cycles for now
    memcpy(&emu.cycles.opCycles[0], &GB_CycleTable[0], 0x100);
    memcpy(&emu.cycles.opCBCycles[0], &GB_CBCycleTable[0], 0x100);

    //----------------------------------------//
    // Load the base ROM (32K).               //
    //----------------------------------------//
    memcpy(&emu.memory.romBank0[0x0000], &emu.cart.dataBuffer[0x0000], 0x4000);
    memcpy(&emu.memory.romBank1[0x0000], &emu.cart.dataBuffer[0x4000], 0x4000);

    // Clear the video RAM.
    memset(&emu.memory.videoRam, 0, 0x2000);

    emu.cycles.statCycles = 0;
    emu.cycles.internalCounterCycles = 0xABCC;

    emu.state.halted = 0;
    emu.state.haltInstructionRepeat = 0;
    emu.state.stopped = 0;

    return 1;   
}

//----------------------------------------//
// This function takes care of the main GB//
// CPU processes.                         //
//----------------------------------------//
void RunEmulation()
{
    unsigned int systemRunning = 1;
    unsigned int conditionalCycles = 0;
    unsigned int cyclesRan = 0;

    while (systemRunning)
    {
        cyclesRan += HandleInterrupts();

        if (emu.state.eiDelay == 1)
        {
            emu.state.eiDelay = 0;
            IntrpMasterEnable = 1;
        }

                    // Get the value from memory at the HL pointer in case an operation needs it.
        hlMemVal = ReadMemory(emu.cpu.regs.HL);

        // Don't actually execute an opcode if the system is halted or stopped.
        if ((emu.state.halted == 0) && (emu.state.stopped == 0))
        {
            opcode = ReadMemory(emu.cpu.regs.PC);

            if (emu.state.haltInstructionRepeat == 1) // If the halt bug occured, don't increment PC for one instruction.
                emu.state.haltInstructionRepeat = 0;
            else
                emu.cpu.regs.PC++;

            // Use the opcode to call the appropriate function.
            switch (opcode)
            {
   // *switch statement covering all opcodes from 0x00 to 0xFF
                    // Cover the special 0xCB opcodes.
                case 0xCB:
                {
                    cbOpcode = ReadMemory(emu.cpu.regs.PC);
                    emu.cpu.regs.PC++;
                    switch (cbOpcode)
                    {
    // *switch statement covering special CB opcodes.
                    }
                    break;
                    }
                }
                break;

                }
                break;
                }
            }

        // If the opcode was 0xCB, add cycles from the bit operation cycle table.
        if (opcode == 0xCB)
            cyclesRan += emu.cycles.opCBCycles[cbOpcode];
        else
            cyclesRan += emu.cycles.opCycles[opcode] + conditionalCycles;

        // If the last instruction ran was a HALT or a STOP, run cycles until the system resumes.
        if ((opcode == 0x76) || (opcode == 0x10))
            cyclesRan += 4;

        if (IOregister_LCDC & BIT_7)
            emu.cycles.statCycles += cyclesRan;

        emu.cycles.internalCounterCycles += cyclesRan;

        UpdateIORegisters();

        conditionalCycles = 0;  // Reset the conditional cycles added if an instruction's condition was true.
        cyclesRan = 0;  // Reset the total cycles ran.

        HandleSDLEvents();
    }
}
\$\endgroup\$
  • \$\begingroup\$ " are there parts of the code that stick out and say "unprofessional"?" 1) Try compiling this code using only what is posted and <> headers. #include "define.h" belongs here as part of the review. 2) The nice tables for GB_CycleTable[0x100] and others are not productive - too much effort to maintain. Use auto formating. \$\endgroup\$ – chux Sep 25 '18 at 18:42
  • \$\begingroup\$ Thanks for the suggestions. I would've liked to include the relevant code, but the body character limit prevented it. \$\endgroup\$ – Benjamin Crew Sep 25 '18 at 21:00
3
\$\begingroup\$

I'm just looking for some general critiques.

Fixed width types

Rather than int, unsigned char, unsigned short, I'd go right into using int32_t, uint8_t, uint16_t. It conveys code's intent and is more portable amongst platforms with variant int size.

Fixed width types do prevent portability to non 2's complement machines and those with CHAR_BIT > 8, yet those rare platforms are likely very troublesome to code this task for anyways.

Complete memory map

Consider a complete 64k map including the unusable locations. Perhaps use a union of 64k bytes. Certain to simplify ReadMemory() and WriteMemory().

Reliance on packed structures

regs only makes sense if struct are packed. As packing tends to be implementation defined, consider adding _Static_assert() to insure adherence.

    union Registers
    {
        struct { unsigned short int AF, BC, DE, HL, SP, PC; };
        struct { unsigned char F, A, C, B, E, D, L, H, SPL, SPH, PCL, PCH; };
    } regs;

    _Static_assert(offsetof(regs, PC) == 10, "Oops, `short` not packed as expected");  
    _Static_assert(offsetof(regs, PCH) == 11, "Oops, `char` not packed as expected");  

Use more than one .c file

Perhaps one for instructions, memory, etc?

Unneeded code

address >= 0x0000 is always true as address is unsigned. It may look symmetric in code presentation, yet it can trigger warnings. Recommend deletion.

Unclear code source

BIT_7, BIT_0 and many others are not defined here. Where are they defined? If in #include "define.h", I'd expect that file posted here too so reviewers may successfully compile.

Minor: Powers of 2 as decimal

Powers of 2 as decimal constants, more than 1024, like 16777216 tend to be easy to mis-code and harder to review.

Code could use 16*1024*1204 but that has problems too. See There are reasons not to use 1000 * 1000 * 1000

Usually better to append a u to insure the constant is unsigned when coding powers-of-2 and especially powers-of-2-minus-1.

Minor: Format to presentation width

Code should readily accommodate, through an auto formatter, the presentation width and not run-off to the right.

unsigned char GB_CycleTable[0x100] = { //0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F
                                 /*0x00*/  4,  12,   8,   8,   4,   4,   8,   4,  20,   8,   8,   8,   4,   4,   8,   4,
                                 /*0x10*/  0,  12,   8,   8,   4,   4,   8,   4,  12,   8,   8,   8,   4,   4,   8,   4,
                                 /*0x20*/  8,  12,   8,   8,   4,   4,   8,   4,   8,   8,   8,   8,   4,   4,   8,   4,
                                 /*0x30*/  8,  12,   8,   8,  12,  12,  12,   4,   8,   8,   8,   8,   4,   4,   8,   4,
...
                                 /*0xF0*/ 12,  12,   8,   4,   0,  16,   8,  16,  12,   8,  16,   4,   0,   0,   8,  16
};

vs.

unsigned char GB_CycleTable[0x100] = {
    /*0x00*/4, 12, 8, 8, 4, 4, 8, 4, 20, 8, 8, 8, 4, 4, 8, 4,
    /*0x10*/0, 12, 8, 8, 4, 4, 8, 4, 12, 8, 8, 8, 4, 4, 8, 4,
    /*0x20*/8, 12, 8, 8, 4, 4, 8, 4, 8, 8, 8, 8, 4, 4, 8, 4,
    ....

Other alternatives

Since all are small multiples of 4, use #define or enum

#define M1 4
#define M2 8
#define M3 12
#define M4 16
#define M5 20
#define M6 24

/*0x00*/ M1, M3, M2, M2, M1, M1, M2, M1, M5, M2, M2, M2, M1, M1, M2, M1,

If you really want to line up the columns, use 8 0xXX values/line.

In this narrow case, I'd even consider 2-digit octal as all values appear to be < 64.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.