From e7c0d1f04dd12e16476dfaeb4c86cc4a4ca63eed Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Fri, 10 Jul 2020 20:43:11 +0100 Subject: [PATCH] Initial commit Background support only. No input support. No sound. Passes all Blargg instruction tests. Supports Tetris. --- .gitignore | 2 + CMakeLists.txt | 6 + include/Bytes.h | 40 + include/CPU.h | 86 + include/Control.h | 26 + include/CoreMemory.h | 20 + include/Display.h | 26 + include/GPU.h | 52 + include/GUI.h | 18 + include/Gameboy.h | 30 + include/Instructions.h | 26 + include/LogFile.h | 19 + include/Memory.h | 31 + include/MemoryHook.h | 23 + include/MemoryMap.h | 39 + include/MonochromePalette.h | 17 + include/Pixels.h | 29 + include/Rom.h | 27 + include/Tile.h | 29 + include/TileMap.h | 31 + include/TileSet.h | 24 + include/Timer.h | 28 + include/Types.h | 15 + include/VRAM.h | 23 + src/Bytes.cpp | 125 ++ src/CPU.cpp | 319 ++++ src/Control.cpp | 22 + src/CoreMemory.cpp | 37 + src/Display.cpp | 65 + src/GPU.cpp | 207 +++ src/Gameboy.cpp | 24 + src/Instructions.cpp | 3011 +++++++++++++++++++++++++++++++++++ src/LogFile.cpp | 30 + src/Memory.cpp | 108 ++ src/MonochromePalette.cpp | 30 + src/Pixels.cpp | 56 + src/Rom.cpp | 33 + src/Tile.cpp | 56 + src/TileMap.cpp | 33 + src/TileSet.cpp | 41 + src/Timer.cpp | 81 + src/VRAM.cpp | 24 + 42 files changed, 4969 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 include/Bytes.h create mode 100644 include/CPU.h create mode 100644 include/Control.h create mode 100644 include/CoreMemory.h create mode 100644 include/Display.h create mode 100644 include/GPU.h create mode 100644 include/GUI.h create mode 100644 include/Gameboy.h create mode 100644 include/Instructions.h create mode 100644 include/LogFile.h create mode 100644 include/Memory.h create mode 100644 include/MemoryHook.h create mode 100644 include/MemoryMap.h create mode 100644 include/MonochromePalette.h create mode 100644 include/Pixels.h create mode 100644 include/Rom.h create mode 100644 include/Tile.h create mode 100644 include/TileMap.h create mode 100644 include/TileSet.h create mode 100644 include/Timer.h create mode 100644 include/Types.h create mode 100644 include/VRAM.h create mode 100644 src/Bytes.cpp create mode 100644 src/CPU.cpp create mode 100644 src/Control.cpp create mode 100644 src/CoreMemory.cpp create mode 100644 src/Display.cpp create mode 100644 src/GPU.cpp create mode 100644 src/Gameboy.cpp create mode 100644 src/Instructions.cpp create mode 100644 src/LogFile.cpp create mode 100644 src/Memory.cpp create mode 100644 src/MonochromePalette.cpp create mode 100644 src/Pixels.cpp create mode 100644 src/Rom.cpp create mode 100644 src/Tile.cpp create mode 100644 src/TileMap.cpp create mode 100644 src/TileSet.cpp create mode 100644 src/Timer.cpp create mode 100644 src/VRAM.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5acb669 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build +.vscode diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..99f390c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,6 @@ +project(mboy_core) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") +include_directories(include) +file(GLOB SOURCES "src/*.cpp") +add_library(mboy_core STATIC ${SOURCES}) diff --git a/include/Bytes.h b/include/Bytes.h new file mode 100644 index 0000000..be121eb --- /dev/null +++ b/include/Bytes.h @@ -0,0 +1,40 @@ +// +// Created by matthew on 05/07/2020. +// + +#ifndef MY_APPLICATION_BYTES_H +#define MY_APPLICATION_BYTES_H + +#include "Types.h" + +class Bytes { +public: + static uint8 setBit_8(uint8 value, uint8 index); + static uint8 getBit_8(uint8 value, uint8 index); + static uint8 clearBit_8(uint8 value, uint8 index); + static uint8 join_4(uint8 upper, uint8 lower); + static uint16 join_8(uint8 upper, uint8 lower); + static uint32 join_32(uint8 first, uint8 second, uint8 third); + static uint8 split_8_upper(uint8 value); + static uint8 split_8_lower(uint8 value); + static uint8 split_16_upper(uint16 value); + static uint8 split_16_lower(uint16 value); + static bool isHalfCarrySub_8(uint8 value_1, uint8 value_2); + static bool isHalfCarrySub_8_three(uint8 value_1, uint8 value_2, uint8 value_3); + static bool isHalfCarryAdd_8(uint8 value_1, uint8 value_2); + static bool isHalfCarryAdd_8_three(uint8 value_1, uint8 value_2, uint8 value_3); + static bool isHalfCarryAdd_16(uint16 value_1, uint16 value_2); + static bool isCarrySub_8_three(uint8 value_1, uint8 value_2, uint8 value_3); + static bool isCarryAdd_8(uint8 value_1, uint8 value_2); + static bool isCarryAdd_8_three(uint8 value_1, uint8 value_2, uint8 value_3); + static bool isCarryAdd_16(uint16 value_1, uint16 value_2); + static uint8 wrappingAdd_8(uint8 value, uint8 add); + static uint16 wrappingAdd_16(uint16 value, uint16 add); + static uint8 wrappingSub_8(uint8 value, uint8 sub); + static uint16 wrappingSub_16(uint16 value, uint16 sub); + static uint8 rotateRight_8(uint8 value, uint8 n); + static uint8 rotateLeft_8(uint8 value, uint8 n); + static int8 toSigned_8(uint8 value); +}; + +#endif //MY_APPLICATION_BYTES_H diff --git a/include/CPU.h b/include/CPU.h new file mode 100644 index 0000000..a6e0810 --- /dev/null +++ b/include/CPU.h @@ -0,0 +1,86 @@ +// +// Created by matthew on 04/07/2020. +// + +#ifndef MY_APPLICATION_CPU_H +#define MY_APPLICATION_CPU_H + +#include "Memory.h" +#include "Types.h" +#include "MemoryHook.h" +#include "LogFile.h" + +enum Register { + A, + B, + C, + D, + E, + H, + L, + BC, + DE, + HL, + AF +}; + +class CPU : MemoryHook { + +public: + uint8 a; + uint8 b; + uint8 c; + uint8 d; + uint8 e; + uint8 h; + uint8 l; + uint16 sp; + uint16 pc; + + bool flag_z; + bool flag_n; + bool flag_h; + bool flag_c; + + bool interruptsEnabled; + bool halt; + bool swapSpeed; + bool currentSpeed; + + bool interruptEnableVerticalBlank = false; + bool interruptEnableLcd = false; + bool interruptEnableTimer = false; + bool interruptEnableSerial = false; + bool interruptEnableJoypad = false; + + bool interruptFlagsVerticalBlank = false; + bool interruptFlagsLcd = false; + bool interruptFlagsTimer = false; + bool interruptFlagsSerial = false; + bool interruptFlagsJoypad = false; + + LogFile logFile; + + CPU(); + uint8 get_8(Register cpuRegister); + uint8 get_f(); + void set_f(uint8 value); + uint16 get_16(Register cpuRegister); + void set_8(Register cpuRegister, uint8 value); + void set_16(Register cpuRegister, uint16 value); + uint16 step(Memory* memory, uint32 count, bool debug); + uint8 getInterruptEnable(); + uint8 getInterruptFlags(); + void setInterruptEnable(uint8 value); + void setInterruptFlags(uint8 value); + void flagInterrupt(uint8 bit); + +private: + uint16 checkInterrupts(Memory* memory); + uint8 get_8(uint16 address) override; + bool set_8(uint16 address, uint8 value) override; + void flag_interrupt(uint8 bit) override; +}; + + +#endif //MY_APPLICATION_CPU_H diff --git a/include/Control.h b/include/Control.h new file mode 100644 index 0000000..b19be11 --- /dev/null +++ b/include/Control.h @@ -0,0 +1,26 @@ +// +// Created by matthew on 05/07/2020. +// + +#ifndef MY_APPLICATION_CONTROL_H +#define MY_APPLICATION_CONTROL_H + +#include "Types.h" +#include "Memory.h" + +class Control { +public: + bool background; + bool sprites; + bool largeSprites; + bool alternateBackgroundTileMap; + bool alternateBackgroundTileSet; + bool window; + bool alternateWindowTileMap; + bool display; + + Control(uint8 value); + Control(Memory* memory); +}; + +#endif //MY_APPLICATION_CONTROL_H diff --git a/include/CoreMemory.h b/include/CoreMemory.h new file mode 100644 index 0000000..51ffa38 --- /dev/null +++ b/include/CoreMemory.h @@ -0,0 +1,20 @@ +// +// Created by matthew on 05/07/2020. +// + +#ifndef MY_APPLICATION_COREMEMORY_H +#define MY_APPLICATION_COREMEMORY_H + +#include "Types.h" + +class CoreMemory { +private: + uint8 data[64 * 1024] = {0}; +public: + CoreMemory(); + uint8 get_8(uint16 address); + void set_8(uint16 address, uint8 value); +}; + + +#endif //MY_APPLICATION_COREMEMORY_H diff --git a/include/Display.h b/include/Display.h new file mode 100644 index 0000000..a98b1f1 --- /dev/null +++ b/include/Display.h @@ -0,0 +1,26 @@ +// +// Created by matthew on 06/07/2020. +// + +#ifndef MY_APPLICATION_DISPLAY_H +#define MY_APPLICATION_DISPLAY_H + +#include "TileMap.h" +#include "Control.h" + +class Display : MemoryHook { +private: + Memory* memory; + TileMap tileMap_0; + TileMap tileMap_1; + TileSet tileSet_0; + TileSet tileSet_1; +public: + Display(Memory* memory); + void drawLine(Pixels* pixels, uint8 line, bool isColour, Control* control); + uint8 get_8(uint16 address) override; + bool set_8(uint16 address, uint8 value) override; +}; + + +#endif //MY_APPLICATION_DISPLAY_H diff --git a/include/GPU.h b/include/GPU.h new file mode 100644 index 0000000..7de7ab0 --- /dev/null +++ b/include/GPU.h @@ -0,0 +1,52 @@ +// +// Created by matthew on 05/07/2020. +// + +#ifndef MY_APPLICATION_GPU_H +#define MY_APPLICATION_GPU_H + +#include "Types.h" +#include "Memory.h" +#include "Control.h" +#include "Pixels.h" +#include "Display.h" +#include "GUI.h" +#include "LogFile.h" +#include + +const uint8 SCREEN_HEIGHT = 144; +const uint8 SCREEN_WIDTH = 160; + +class GPU : MemoryHook { +private: + uint8 mode; + uint16 cycleCount; + uint16 line; + Pixels pixels; + Display display; + Memory* memory; + Control* control; + bool coincidenceInterrupt; + bool oamInterrupt; + bool vblankInterrupt; + bool hblankInterrupt; + uint16 coincidenceLine; + GUI* gui; + LogFile logFile; + std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); + uint16 frameCount = 0; + +public: + GPU(Memory* memory, GUI* gui); + void step(uint16 lastInstructionDuration, Memory* memory, bool isColour, uint32 count); + uint8 getStat(); + void setStat(uint8 value); + void setControl(uint8 value); + +private: + uint8 get_8(uint16 address) override; + bool set_8(uint16 address, uint8 value) override; +}; + + +#endif //MY_APPLICATION_GPU_H diff --git a/include/GUI.h b/include/GUI.h new file mode 100644 index 0000000..57eb0d8 --- /dev/null +++ b/include/GUI.h @@ -0,0 +1,18 @@ +// +// Created by matthew on 06/07/2020. +// + +#ifndef MY_APPLICATION_GUI_H +#define MY_APPLICATION_GUI_H + +#include "Types.h" + +class GUI { +public: + virtual void displayBuffer(uint32* pixels) {}; + virtual bool isOpen() { return true; } + virtual void displayFPS(uint16 fps) {}; +}; + + +#endif //MY_APPLICATION_GUI_H diff --git a/include/Gameboy.h b/include/Gameboy.h new file mode 100644 index 0000000..1f362c4 --- /dev/null +++ b/include/Gameboy.h @@ -0,0 +1,30 @@ +// +// Created by matthew on 05/07/2020. +// + +#ifndef MY_APPLICATION_GAMEBOY_H +#define MY_APPLICATION_GAMEBOY_H + + +#include "Memory.h" +#include "CPU.h" +#include "Timer.h" +#include "GPU.h" +#include "GUI.h" + +class Gameboy { +private: + Memory memory; + CPU cpu; + GPU gpu; + CoreMemory coreMemory; + Rom* rom; + Timer timer; + GUI* gui; +public: + Gameboy(Rom rom, GUI* gui); + void run(); +}; + + +#endif //MY_APPLICATION_GAMEBOY_H diff --git a/include/Instructions.h b/include/Instructions.h new file mode 100644 index 0000000..a60fe8a --- /dev/null +++ b/include/Instructions.h @@ -0,0 +1,26 @@ +// +// Created by matthew on 05/07/2020. +// + +#ifndef MY_APPLICATION_INSTRUCTIONS_H +#define MY_APPLICATION_INSTRUCTIONS_H + +#include "Types.h" +#include "CPU.h" + +struct instructionInfo { + uint8 length; + uint8 cyclesAction; + uint8 cyclesNoAction; + char debug[20]; +}; + +class Instructions { +public: + static instructionInfo getInfo(uint8 index, uint8 arg); + static bool run(uint8 index, CPU* cpu, Memory* memory, uint8 arg_8, uint16 arg_16); + static void call(CPU* cpu, Memory* memory, uint16 value); +}; + + +#endif //MY_APPLICATION_INSTRUCTIONS_H diff --git a/include/LogFile.h b/include/LogFile.h new file mode 100644 index 0000000..f9760d9 --- /dev/null +++ b/include/LogFile.h @@ -0,0 +1,19 @@ +// +// Created by matthew on 06/07/2020. +// + +#ifndef MY_APPLICATION_LOGFILE_H +#define MY_APPLICATION_LOGFILE_H + +#include + +class LogFile { +private: + std::ofstream file; +public: + LogFile(std::string path); + void write(const std::string fmt, ...); +}; + + +#endif //MY_APPLICATION_LOGFILE_H diff --git a/include/Memory.h b/include/Memory.h new file mode 100644 index 0000000..82edec8 --- /dev/null +++ b/include/Memory.h @@ -0,0 +1,31 @@ +// +// Created by matthew on 05/07/2020. +// + +#ifndef MY_APPLICATION_MEMORY_H +#define MY_APPLICATION_MEMORY_H + +#include "Types.h" +#include "CoreMemory.h" +#include "Rom.h" +#include "MemoryHook.h" +#include "VRAM.h" + +class Memory { +public: + CoreMemory* coreMemory; + Rom* rom; + MemoryHook* cpu; + MemoryHook* gpu; + MemoryHook* timer; + uint8 column = 0; + + void init(CoreMemory* coreMemory, Rom* rom, MemoryHook* cpu, MemoryHook* gpu, MemoryHook* timer); + uint8 get_8(uint16 address); + void set_8(uint16 address, uint8 value); + void set_16(uint16 address, uint16 value); + void flag_interrupt(uint8 bit); + void dma(uint8 value); +}; + +#endif //MY_APPLICATION_MEMORY_H diff --git a/include/MemoryHook.h b/include/MemoryHook.h new file mode 100644 index 0000000..d26c3d6 --- /dev/null +++ b/include/MemoryHook.h @@ -0,0 +1,23 @@ +// +// Created by matthew on 05/07/2020. +// + +#ifndef MY_APPLICATION_MEMORYHOOK_H +#define MY_APPLICATION_MEMORYHOOK_H + +#include "Types.h" + +class MemoryHook { +public: + virtual uint8 get_8(uint16 address) { + return 0; + } + + virtual bool set_8(uint16 address, uint8 value) { + return false; + } + + virtual void flag_interrupt(uint8 bit) {}; +}; + +#endif //MY_APPLICATION_MEMORYHOOK_H diff --git a/include/MemoryMap.h b/include/MemoryMap.h new file mode 100644 index 0000000..5f396e5 --- /dev/null +++ b/include/MemoryMap.h @@ -0,0 +1,39 @@ +// +// Created by matthew on 05/07/2020. +// + +#ifndef MY_APPLICATION_MEMORYMAP_H +#define MY_APPLICATION_MEMORYMAP_H + +#include "Types.h" + +const uint16 ADDRESS_INTERRUPT_ENABLE = 0xFFFF; +const uint16 ADDRESS_INTERRUPT_FLAGS = 0xFF0F; +const uint16 ADDRESS_DIVIDER = 0xFF04; +const uint16 ADDRESS_STAT = 0xFF41; +const uint16 ADDRESS_TARGET_LINE = 0xFF45; +const uint16 ADDRESS_LINE = 0xFF44; +const uint16 ADDRESS_JOYPAD = 0xFF00; +const uint16 ADDRESS_VRAM_START = 0x8000; +const uint16 ADDRESS_VRAM_END = 0x9FFF; +const uint16 ADDRESS_VRAM_BANK = 0xFF4F; + +const uint16 TILE_SET_1_START = 0x8000; +const uint16 TILE_SET_1_END = 0x8FFF + 1; +const uint16 TILE_SET_0_START = 0x8800; +const uint16 TILE_SET_0_END = 0x97FF + 1; +const uint16 TILE_MAP_0_START = 0x9800; +const uint16 TILE_MAP_0_END = 0x9BFF + 1; +const uint16 TILE_MAP_1_START = 0x9C00; +const uint16 TILE_MAP_1_END = 0x9FFF + 1; +const uint16 BACKGROUND_PALETTE = 0xFF47; +const uint16 LCD_CONTROL = 0xFF40; + +const uint16 SCROLL_Y = 0xFF42; +const uint16 SCROLL_X = 0xFF43; + +const uint8 INTERRUPT_BIT_VERTICAL_BLANK = 0; +const uint8 INTERRUPT_BIT_LCD_STAT = 1; +const uint8 INTERRUPT_BIT_TIMER = 2; + +#endif //MY_APPLICATION_MEMORYMAP_H diff --git a/include/MonochromePalette.h b/include/MonochromePalette.h new file mode 100644 index 0000000..ab44168 --- /dev/null +++ b/include/MonochromePalette.h @@ -0,0 +1,17 @@ +// +// Created by matthew on 06/07/2020. +// + +#ifndef MY_APPLICATION_MONOCHROMEPALETTE_H +#define MY_APPLICATION_MONOCHROMEPALETTE_H + + +#include "Tile.h" + +class MonochromePalette { +public: + static palette get(uint8 value); +}; + + +#endif //MY_APPLICATION_MONOCHROMEPALETTE_H diff --git a/include/Pixels.h b/include/Pixels.h new file mode 100644 index 0000000..62bfc25 --- /dev/null +++ b/include/Pixels.h @@ -0,0 +1,29 @@ +// +// Created by matthew on 05/07/2020. +// + +#ifndef MY_APPLICATION_PIXELS_H +#define MY_APPLICATION_PIXELS_H + +#include "Types.h" + +const uint32 WHITE = 0x00FFFFFF; + +class Pixels { +private: + uint16 width; + uint16 height; + + uint16 getIndex(uint16 x, uint16 y); +public: + uint32* data; + + Pixels(uint16 width, uint16 height); + uint32 get(uint16 x, uint16 y); + void set(uint16 x, uint16 y, uint32 pixel); + void setLine(uint16 y, uint32* pixels, uint16 offset, uint16 width); + void clearLine(uint16 y, uint16 offset, uint16 width); + uint32* getLine(uint16 y, uint16 offset, uint16 width); +}; + +#endif //MY_APPLICATION_PIXELS_H diff --git a/include/Rom.h b/include/Rom.h new file mode 100644 index 0000000..f85e3b5 --- /dev/null +++ b/include/Rom.h @@ -0,0 +1,27 @@ +// +// Created by matthew on 04/07/2020. +// + +#ifndef MY_APPLICATION_ROM_H +#define MY_APPLICATION_ROM_H + +#include "Types.h" +#include + +class Rom { + +private: + uint8 rom[0xFFFF] = {0}; + + std::string readName(uint8* rom); + +public: + bool isColour = false; + std::string name; + + Rom(std::string filename); + uint8 get_8(uint16 address); + bool set_8(uint16 address, uint8 value); +}; + +#endif //MY_APPLICATION_ROM_H diff --git a/include/Tile.h b/include/Tile.h new file mode 100644 index 0000000..0da9ee1 --- /dev/null +++ b/include/Tile.h @@ -0,0 +1,29 @@ +// +// Created by matthew on 06/07/2020. +// + +#ifndef MY_APPLICATION_TILE_H +#define MY_APPLICATION_TILE_H + +#include "Types.h" +#include "Memory.h" +#include "Pixels.h" + +const uint16 TILE_SIZE = 8; +const uint16 TILE_SIZE_LARGE = 16; + +struct palette { + uint32 colours[4] = {0}; +}; + +class Tile { +private: + uint32 colourIndexes[TILE_SIZE * TILE_SIZE]; + bool large; +public: + Tile(Memory* memory, uint16 start, bool large, bool alternateBank); + void drawLine(Pixels* pixels, palette palette, uint16 localY, + uint16 targetX, uint16 targetY, bool flipX, bool flipY); +}; + +#endif //MY_APPLICATION_TILE_H diff --git a/include/TileMap.h b/include/TileMap.h new file mode 100644 index 0000000..b49d4d6 --- /dev/null +++ b/include/TileMap.h @@ -0,0 +1,31 @@ +// +// Created by matthew on 06/07/2020. +// + +#ifndef MY_APPLICATION_TILEMAP_H +#define MY_APPLICATION_TILEMAP_H + + +#include "Pixels.h" +#include "Memory.h" +#include "Tile.h" +#include "TileSet.h" + +const uint8 TILE_COUNT = 32; + +class TileMap { +private: + Memory* memory; + uint16 start; + uint16 end; +public: + Pixels pixels; + + TileMap(Memory* memory, uint16 start, uint16 end); + uint16 getIndex(uint16 tileX, uint16 tileY); + uint16 getAttributeIndex(uint16 tileX, uint16 tileY); + void drawTile(uint16 tileIndexX, uint16 tileIndexY, palette palette, TileSet* tileSet); +}; + + +#endif //MY_APPLICATION_TILEMAP_H diff --git a/include/TileSet.h b/include/TileSet.h new file mode 100644 index 0000000..3fbf678 --- /dev/null +++ b/include/TileSet.h @@ -0,0 +1,24 @@ +// +// Created by matthew on 06/07/2020. +// + +#ifndef MY_APPLICATION_TILESET_H +#define MY_APPLICATION_TILESET_H + +#include "Types.h" +#include "Memory.h" +#include "Tile.h" + +class TileSet : MemoryHook { +private: + uint16 start; + bool isSigned; + Tile* tileCache[256]; + bool tileCacheSet[256] = {[0 ... 255] = false}; +public: + TileSet(uint16 start, bool isSigned); + Tile* getTile(Memory* memory, uint8 index, bool large, bool alternateBank); + void clearCache(); +}; + +#endif //MY_APPLICATION_TILESET_H diff --git a/include/Timer.h b/include/Timer.h new file mode 100644 index 0000000..e4fe6a4 --- /dev/null +++ b/include/Timer.h @@ -0,0 +1,28 @@ +// +// Created by matthew on 05/07/2020. +// + +#ifndef MY_APPLICATION_TIMER_H +#define MY_APPLICATION_TIMER_H + +#include "Types.h" +#include "MemoryHook.h" +#include "Memory.h" + +class Timer : MemoryHook { +private: + uint16 divider = 0; + uint16 counter = 0; + uint16 modulo = 0; + uint16 speed = 256; + bool running = false; + uint16 dividerCycleCount = 0; + uint16 counterCycleCount = 0; +public: + void step(uint16 cycleCount, Memory* memory); + uint8 get_8(uint16 address) override; + bool set_8(uint16 address, uint8 value) override; +}; + + +#endif //MY_APPLICATION_TIMER_H diff --git a/include/Types.h b/include/Types.h new file mode 100644 index 0000000..2d44128 --- /dev/null +++ b/include/Types.h @@ -0,0 +1,15 @@ +// +// Created by matthew on 05/07/2020. +// + +#ifndef MY_APPLICATION_TYPES_H +#define MY_APPLICATION_TYPES_H + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef char int8; +typedef short int16; +typedef int int32; + +#endif //MY_APPLICATION_TYPES_H diff --git a/include/VRAM.h b/include/VRAM.h new file mode 100644 index 0000000..fcbadb1 --- /dev/null +++ b/include/VRAM.h @@ -0,0 +1,23 @@ +// +// Created by matthew on 06/07/2020. +// + +#ifndef MY_APPLICATION_VRAM_H +#define MY_APPLICATION_VRAM_H + +#include "Types.h" + +class VRAM { +private: + uint8 data[2][8 * 1024]; +public: + uint8 bank; + + void setBank(uint8 value); + uint8 get_8(uint16 address); + uint8 get_8(uint16 address, uint8 bank); + void set_8(uint16 address, uint8 value); +}; + + +#endif //MY_APPLICATION_VRAM_H diff --git a/src/Bytes.cpp b/src/Bytes.cpp new file mode 100644 index 0000000..6d23bc9 --- /dev/null +++ b/src/Bytes.cpp @@ -0,0 +1,125 @@ +// +// Created by matthew on 05/07/2020. +// + +#include "Bytes.h" + +uint8 Bytes::setBit_8(uint8 value, uint8 index) { + return value | (1 << index); +} + +uint8 Bytes::getBit_8(uint8 value, uint8 index) { + return (value & (1 << index)) != 0; +} + +uint16 Bytes::join_8(uint8 upper, uint8 lower) { + return (upper << 8) | lower; +} + +uint8 Bytes::split_16_upper(uint16 value) { + return value >> 8; +} + +uint8 Bytes::split_16_lower(uint16 value) { + return value & 0x00FF; +} + +uint8 Bytes::clearBit_8(uint8 value, uint8 index) { + return value & ~(1 << index); +} + +uint8 Bytes::join_4(uint8 upper, uint8 lower) { + return upper << 4 | lower; +} + +uint32 Bytes::join_32(uint8 first, uint8 second, uint8 third) { + return ((first << 16) | (second << 8)) | third; +} + +uint8 Bytes::split_8_upper(uint8 value) { + return value >> 4; +} + +uint8 Bytes::split_8_lower(uint8 value) { + return value & 0x0F; +} + +bool Bytes::isHalfCarrySub_8(uint8 value_1, uint8 value_2) { + int8 result = (value_1 & 0xF) - (value_2 & 0xF); + return result < 0; +} + +bool Bytes::isHalfCarrySub_8_three(uint8 value_1, uint8 value_2, uint8 value_3) { + int16 result = (value_1 & 0xF) - (value_2 & 0xF) - (value_3 & 0xF); + return result < 0; +} + +bool Bytes::isHalfCarryAdd_8(uint8 value_1, uint8 value_2) { + return (((value_1 & 0xF) + (value_2 & 0xF)) & 0x10) == 0x10; +} + +bool Bytes::isHalfCarryAdd_8_three(uint8 value_1, uint8 value_2, uint8 value_3) { + return (((value_1 & 0xF) + (value_2 & 0xF) + (value_3 & 0xF)) & 0x10) == 0x10; +} + +bool Bytes::isHalfCarryAdd_16(uint16 value_1, uint16 value_2) { + uint16 value_1_nibble_3 = value_1 & 0xFFF; + uint16 value_2_nibble_3 = value_2 & 0xFFF; + return ((value_1_nibble_3 + value_2_nibble_3) & 0x1000) == 0x1000; +} + +bool Bytes::isCarrySub_8_three(uint8 value_1, uint8 value_2, uint8 value_3) { + int16 result = value_1 - value_2 - value_3; + return result < 0; +} + +bool Bytes::isCarryAdd_8(uint8 value_1, uint8 value_2) { + uint16 result = value_1 + value_2; + return result > 255; +} + +bool Bytes::isCarryAdd_8_three(uint8 value_1, uint8 value_2, uint8 value_3) { + uint16 result = value_1 + value_2 + value_3; + return result > 255; +} + +bool Bytes::isCarryAdd_16(uint16 value_1, uint16 value_2) { + uint32 result = value_1 + value_2; + return result > 65535; +} + +uint8 Bytes::wrappingAdd_8(uint8 value, uint8 add) { + uint16 result = value + add; + if(result > 255) { + return result - 256; + } + return result; +} + +uint16 Bytes::wrappingAdd_16(uint16 value, uint16 add) { + uint32 result = value + add; + if(result > 65535) { + return result - 65536; + } + return result; +} + +uint8 Bytes::wrappingSub_8(uint8 value, uint8 sub) { + return value - sub; +} + +uint16 Bytes::wrappingSub_16(uint16 value, uint16 sub) { + return value - sub; +} + +uint8 Bytes::rotateRight_8(uint8 value, uint8 n) { + return ((value >> n) | (value << (8 - n))) & 0xFF; +} + +uint8 Bytes::rotateLeft_8(uint8 value, uint8 n) { + return ((((value) << n) & 0xFF) | (value >> (8 - n))) & 0xFF; +} + +int8 Bytes::toSigned_8(uint8 value) { + return value; +} diff --git a/src/CPU.cpp b/src/CPU.cpp new file mode 100644 index 0000000..e66c546 --- /dev/null +++ b/src/CPU.cpp @@ -0,0 +1,319 @@ +// +// Created by matthew on 04/07/2020. +// + +#include +#include "Types.h" +#include "Bytes.h" +#include "CPU.h" +#include "Instructions.h" +#include "MemoryMap.h" + +const uint16 STACK_POINTER_START = 0xFFFE; +const uint16 EXECUTION_START = 0x100; +const uint16 INTERRUPT_ROUTINE_VERTICAL_BLANK = 0x0040; +const uint16 INTERRUPT_ROUTINE_LCD = 0x0048; +const uint16 INTERRUPT_ROUTINE_TIMER = 0x0050; +const uint16 INTERRUPT_ROUTINE_SERIAL = 0x0058; +const uint16 INTERRUPT_ROUTINE_JOYPAD = 0x0060; +const std::string LOG_PATH = "/sdcard/Download/matterboy_log_cpu.txt"; + +CPU::CPU() : logFile(LOG_PATH) { + a = 0x11; + b = 0x00; + c = 0x13; + d = 0x00; + e = 0xD8; + h = 0x01; + l = 0x4D; + sp = STACK_POINTER_START; + pc = EXECUTION_START; + flag_z = true; + flag_n = false; + flag_h = true; + flag_c = true; + interruptsEnabled = false; + halt = false; + swapSpeed = false; + currentSpeed = 1; +} + +uint8 CPU::get_8(Register cpuRegister) { + switch(cpuRegister) { + case A: + return a; + case B: + return b; + case C: + return c; + case D: + return d; + case E: + return e; + case H: + return h; + case L: + return l; + default: + return 0; + } +} + +uint8 CPU::get_f() { + uint8 value = 0x0; + if(flag_z) { value = Bytes::setBit_8(value, 7); } + if(flag_n) { value = Bytes::setBit_8(value, 6); } + if(flag_h) { value = Bytes::setBit_8(value, 5); } + if(flag_c) { value = Bytes::setBit_8(value, 4); } + return value; +} + +void CPU::set_f(uint8 value) { + flag_z = Bytes::getBit_8(value, 7); + flag_n = Bytes::getBit_8(value, 6); + flag_h = Bytes::getBit_8(value, 5); + flag_c = Bytes::getBit_8(value, 4); +} + +uint16 CPU::get_16(Register cpuRegister) { + switch(cpuRegister) { + case BC: + return Bytes::join_8(b, c); + case DE: + return Bytes::join_8(d, e); + case HL: + return Bytes::join_8(h, l); + case AF: + return Bytes::join_8(a, get_f()); + default: + throw std::invalid_argument("Invalid 16-bit read from CPU."); + } +} + +void CPU::set_8(Register cpuRegister, uint8 value) { + switch(cpuRegister) { + case A: + a = value; + return; + case B: + b = value; + return; + case C: + c = value; + return; + case D: + d = value; + return; + case E: + e = value; + return; + case H: + h = value; + return; + case L: + l = value; + return; + default: + throw std::invalid_argument("Invalid write to CPU."); + } +} + +void CPU::set_16(Register cpuRegister, uint16 value) { + uint8 upper = Bytes::split_16_upper(value); + uint8 lower = Bytes::split_16_lower(value); + + switch(cpuRegister) { + case BC: + b = upper; + c = lower; + return; + case DE: + d = upper; + e = lower; + return; + case HL: + h = upper; + l = lower; + return; + case AF: + a = upper; + set_f(lower); + return; + default: + throw std::invalid_argument("Invalid 16-bit write to CPU."); + } +} + +uint16 CPU::step(Memory* memory, uint32 count, bool debug) { + uint8 instructionCode = memory->get_8(pc); + uint8 arg_1 = 0; + uint8 arg_2 = 0; + uint8 arg_8 = 0; + uint16 arg_16 = 0; + + if(instructionCode == 0xCB) { + arg_1 = memory->get_8(pc + 1); + arg_8 = arg_1; + } + + instructionInfo instruction = Instructions::getInfo(instructionCode, arg_8); + uint8 instructionLength = instruction.length; + + if(instructionLength > 1) { + arg_1 = memory->get_8(pc + 1); + arg_8 = arg_1; + } + + if(instructionLength > 2) { + arg_2 = memory->get_8(pc + 2); + arg_16 = Bytes::join_8(arg_2, arg_1); + } + + uint16 instructionDurationAction = instruction.cyclesAction; + uint16 instructionDurationNoAction = instruction.cyclesNoAction; + uint16 totalCycles = 0; + + //uint8 argLength = instruction.length - 1; + +// logFile.write("%d - 0x%04X - 0x%04X - %10s - %02X - %04X - AF=%04X BC=%04X DE=%04X HL=%04X SP=%04X - Z=%d N=%d H=%d C=%d", +// count, pc, instructionCode, instruction.debug, arg_8, arg_16, +// get_16(AF), get_16(BC), get_16(DE), get_16(HL), sp, +// flag_z, flag_n, flag_h, flag_c); + + if(!halt) { + pc += instructionLength; + bool action = Instructions::run(instructionCode, this, memory, arg_8, arg_16); + uint16 interruptCycles = checkInterrupts(memory); + uint16 instructionCycles = action ? instructionDurationAction : instructionDurationNoAction; + totalCycles = instructionCycles + interruptCycles; + } else { + totalCycles = 12 + checkInterrupts(memory); + } + + return totalCycles / currentSpeed; +} + +uint8 CPU::getInterruptEnable() { + uint8 value = 0; + + if(interruptEnableVerticalBlank) value = Bytes::setBit_8(value, 0); + if(interruptEnableLcd) value = Bytes::setBit_8(value, 1); + if(interruptEnableTimer) value = Bytes::setBit_8(value, 2); + if(interruptEnableSerial) value = Bytes::setBit_8(value, 3); + if(interruptEnableJoypad) value = Bytes::setBit_8(value, 4); + + return value; +} + +uint8 CPU::getInterruptFlags() { + uint8 value = 0; + + if(interruptFlagsVerticalBlank) value = Bytes::setBit_8(value, 0); + if(interruptFlagsLcd) value = Bytes::setBit_8(value, 1); + if(interruptFlagsTimer) value = Bytes::setBit_8(value, 2); + if(interruptFlagsSerial) value = Bytes::setBit_8(value, 3); + if(interruptFlagsJoypad) value = Bytes::setBit_8(value, 4); + + return value; +} + +void CPU::setInterruptEnable(uint8 value) { + interruptEnableVerticalBlank = Bytes::getBit_8(value, 0); + interruptEnableLcd = Bytes::getBit_8(value, 1); + interruptEnableTimer = Bytes::getBit_8(value, 2); + interruptEnableSerial = Bytes::getBit_8(value, 3); + interruptEnableJoypad = Bytes::getBit_8(value, 4); +} + +void CPU::setInterruptFlags(uint8 value) { + interruptFlagsVerticalBlank = Bytes::getBit_8(value, 0); + interruptFlagsLcd = Bytes::getBit_8(value, 1); + interruptFlagsTimer = Bytes::getBit_8(value, 2); + interruptFlagsSerial = Bytes::getBit_8(value, 3); + interruptFlagsJoypad = Bytes::getBit_8(value, 4); +} + +void CPU::flagInterrupt(uint8 bit) { + switch(bit) { + case 0: + interruptFlagsVerticalBlank = true; + return; + case 1: + interruptFlagsLcd = true; + return; + case 2: + interruptFlagsTimer = true; + return; + case 3: + interruptFlagsSerial = true; + return; + case 4: + interruptFlagsJoypad = true; + return; + default: + throw std::invalid_argument("Invalid interrupt flag."); + } +} + +uint16 CPU::checkInterrupts(Memory* memory) { + if (!halt && !interruptsEnabled) { + return 0; + } + + uint16 target = 0; + + if (interruptEnableVerticalBlank && interruptFlagsVerticalBlank) { + target = INTERRUPT_ROUTINE_VERTICAL_BLANK; + } else if (interruptEnableLcd && interruptFlagsLcd) { + target = INTERRUPT_ROUTINE_LCD; + } else if (interruptEnableTimer && interruptFlagsTimer) { + target = INTERRUPT_ROUTINE_TIMER; + } else if (interruptEnableSerial && interruptFlagsSerial) { + target = INTERRUPT_ROUTINE_SERIAL; + } else if (interruptEnableJoypad && interruptFlagsJoypad) { + target = INTERRUPT_ROUTINE_JOYPAD; + } + + if (target != 0) { + halt = false; + + if (interruptsEnabled) { + interruptsEnabled = false; + setInterruptFlags(0); + Instructions::call(this, memory, target); + return 20; + } + + return 0; + } + + return 0; +} + +uint8 CPU::get_8(uint16 address) { + switch(address) { + case ADDRESS_INTERRUPT_ENABLE: + return getInterruptEnable(); + case ADDRESS_INTERRUPT_FLAGS: + return getInterruptFlags(); + default: + throw std::invalid_argument("Invalid read from CPU."); + } +} + +bool CPU::set_8(uint16 address, uint8 value) { + switch(address) { + case ADDRESS_INTERRUPT_ENABLE: + setInterruptEnable(value); + return true; + case ADDRESS_INTERRUPT_FLAGS: + setInterruptFlags(value); + return true; + default: + throw std::invalid_argument("Invalid write to CPU."); + } +} + +void CPU::flag_interrupt(uint8 bit) { + flagInterrupt(bit); +} diff --git a/src/Control.cpp b/src/Control.cpp new file mode 100644 index 0000000..15cf08a --- /dev/null +++ b/src/Control.cpp @@ -0,0 +1,22 @@ +// +// Created by matthew on 05/07/2020. +// + +#include "Control.h" +#include "Bytes.h" +#include "MemoryMap.h" + +Control::Control(uint8 value) { + background = Bytes::getBit_8(value, 0); + sprites = Bytes::getBit_8(value, 1); + largeSprites = Bytes::getBit_8(value, 2); + alternateBackgroundTileMap = Bytes::getBit_8(value, 3); + alternateBackgroundTileSet = Bytes::getBit_8(value, 4); + window = Bytes::getBit_8(value, 5); + alternateWindowTileMap = Bytes::getBit_8(value, 6); + display = Bytes::getBit_8(value, 7); +} + +Control::Control(Memory *memory) { + Control(memory->coreMemory->get_8(LCD_CONTROL)); +} diff --git a/src/CoreMemory.cpp b/src/CoreMemory.cpp new file mode 100644 index 0000000..8587fe8 --- /dev/null +++ b/src/CoreMemory.cpp @@ -0,0 +1,37 @@ +// +// Created by matthew on 05/07/2020. +// + +#include "CoreMemory.h" +#include "Bytes.h" +#include "MemoryMap.h" + +CoreMemory::CoreMemory() { + + set_8(0xFF10, 0x80); + set_8(0xFF11, 0xBF); + set_8(0xFF12, 0xF3); + set_8(0xFF14, 0xBF); + set_8(0xFF16, 0x3F); + set_8(0xFF19, 0xBF); + set_8(0xFF1A, 0x7F); + set_8(0xFF1B, 0xFF); + set_8(0xFF1C, 0x9F); + set_8(0xFF1E, 0xBF); + set_8(0xFF20, 0xFF); + set_8(0xFF23, 0xBF); + set_8(0xFF24, 0x77); + set_8(0xFF25, 0xF3); + set_8(0xFF26, 0xF1); + set_8(0xFF47, 0xFC); + set_8(0xFF48, 0xFF); + set_8(0xFF48, 0xFF); +} + +uint8 CoreMemory::get_8(uint16 address) { + return data[address]; +} + +void CoreMemory::set_8(uint16 address, uint8 value) { + data[address] = value; +} diff --git a/src/Display.cpp b/src/Display.cpp new file mode 100644 index 0000000..fc94792 --- /dev/null +++ b/src/Display.cpp @@ -0,0 +1,65 @@ +// +// Created by matthew on 06/07/2020. +// + +#include "Display.h" +#include "MemoryMap.h" +#include "MonochromePalette.h" +#include "Bytes.h" +#include "GPU.h" + +Display::Display(Memory *memory) : +tileMap_0(memory, TILE_MAP_0_START, TILE_MAP_0_END), +tileMap_1(memory, TILE_MAP_1_START, TILE_MAP_1_END), +tileSet_0(TILE_SET_0_START, true), +tileSet_1(TILE_SET_1_START, false) { + this->memory = memory; +} + +static void drawBackgroundLine(Pixels* pixels, Memory* memory, Control* control, + TileMap* tileMap, TileSet* tileSet, palette palette, uint8 line) { + if(!control->background) return; + + uint16 scrollX = memory->coreMemory->get_8(SCROLL_X); + uint16 scrollY = memory->coreMemory->get_8(SCROLL_Y);; + uint16 localY = Bytes::wrappingAdd_8(scrollY, line); + uint16 tileIndexY = localY / TILE_SIZE; + uint16 scrollIndex = scrollX / TILE_SIZE; + uint16 lastScrollIndex = scrollIndex + 20; + int32 scrollWrapIndex = lastScrollIndex > TILE_COUNT ? lastScrollIndex - TILE_COUNT : -1; + + for(uint16 tileIndexX = 0; tileIndexX < TILE_COUNT; tileIndexX++) { + if(scrollWrapIndex == -1 && (tileIndexX < scrollIndex || tileIndexX > lastScrollIndex)) continue; + if(scrollWrapIndex != -1 && tileIndexX > scrollWrapIndex && tileIndexX < scrollIndex) continue; + + tileMap->drawTile(tileIndexX, tileIndexY, palette, tileSet); + } + + uint32* linePixels = tileMap->pixels.getLine(localY, scrollX, SCREEN_WIDTH); + pixels->setLine(line, linePixels, 0, SCREEN_WIDTH); + delete linePixels; +} + +void Display::drawLine(Pixels *pixels, uint8 line, bool isColour, Control *control) { + TileSet* tileSet = !control->alternateBackgroundTileSet ? &tileSet_0 : &tileSet_1; + TileMap* tileMap = !control->alternateBackgroundTileMap ? &tileMap_0 : &tileMap_1; + palette backgroundMonochromePalette = MonochromePalette::get(memory->get_8(BACKGROUND_PALETTE)); + + drawBackgroundLine(pixels, memory, control, tileMap, tileSet, backgroundMonochromePalette, line); +} + +uint8 Display::get_8(uint16 address) { + throw std::invalid_argument("Invalid read from display."); +} + +bool Display::set_8(uint16 address, uint8 value) { + if(address >= TILE_SET_0_START && address < TILE_SET_0_END) { + tileSet_0.clearCache(); + } + + if(address >= TILE_SET_1_START && address < TILE_SET_1_END) { + tileSet_1.clearCache(); + } + + return true; +} diff --git a/src/GPU.cpp b/src/GPU.cpp new file mode 100644 index 0000000..17bae52 --- /dev/null +++ b/src/GPU.cpp @@ -0,0 +1,207 @@ +// +// Created by matthew on 05/07/2020. +// + +#include "GPU.h" +#include "MemoryMap.h" +#include "Types.h" +#include "Bytes.h" + +const uint8 MODE_HORIZONTAL_BLANK = 0; +const uint8 MODE_VERTICAL_BLANK = 1; +const uint8 MODE_SCANLINE_SPRITE = 2; +const uint8 MODE_SCANLINE_BACKGROUND = 3; +const uint16 DURATION_SCANLINE_SPRITE = 80; +const uint16 DURATION_SCANLINE_BACKGROUND = 172; +const uint16 DURATION_HORIZONTAL_BLANK = 204; +const uint16 DURATION_VERTICAL_BLANK = 456; +const std::string LOG_PATH = "/sdcard/Download/matterboy_log_gpu.txt"; + +const uint16 MODE_DURATIONS[] = { + DURATION_HORIZONTAL_BLANK, + DURATION_VERTICAL_BLANK, + DURATION_SCANLINE_SPRITE, + DURATION_SCANLINE_BACKGROUND}; + +GPU::GPU(Memory *memory, GUI* gui) : +pixels(SCREEN_WIDTH, SCREEN_HEIGHT), +display(memory), +logFile(LOG_PATH) { + this->memory = memory; + this->control = new Control((uint8) 0); + this->gui = gui; + + mode = 0; + cycleCount = 0; + line = 0; + coincidenceInterrupt = false; + oamInterrupt = false; + vblankInterrupt = false; + hblankInterrupt = false; + coincidenceLine = 0; +} + +void GPU::step(uint16 lastInstructionDuration, Memory *memory, bool isColour, uint32 count) { + if(!control->display) { return; } + + for(uint16 i = 0; i < lastInstructionDuration; i++) { + if(cycleCount >= MODE_DURATIONS[mode]) { + switch (mode) { + case MODE_HORIZONTAL_BLANK: + cycleCount = 0; + line += 1; + + if(coincidenceInterrupt && line == coincidenceLine) { + memory->flag_interrupt(INTERRUPT_BIT_LCD_STAT); + } + + if(line == SCREEN_HEIGHT) { + mode = MODE_VERTICAL_BLANK; + + gui->displayBuffer(pixels.data); + frameCount += 1; + + std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); + uint32 duration = std::chrono::duration_cast(end - begin).count(); + + if(duration > 1000) { + gui->displayFPS(frameCount); + frameCount = 0; + begin = std::chrono::steady_clock::now(); + } + + memory->flag_interrupt(INTERRUPT_BIT_VERTICAL_BLANK); + + if(vblankInterrupt) { + memory->flag_interrupt(INTERRUPT_BIT_LCD_STAT); + } + } else { + mode = MODE_SCANLINE_SPRITE; + } + break; + case MODE_VERTICAL_BLANK: + line += 1; + cycleCount = 0; + + if(line > 153) { + mode = MODE_SCANLINE_SPRITE; + line = 0; + + if (oamInterrupt) { + memory->flag_interrupt(INTERRUPT_BIT_LCD_STAT); + } + } + break; + case MODE_SCANLINE_SPRITE: + mode = MODE_SCANLINE_BACKGROUND; + cycleCount = 0; + break; + case MODE_SCANLINE_BACKGROUND: + mode = MODE_HORIZONTAL_BLANK; + cycleCount = 0; + + display.drawLine(&pixels, line, isColour, control); + + if(hblankInterrupt) { + memory->flag_interrupt(INTERRUPT_BIT_LCD_STAT); + } + break; + } + } + + cycleCount += 1; + } + + //logFile.write("%d - %d - %d - %d - %d", count, mode, cycleCount, line, coincidenceLine); +} + +uint8 GPU::getStat() { + uint8 lcdStat = 0; + + if (mode == MODE_VERTICAL_BLANK) { + lcdStat = Bytes::setBit_8(lcdStat, 0); + } else if (mode == MODE_SCANLINE_SPRITE) { + lcdStat = Bytes::setBit_8(lcdStat, 1); + } else if (mode == MODE_SCANLINE_BACKGROUND) { + lcdStat = Bytes::setBit_8(lcdStat, 0); + lcdStat = Bytes::setBit_8(lcdStat, 1); + } + + if(line == coincidenceLine) { + lcdStat = Bytes::setBit_8(lcdStat, 2); + } + + if(hblankInterrupt) { + lcdStat = Bytes::setBit_8(lcdStat, 3); + } + + if(vblankInterrupt) { + lcdStat = Bytes::setBit_8(lcdStat, 4); + } + + if(oamInterrupt) { + lcdStat = Bytes::setBit_8(lcdStat, 5); + } + + if(coincidenceInterrupt) { + lcdStat = Bytes::setBit_8(lcdStat, 6); + } + + return lcdStat; +} + +void GPU::setStat(uint8 value) { + hblankInterrupt = Bytes::getBit_8(value, 3); + vblankInterrupt = Bytes::getBit_8(value, 4); + oamInterrupt = Bytes::getBit_8(value, 5); + coincidenceInterrupt = Bytes::getBit_8(value, 6); +} + +void GPU::setControl(uint8 value) { + Control* current = this->control; + delete current; + + control = new Control(value); + + if(!control->display) { + line = 0; + mode = 0; + } +} + +uint8 GPU::get_8(uint16 address) { + switch(address) { + case ADDRESS_STAT: + return getStat(); + case ADDRESS_TARGET_LINE: + return coincidenceLine; + case ADDRESS_LINE: + return line; + default: + throw std::invalid_argument("Invalid read from GPU."); + } +} + +bool GPU::set_8(uint16 address, uint8 value) { + if(address >= TILE_SET_1_START && address < TILE_SET_0_END) { + display.set_8(address, value); + return true; + } else { + switch (address) { + case LCD_CONTROL: + setControl(value); + return true; + case ADDRESS_STAT: + setStat(value); + return true; + case ADDRESS_TARGET_LINE: + coincidenceLine = value; + return true; + case ADDRESS_LINE: + line = 0; + return true; + default: + throw std::invalid_argument("Invalid write on GPU."); + } + } +} diff --git a/src/Gameboy.cpp b/src/Gameboy.cpp new file mode 100644 index 0000000..27ff7be --- /dev/null +++ b/src/Gameboy.cpp @@ -0,0 +1,24 @@ +// +// Created by matthew on 05/07/2020. +// + +#include "Gameboy.h" + +Gameboy::Gameboy(Rom rom, GUI* gui) : gpu(&memory, gui) { + this->rom = &rom; + this->gui = gui; + memory.init(&coreMemory, &rom, (MemoryHook*) &cpu, (MemoryHook*) &gpu, (MemoryHook*) &timer); +} + +void Gameboy::run() { + uint32 count = 0; + bool isColour = rom->isColour; + + while(this->gui->isOpen()) { + uint16 instructionDuration = cpu.step(&memory, count, false); + gpu.step(instructionDuration, &memory, isColour, count); + timer.step(instructionDuration, &memory); + + count += 1; + } +} diff --git a/src/Instructions.cpp b/src/Instructions.cpp new file mode 100644 index 0000000..533b0cc --- /dev/null +++ b/src/Instructions.cpp @@ -0,0 +1,3011 @@ +// +// Created by matthew on 05/07/2020. +// + +#include "Instructions.h" +#include "CPU.h" +#include "Bytes.h" + +const instructionInfo CORE_INSTRUCTION_INFO[256] = { + {1, 4, 0, "NOP"}, + {3, 12, 0, "LD BC,nn"}, + {1, 8, 0, "LD BC,A"}, + {1, 8, 0, "INC BC"}, + {1, 4, 0, "INC B"}, + {1, 4, 0, "DEC B"}, + {2, 8, 0, "LD B,n"}, + {1, 4, 0, "RLCA"}, + {3, 20, 0, "LD (nn),SP"}, + {1, 8, 0, "ADD HL,BC"}, + {1, 8, 0, "LD A,BC"}, + {1, 8, 0, "DEC BC"}, + {1, 4, 0, "INC C"}, + {1, 4, 0, "DEC C"}, + {2, 8, 0, "LD C,n"}, + {1, 4, 0, "RRCA"}, + {1, 0, 0, "STOP 0"}, + {3, 12, 0, "LD DE,nn"}, + {1, 8, 0, "LD (DE),A"}, + {1, 8, 0, "INC DE"}, + {1, 4, 0, "INC D"}, + {1, 4, 0, "DEC D"}, + {2, 8, 0, "LD D,n"}, + {1, 4, 0, "RLA"}, + {2, 12, 0, "JR n"}, + {1, 8, 0, "ADD HL,DE"}, + {1, 8, 0, "LD A,(DE)"}, + {1, 8, 0, "DEC DE"}, + {1, 4, 0, "INC E"}, + {1, 4, 0, "DEC E"}, + {2, 8, 0, "LD E,n"}, + {1, 4, 0, "RRA"}, + {2, 12, 8, "JR NZ,n"}, + {3, 12, 0, "LD HL,nn"}, + {1, 8, 0, "LD (HL+),A"}, + {1, 8, 0, "INC HL"}, + {1, 4, 0, "INC H"}, + {1, 4, 0, "DEC H"}, + {2, 8, 0, "LD H,n"}, + {1, 4, 0, "DAA"}, + {2, 12, 8, "JR Z,n"}, + {1, 8, 0, "ADD HL,HL"}, + {1, 8, 0, "LD A,(HL+)"}, + {1, 8, 0, "DEC HL"}, + {1, 4, 0, "INC L"}, + {1, 4, 0, "DEC L"}, + {2, 8, 0, "LD L,n"}, + {1, 4, 0, "CPL"}, + {2, 12, 8, "JR NC,n"}, + {3, 12, 0, "LD SP,nn"}, + {1, 8, 0, "LD (HL-),A"}, + {1, 8, 0, "INC SP"}, + {1, 12, 0, "INC (HL)"}, + {1, 12, 0, "DEC (HL)"}, + {2, 12, 0, "LD (HL),n"}, + {1, 4, 0, "SCF"}, + {2, 12, 8, "JR C,n"}, + {1, 8, 0, "ADD HL,SP"}, + {1, 8, 0, "LD A,(HL-)"}, + {1, 8, 0, "DEC SP"}, + {1, 4, 0, "INC A"}, + {1, 4, 0, "DEC A"}, + {2, 8, 0, "LD A,n"}, + {1, 4, 0, "CCF"}, + {1, 4, 0, "LD B,B"}, + {1, 4, 0, "LD B,C"}, + {1, 4, 0, "LD B,D"}, + {1, 4, 0, "LD B,E"}, + {1, 4, 0, "LD B,H"}, + {1, 4, 0, "LD B,L"}, + {1, 8, 0, "LD B,(HL)"}, + {1, 4, 0, "LD B,A"}, + {1, 4, 0, "LD C,B"}, + {1, 4, 0, "LD C,C"}, + {1, 4, 0, "LD C,D"}, + {1, 4, 0, "LD C,E"}, + {1, 4, 0, "LD C,H"}, + {1, 4, 0, "LD C,L"}, + {1, 8, 0, "LD C,(HL)"}, + {1, 4, 0, "LD C,A"}, + {1, 4, 0, "LD D,B"}, + {1, 4, 0, "LD D,C"}, + {1, 4, 0, "LD D,D"}, + {1, 4, 0, "LD D,E"}, + {1, 4, 0, "LD D,H"}, + {1, 4, 0, "LD D,L"}, + {1, 8, 0, "LD D,(HL)"}, + {1, 4, 0, "LD D,A"}, + {1, 4, 0, "LD E,B"}, + {1, 4, 0, "LD E,C"}, + {1, 4, 0, "LD E,D"}, + {1, 4, 0, "LD E,E"}, + {1, 4, 0, "LD E,H"}, + {1, 4, 0, "LD E,L"}, + {1, 8, 0, "LD E,(HL)"}, + {1, 4, 0, "LD E,A"}, + {1, 4, 0, "LD H,B"}, + {1, 4, 0, "LD H,C"}, + {1, 4, 0, "LD H,D"}, + {1, 4, 0, "LD H,E"}, + {1, 4, 0, "LD H,H"}, + {1, 4, 0, "LD H,L"}, + {1, 8, 0, "LD H,(HL)"}, + {1, 4, 0, "LD H,A"}, + {1, 4, 0, "LD L,B"}, + {1, 4, 0, "LD L,C"}, + {1, 4, 0, "LD L,D"}, + {1, 4, 0, "LD L,E"}, + {1, 4, 0, "LD L,H"}, + {1, 4, 0, "LD L,L"}, + {1, 8, 0, "LD L,(HL)"}, + {1, 4, 0, "LD L,A"}, + {1, 8, 0, "LD (HL),B"}, + {1, 8, 0, "LD (HL),C"}, + {1, 8, 0, "LD (HL),D"}, + {1, 8, 0, "LD (HL),E"}, + {1, 8, 0, "LD (HL),H"}, + {1, 8, 0, "LD (HL),L"}, + {1, 0, 0, "HALT"}, + {1, 8, 0, "LD (HL),A"}, + {1, 4, 0, "LD A,B"}, + {1, 4, 0, "LD A,C"}, + {1, 4, 0, "LD A,D"}, + {1, 4, 0, "LD A,E"}, + {1, 4, 0, "LD A,H"}, + {1, 4, 0, "LD A,L"}, + {1, 8, 0, "LD A,(HL)"}, + {1, 4, 0, "LD A,A"}, + {1, 4, 0, "ADD A,B"}, + {1, 4, 0, "ADD A,C"}, + {1, 4, 0, "ADD A,D"}, + {1, 4, 0, "ADD A,E"}, + {1, 4, 0, "ADD A,H"}, + {1, 4, 0, "ADD A,L"}, + {1, 8, 0, "ADD A,(HL)"}, + {1, 4, 0, "ADD A,A"}, + {1, 4, 0, "ADC A,B"}, + {1, 4, 0, "ADC A,C"}, + {1, 4, 0, "ADC A,D"}, + {1, 4, 0, "ADC A,E"}, + {1, 4, 0, "ADC A,H"}, + {1, 4, 0, "ADC A,L"}, + {1, 8, 0, "ADC A,(HL)"}, + {1, 4, 0, "ADC A,A"}, + {1, 4, 0, "SUB B"}, + {1, 4, 0, "SUB C"}, + {1, 4, 0, "SUB D"}, + {1, 4, 0, "SUB E"}, + {1, 4, 0, "SUB H"}, + {1, 4, 0, "SUB L"}, + {1, 8, 0, "SUB (HL)"}, + {1, 4, 0, "SUB A"}, + {1, 4, 0, "SBC A,B"}, + {1, 4, 0, "SBC A,C"}, + {1, 4, 0, "SBC A,D"}, + {1, 4, 0, "SBC A,E"}, + {1, 4, 0, "SBC A,H"}, + {1, 4, 0, "SBC A,L"}, + {1, 8, 0, "SBC A,(HL)"}, + {1, 4, 0, "SBC A,A"}, + {1, 4, 0, "AND B"}, + {1, 4, 0, "AND C"}, + {1, 4, 0, "AND D"}, + {1, 4, 0, "AND E"}, + {1, 4, 0, "AND H"}, + {1, 4, 0, "AND L"}, + {1, 8, 0, "AND (HL)"}, + {1, 4, 0, "AND A"}, + {1, 4, 0, "XOR B"}, + {1, 4, 0, "XOR C"}, + {1, 4, 0, "XOR D"}, + {1, 4, 0, "XOR E"}, + {1, 4, 0, "XOR H"}, + {1, 4, 0, "XOR L"}, + {1, 8, 0, "XOR (HL)"}, + {1, 4, 0, "XOR A"}, + {1, 4, 0, "OR B"}, + {1, 4, 0, "OR C"}, + {1, 4, 0, "OR D"}, + {1, 4, 0, "OR E"}, + {1, 4, 0, "OR H"}, + {1, 4, 0, "OR L"}, + {1, 8, 0, "OR (HL)"}, + {1, 4, 0, "OR A"}, + {1, 4, 0, "CP B"}, + {1, 4, 0, "CP C"}, + {1, 4, 0, "CP D"}, + {1, 4, 0, "CP E"}, + {1, 4, 0, "CP H"}, + {1, 4, 0, "CP L"}, + {1, 8, 0, "CP (HL)"}, + {1, 4, 0, "CP A"}, + {1, 20, 8, "RET NZ"}, + {1, 12, 0, "POP BC"}, + {3, 16, 12, "JP NZ,nn"}, + {3, 16, 0, "JP nn"}, + {3, 24, 12, "CALL NZ,nn"}, + {1, 16, 0, "PUSH BC"}, + {2, 8, 0, "ADD A,n"}, + {1, 16, 0, "RST 00H"}, + {1, 20, 8, "RET Z"}, + {1, 16, 0, "RET"}, + {3, 16, 12, "JP Z,nn"}, + {3, 0, 0, "JP Z,nn"}, + {3, 24, 12, "CALL Z,nn"}, + {3, 24, 0, "CALL nn"}, + {2, 8, 0, "ADC A,n"}, + {1, 16, 0, "RST 08H"}, + {1, 20, 8, "RET NC"}, + {1, 12, 0, "POP DE"}, + {3, 16, 12, "JP NC,nn"}, + {0, 0, 0, "NO INSTRUCTION"}, + {3, 24, 12, "CALL NC,nn"}, + {1, 16, 0, "PUSH DE"}, + {2, 8, 0, "SUB n"}, + {1, 16, 0, "RST 10H"}, + {1, 20, 8, "RET C"}, + {1, 16, 0, "RETI"}, + {3, 16, 12, "JP C,nn"}, + {0, 0, 0, "NO INSTRUCTION"}, + {3, 24, 12, "CALL C,nn"}, + {0, 0, 0, "NO INSTRUCTION"}, + {2, 8, 0, "SBC A,n"}, + {1, 16, 0, "RST 18H"}, + {2, 12, 0, "LDH (n),A"}, + {1, 12, 0, "POP HL"}, + {1, 8, 0, "LD (C),A"}, + {0, 0, 0, "NO INSTRUCTION"}, + {0, 0, 0, "NO INSTRUCTION"}, + {1, 16, 0, "PUSH HL"}, + {2, 8, 0, "AND n"}, + {1, 16, 0, "RST 20H"}, + {2, 16, 0, "ADD SP,n"}, + {1, 4, 0, "JP (HL)"}, + {3, 16, 0, "LD (nn),A"}, + {0, 0, 0, "NO INSTRUCTION"}, + {0, 0, 0, "NO INSTRUCTION"}, + {0, 0, 0, "NO INSTRUCTION"}, + {2, 8, 0, "XOR n"}, + {1, 16, 0, "RST 28H"}, + {2, 12, 0, "LDH A,(n)"}, + {1, 12, 0, "POP AF"}, + {1, 8, 0, "LD A,(C)"}, + {1, 4, 0, "DI"}, + {0, 0, 0, "NO INSTRUCTION"}, + {1, 16, 0, "PUSH AF"}, + {2, 8, 0, "OR n"}, + {1, 16, 0, "RST 30H"}, + {2, 12, 0, "LD HL,SP+n"}, + {1, 8, 0, "LD SP,HL"}, + {3, 16, 0, "LD A,(nn)"}, + {1, 4, 0, "EI"}, + {0, 0, 0, "NO INSTRUCTION"}, + {0, 0, 0, "NO INSTRUCTION"}, + {2, 8, 0, "CP n"}, + {1, 16, 0, "RST 38H"} +}; + +const instructionInfo CB_INSTRUCTION_INFO[256] = { + {2, 8, 0, "NOP"}, + {2, 8, 0, "LD BC,nn"}, + {2, 8, 0, "LD BC,A"}, + {2, 8, 0, "INC BC"}, + {2, 8, 0, "INC B"}, + {2, 8, 0, "DEC B"}, + {2, 16, 0, "LD B,n"}, + {2, 8, 0, "RLCA"}, + {2, 8, 0, "LD (nn),SP"}, + {2, 8, 0, "ADD HL,BC"}, + {2, 8, 0, "LD A,BC"}, + {2, 8, 0, "DEC BC"}, + {2, 8, 0, "INC C"}, + {2, 8, 0, "DEC C"}, + {2, 16, 0, "LD C,n"}, + {2, 8, 0, "RRCA"}, + {2, 8, 0, "STOP 0"}, + {2, 8, 0, "LD DE,nn"}, + {2, 8, 0, "LD (DE),A"}, + {2, 8, 0, "INC DE"}, + {2, 8, 0, "INC D"}, + {2, 8, 0, "DEC D"}, + {2, 16, 0, "LD D,n"}, + {2, 8, 0, "RLA"}, + {2, 8, 0, "JR n"}, + {2, 8, 0, "ADD HL,DE"}, + {2, 8, 0, "LD A,(DE)"}, + {2, 8, 0, "DEC DE"}, + {2, 8, 0, "INC E"}, + {2, 8, 0, "DEC E"}, + {2, 16, 0, "LD E,n"}, + {2, 8, 0, "RRA"}, + {2, 8, 0, "JR NZ,n"}, + {2, 8, 0, "LD HL,nn"}, + {2, 8, 0, "LD (HL+),A"}, + {2, 8, 0, "INC HL"}, + {2, 8, 0, "INC H"}, + {2, 8, 0, "DEC H"}, + {2, 16, 0, "LD H,n"}, + {2, 8, 0, "DAA"}, + {2, 8, 0, "JR Z,n"}, + {2, 8, 0, "ADD HL,HL"}, + {2, 8, 0, "LD A,(HL+)"}, + {2, 8, 0, "DEC HL"}, + {2, 8, 0, "INC L"}, + {2, 8, 0, "DEC L"}, + {2, 16, 0, "LD L,n"}, + {2, 8, 0, "CPL"}, + {2, 8, 0, "JR NC,n"}, + {2, 8, 0, "LD SP,nn"}, + {2, 8, 0, "LD (HL-),A"}, + {2, 8, 0, "INC SP"}, + {2, 8, 0, "INC (HL)"}, + {2, 8, 0, "DEC (HL)"}, + {2, 16, 0, "LD (HL),n"}, + {2, 8, 0, "SCF"}, + {2, 8, 0, "JR C,n"}, + {2, 8, 0, "ADD HL,SP"}, + {2, 8, 0, "LD A,(HL-)"}, + {2, 8, 0, "DEC SP"}, + {2, 8, 0, "INC A"}, + {2, 8, 0, "DEC A"}, + {2, 16, 0, "LD A,n"}, + {2, 8, 0, "CCF"}, + {2, 8, 0, "LD B,B"}, + {2, 8, 0, "LD B,C"}, + {2, 8, 0, "LD B,D"}, + {2, 8, 0, "LD B,E"}, + {2, 8, 0, "LD B,H"}, + {2, 8, 0, "LD B,L"}, + {2, 12, 0, "LD B,(HL)"}, + {2, 8, 0, "LD B,A"}, + {2, 8, 0, "LD C,B"}, + {2, 8, 0, "LD C,C"}, + {2, 8, 0, "LD C,D"}, + {2, 8, 0, "LD C,E"}, + {2, 8, 0, "LD C,H"}, + {2, 8, 0, "LD C,L"}, + {2, 12, 0, "LD C,(HL)"}, + {2, 8, 0, "LD C,A"}, + {2, 8, 0, "LD D,B"}, + {2, 8, 0, "LD D,C"}, + {2, 8, 0, "LD D,D"}, + {2, 8, 0, "LD D,E"}, + {2, 8, 0, "LD D,H"}, + {2, 8, 0, "LD D,L"}, + {2, 12, 0, "LD D,(HL)"}, + {2, 8, 0, "LD D,A"}, + {2, 8, 0, "LD E,B"}, + {2, 8, 0, "LD E,C"}, + {2, 8, 0, "LD E,D"}, + {2, 8, 0, "LD E,E"}, + {2, 8, 0, "LD E,H"}, + {2, 8, 0, "LD E,L"}, + {2, 12, 0, "LD E,(HL)"}, + {2, 8, 0, "LD E,A"}, + {2, 8, 0, "LD H,B"}, + {2, 8, 0, "LD H,C"}, + {2, 8, 0, "LD H,D"}, + {2, 8, 0, "LD H,E"}, + {2, 8, 0, "LD H,H"}, + {2, 8, 0, "LD H,L"}, + {2, 12, 0, "LD H,(HL)"}, + {2, 8, 0, "LD H,A"}, + {2, 8, 0, "LD L,B"}, + {2, 8, 0, "LD L,C"}, + {2, 8, 0, "LD L,D"}, + {2, 8, 0, "LD L,E"}, + {2, 8, 0, "LD L,H"}, + {2, 8, 0, "LD L,L"}, + {2, 12, 0, "LD L,(HL)"}, + {2, 8, 0, "LD L,A"}, + {2, 8, 0, "LD (HL),B"}, + {2, 8, 0, "LD (HL),C"}, + {2, 8, 0, "LD (HL),D"}, + {2, 8, 0, "LD (HL),E"}, + {2, 8, 0, "LD (HL),H"}, + {2, 8, 0, "LD (HL),L"}, + {2, 12, 0, "HALT"}, + {2, 8, 0, "LD (HL),A"}, + {2, 8, 0, "LD A,B"}, + {2, 8, 0, "LD A,C"}, + {2, 8, 0, "LD A,D"}, + {2, 8, 0, "LD A,E"}, + {2, 8, 0, "LD A,H"}, + {2, 8, 0, "LD A,L"}, + {2, 12, 0, "LD A,(HL)"}, + {2, 8, 0, "LD A,A"}, + {2, 8, 0, "ADD A,B"}, + {2, 8, 0, "ADD A,C"}, + {2, 8, 0, "ADD A,D"}, + {2, 8, 0, "ADD A,E"}, + {2, 8, 0, "ADD A,H"}, + {2, 8, 0, "ADD A,L"}, + {2, 16, 0, "ADD A,(HL)"}, + {2, 8, 0, "ADD A,A"}, + {2, 8, 0, "ADC A,B"}, + {2, 8, 0, "ADC A,C"}, + {2, 8, 0, "ADC A,D"}, + {2, 8, 0, "ADC A,E"}, + {2, 8, 0, "ADC A,H"}, + {2, 8, 0, "ADC A,L"}, + {2, 16, 0, "ADC A,(HL)"}, + {2, 8, 0, "ADC A,A"}, + {2, 8, 0, "SUB B"}, + {2, 8, 0, "SUB C"}, + {2, 8, 0, "SUB D"}, + {2, 8, 0, "SUB E"}, + {2, 8, 0, "SUB H"}, + {2, 8, 0, "SUB L"}, + {2, 16, 0, "SUB (HL)"}, + {2, 8, 0, "SUB A"}, + {2, 8, 0, "SBC A,B"}, + {2, 8, 0, "SBC A,C"}, + {2, 8, 0, "SBC A,D"}, + {2, 8, 0, "SBC A,E"}, + {2, 8, 0, "SBC A,H"}, + {2, 8, 0, "SBC A,L"}, + {2, 16, 0, "SBC A,(HL)"}, + {2, 8, 0, "SBC A,A"}, + {2, 8, 0, "AND B"}, + {2, 8, 0, "AND C"}, + {2, 8, 0, "AND D"}, + {2, 8, 0, "AND E"}, + {2, 8, 0, "AND H"}, + {2, 8, 0, "AND L"}, + {2, 16, 0, "AND (HL)"}, + {2, 8, 0, "AND A"}, + {2, 8, 0, "XOR B"}, + {2, 8, 0, "XOR C"}, + {2, 8, 0, "XOR D"}, + {2, 8, 0, "XOR E"}, + {2, 8, 0, "XOR H"}, + {2, 8, 0, "XOR L"}, + {2, 16, 0, "XOR (HL)"}, + {2, 8, 0, "XOR A"}, + {2, 8, 0, "OR B"}, + {2, 8, 0, "OR C"}, + {2, 8, 0, "OR D"}, + {2, 8, 0, "OR E"}, + {2, 8, 0, "OR H"}, + {2, 8, 0, "OR L"}, + {2, 16, 0, "OR (HL)"}, + {2, 8, 0, "OR A"}, + {2, 8, 0, "CP B"}, + {2, 8, 0, "CP C"}, + {2, 8, 0, "CP D"}, + {2, 8, 0, "CP E"}, + {2, 8, 0, "CP H"}, + {2, 8, 0, "CP L"}, + {2, 16, 0, "CP (HL)"}, + {2, 8, 0, "CP A"}, + {2, 8, 0, "RET NZ"}, + {2, 8, 0, "POP BC"}, + {2, 8, 0, "JP NZ,nn"}, + {2, 8, 0, "JP nn"}, + {2, 8, 0, "CALL NZ,nn"}, + {2, 8, 0, "PUSH BC"}, + {2, 16, 0, "ADD A,n"}, + {2, 8, 0, "RST 00H"}, + {2, 8, 0, "RET Z"}, + {2, 8, 0, "RET"}, + {2, 8, 0, "JP Z,nn"}, + {2, 8, 0, "CB"}, + {2, 8, 0, "CALL Z,nn"}, + {2, 8, 0, "CALL nn"}, + {2, 16, 0, "ADC A,n"}, + {2, 8, 0, "RST 08H"}, + {2, 8, 0, "RET NC"}, + {2, 8, 0, "POP DE"}, + {2, 8, 0, "JP NC,nn"}, + {2, 8, 0, "NO INSTRUCTION"}, + {2, 8, 0, "CALL NC,nn"}, + {2, 8, 0, "PUSH DE"}, + {2, 16, 0, "SUB n"}, + {2, 8, 0, "RST 10H"}, + {2, 8, 0, "RET C"}, + {2, 8, 0, "RETI"}, + {2, 8, 0, "JP C,nn"}, + {2, 8, 0, "NO INSTRUCTION"}, + {2, 8, 0, "CALL C,nn"}, + {2, 8, 0, "NO INSTRUCTION"}, + {2, 16, 0, "SBC A,n"}, + {2, 8, 0, "RST 18H"}, + {2, 8, 0, "LDH (n),A"}, + {2, 8, 0, "POP HL"}, + {2, 8, 0, "LD (C),A"}, + {2, 8, 0, "NO INSTRUCTION"}, + {2, 8, 0, "NO INSTRUCTION"}, + {2, 8, 0, "PUSH HL"}, + {2, 16, 0, "AND n"}, + {2, 8, 0, "RST 20H"}, + {2, 8, 0, "ADD SP,n"}, + {2, 8, 0, "JP (HL)"}, + {2, 8, 0, "LD (nn),A"}, + {2, 8, 0, "NO INSTRUCTION"}, + {2, 8, 0, "NO INSTRUCTION"}, + {2, 8, 0, "NO INSTRUCTION"}, + {2, 16, 0, "XOR n"}, + {2, 8, 0, "RST 28H"}, + {2, 8, 0, "LDH A,(n)"}, + {2, 8, 0, "POP AF"}, + {2, 8, 0, "LD A,(C)"}, + {2, 8, 0, "DI"}, + {2, 8, 0, "NO INSTRUCTION"}, + {2, 8, 0, "PUSH AF"}, + {2, 16, 0, "OR n"}, + {2, 8, 0, "RST 30H"}, + {2, 8, 0, "LD HL,SP+n"}, + {2, 8, 0, "LD SP,HL"}, + {2, 8, 0, "LD A,(nn)"}, + {2, 8, 0, "EI"}, + {2, 8, 0, "NO INSTRUCTION"}, + {2, 8, 0, "NO INSTRUCTION"}, + {2, 16, 0, "CP n"}, + {2, 8, 0, "RST 38H"} +}; + +static void rotate_right_r8(CPU* cpu, Register cpuRegister) { + uint8 value = cpu->get_8(cpuRegister); + bool lsb = Bytes::getBit_8(value, 0); + bool carry = cpu->flag_c; + uint8 new_value = value >> 1; + + if (carry) { + new_value = new_value | 128; + } + + cpu->set_8(cpuRegister, new_value); + + cpu->flag_z = false; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = lsb; +} + +static void rotate_right_r8_cb(CPU* cpu, Register cpuRegister) { + uint8 value = cpu->get_8(cpuRegister); + bool lsb = Bytes::getBit_8(value, 0); + bool carry = cpu->flag_c; + uint8 new_value = value >> 1; + + if (carry) { + new_value = new_value | 128; + } + + cpu->set_8(cpuRegister, new_value); + + cpu->flag_z = new_value == 0; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = lsb; +} + +static void rotate_right_r16(CPU* cpu, Memory* memory, Register cpuRegister) { + uint16 address = cpu->get_16(cpuRegister); + uint8 value = memory->get_8(address); + bool lsb = Bytes::getBit_8(value, 0); + bool carry = cpu->flag_c; + uint8 new_value = value >> 1; + + if (carry) { + new_value = new_value | 128; + } + + memory->set_8(address, new_value & 0xFF); + + cpu->flag_z = new_value == 0; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = lsb; +} + +static void load_increment_r16_r8(CPU* cpu, Memory* memory, Register cpuRegister_1, Register cpuRegister_2) { + uint8 value = cpu->get_8(cpuRegister_2); + uint16 address = cpu->get_16(cpuRegister_1); + memory->set_8(address, value); + cpu->set_16(cpuRegister_1, Bytes::wrappingAdd_16(address, 1)); +} + +static void load_increment_r8_ar16(CPU* cpu, Memory* memory, Register cpuRegister_1, Register cpuRegister_2) { + uint16 address = cpu->get_16(cpuRegister_2); + uint8 value = memory->get_8(address); + cpu->set_8(cpuRegister_1, value); + cpu->set_16(cpuRegister_2, Bytes::wrappingAdd_16(address, 1)); +} + +static void load_decrement_r8_ar16(CPU* cpu, Memory* memory, Register cpuRegister_1, Register cpuRegister_2) { + uint16 address = cpu->get_16(cpuRegister_2); + uint8 value = memory->get_8(address); + cpu->set_8(cpuRegister_1, value); + cpu->set_16(cpuRegister_2, Bytes::wrappingSub_16(address, 1)); +} + +static void decimal_r8(CPU* cpu, Register cpuRegister) { + uint16 value = cpu->get_8(cpuRegister); + + if (!cpu->flag_n) { + if (cpu->flag_h || (value & 0x0F) > 0x9) { + value += 0x6; + } + if (cpu->flag_c || value > 0x9F) { + value += 0x60; + } + } else { + if (cpu->flag_h) { + value -= 0x6; + if (!cpu->flag_c) { + value &= 0xFF; + } + } + if (cpu->flag_c) { + value -= 0x60; + } + } + + cpu->flag_h = false; + + if (value > 0xFF) { + cpu->flag_c = true; + } + + value &= 0xFF; + + cpu->flag_z = value == 0; + + cpu->set_8(cpuRegister, value); +} + +static void complement_r8(CPU* cpu, Register cpuRegister) { + uint8 value = cpu->get_8(cpuRegister); + uint8 new_value = value ^ 0xFF; + cpu->set_8(cpuRegister, new_value); + + cpu->flag_n = true; + cpu->flag_h = true; +} + +static void load_sp_n16(CPU* cpu, uint16 value) { + cpu->sp = value; +} + +static void swap_r8(CPU* cpu, Register cpuRegister) { + uint8 value = cpu->get_8(cpuRegister); + uint8 upper = Bytes::split_8_upper(value); + uint8 lower = Bytes::split_8_lower(value); + uint8 new_value = Bytes::join_4(lower, upper); + cpu->set_8(cpuRegister, new_value); + + cpu->flag_z = new_value == 0; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = false; +} + +static void swap_r16(CPU* cpu, Memory* memory, Register cpuRegister) { + uint16 address = cpu->get_16(cpuRegister); + uint8 value = memory->get_8(address); + uint8 upper = Bytes::split_8_upper(value); + uint8 lower = Bytes::split_8_lower(value); + uint8 new_value = Bytes::join_4(lower, upper); + memory->set_8(address, new_value); + + cpu->flag_z = new_value == 0; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = false; +} + +static void shift_left_r8(CPU* cpu, Register cpuRegister) { + uint8 value = cpu->get_8(cpuRegister); + bool hsb = Bytes::getBit_8(value, 7); + uint8 new_value = (value << 1) & 0xFF; + cpu->set_8(cpuRegister, new_value); + + cpu->flag_z = new_value == 0; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = hsb; +} + +static void shift_left_r16(CPU* cpu, Memory* memory, Register cpuRegister) { + uint16 address = cpu->get_16(cpuRegister); + uint8 value = memory->get_8(address); + bool hsb = Bytes::getBit_8(value, 7); + uint8 new_value = (value << 1) & 0xFF; + memory->set_8(address, new_value); + + cpu->flag_z = new_value == 0; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = hsb; +} + +static void shift_right_r8(CPU* cpu, Register cpuRegister) { + uint8 value = cpu->get_8(cpuRegister); + bool lsb = Bytes::getBit_8(value, 0); + uint8 new_value = value >> 1; + cpu->set_8(cpuRegister, new_value); + + cpu->flag_z = new_value == 0; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = lsb; +} + +static void shift_right_weird_r8(CPU* cpu, Register cpuRegister) { + uint8 value = cpu->get_8(cpuRegister); + bool msb = Bytes::getBit_8(value, 7); + bool lsb = Bytes::getBit_8(value, 0); + uint8 new_value = value >> 1; + + if (msb) { + new_value = Bytes::setBit_8(new_value, 7); + } + + cpu->set_8(cpuRegister, new_value); + + cpu->flag_z = new_value == 0; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = lsb; +} + +static void shift_right_weird_r16(CPU* cpu, Memory* memory, Register cpuRegister) { + uint16 address = cpu->get_16(cpuRegister); + uint8 value = memory->get_8(address); + bool msb = Bytes::getBit_8(value, 7); + bool lsb = Bytes::getBit_8(value, 0); + uint8 new_value = value >> 1; + + if (msb) { + new_value = Bytes::setBit_8(new_value, 7); + } + + memory->set_8(address, new_value); + + cpu->flag_z = new_value == 0; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = lsb; +} + +static void shift_right_r16(CPU* cpu, Memory* memory, Register cpuRegister) { + uint16 address = cpu->get_16(cpuRegister); + uint8 value = memory->get_8(address); + bool lsb = Bytes::getBit_8(value, 0); + uint8 new_value = value >> 1; + memory->set_8(address, new_value); + + cpu->flag_z = new_value == 0; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = lsb; +} + +static void bit_8(CPU* cpu, uint8 value, uint8 bit) { + bool set = Bytes::getBit_8(value, bit); + + cpu->flag_z = !set; + cpu->flag_n = false; + cpu->flag_h = true; +} + +static void bit_r16(CPU* cpu, Memory* memory, Register cpuRegister, uint8 bit) { + uint16 address = cpu->get_16(cpuRegister); + uint8 value = memory->get_8(address); + bit_8(cpu, value, bit); +} + +static void bit_r8(CPU* cpu, Register cpuRegister, uint8 bit) { + uint8 value = cpu->get_8(cpuRegister); + bit_8(cpu, value, bit); +} + +static void reset_r8(CPU* cpu, Register cpuRegister, uint8 bit) { + uint8 value = cpu->get_8(cpuRegister); + uint8 new_value = Bytes::clearBit_8(value, bit); + cpu->set_8(cpuRegister, new_value); +} + +static void reset_r16(CPU* cpu, Memory* memory, Register cpuRegister, uint8 bit) { + uint16 address = cpu->get_16(cpuRegister); + uint8 value = memory->get_8(address); + uint8 new_value = Bytes::clearBit_8(value, bit); + memory->set_8(address, new_value); +} + +static void set_r8(CPU* cpu, Register cpuRegister, uint8 bit) { + uint8 value = cpu->get_8(cpuRegister); + uint8 new_value = Bytes::setBit_8(value, bit); + cpu->set_8(cpuRegister, new_value); +} + +static void set_r16(CPU* cpu, Memory* memory, Register cpuRegister, uint8 bit) { + uint16 address = cpu->get_16(cpuRegister); + uint8 value = memory->get_8(address); + uint8 new_value = Bytes::setBit_8(value, bit); + memory->set_8(address, new_value); +} + +static void increment_sp(CPU* cpu) { + cpu->sp = Bytes::wrappingAdd_16(cpu->sp, 1); +} + +static void set_carry(CPU* cpu) { + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = true; +} + +static void rotate_left_r16(CPU* cpu, Memory* memory, Register cpuRegister) { + uint16 address = cpu->get_16(cpuRegister); + uint8 value = memory->get_8(address); + bool carry = cpu->flag_c; + bool msb = Bytes::getBit_8(value, 7); + uint8 new_value = ((value) << 1) & 0xFF; + + if (carry) { + new_value = new_value + 0x1; + } + + memory->set_8(address, new_value & 0xFF); + + cpu->flag_z = new_value == 0; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = msb; +} + +static void rotate_left_r8(CPU* cpu, Register cpuRegister) { + uint8 value = cpu->get_8(cpuRegister); + bool carry = cpu->flag_c; + bool msb = Bytes::getBit_8(value, 7); + uint8 new_value = ((value) << 1) & 0xFF; + + if (carry) { + new_value = new_value + 0x1; + } + + cpu->set_8(cpuRegister, new_value); + + cpu->flag_z = false; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = msb; +} + +static void rotate_left_r8_cb(CPU* cpu, Register cpuRegister) { + uint8 value = cpu->get_8(cpuRegister); + bool carry = cpu->flag_c; + bool msb = Bytes::getBit_8(value, 7); + uint8 new_value = ((value) << 1) & 0xFF; + + if (carry) { + new_value = new_value + 0x1; + } + + cpu->set_8(cpuRegister, new_value); + + cpu->flag_z = new_value == 0; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = msb; +} + +static void rotate_left_carry_r8(CPU* cpu, Register cpuRegister) { + uint8 value = cpu->get_8(cpuRegister); + bool msb = Bytes::getBit_8(value, 7); + uint8 new_value = Bytes::rotateLeft_8(value, 1); + + cpu->set_8(cpuRegister, new_value); + + cpu->flag_z = false; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = msb; +} + +static void rotate_left_carry_r8_cb(CPU* cpu, Register cpuRegister) { + uint8 value = cpu->get_8(cpuRegister); + bool msb = Bytes::getBit_8(value, 7); + uint8 new_value = Bytes::rotateLeft_8(value, 1); + + cpu->set_8(cpuRegister, new_value); + + cpu->flag_z = new_value == 0; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = msb; +} + +static void rotate_left_carry_r16(CPU* cpu, Memory* memory, Register cpuRegister) { + uint16 address = cpu->get_16(cpuRegister); + uint8 value = memory->get_8(address); + bool msb = Bytes::getBit_8(value, 7); + uint8 new_value = Bytes::rotateLeft_8(value, 1); + + memory->set_8(address, new_value); + + cpu->flag_z = new_value == 0; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = msb; +} + +static void rotate_right_carry_r8(CPU* cpu, Register cpuRegister) { + uint8 value = cpu->get_8(cpuRegister); + bool lsb = Bytes::getBit_8(value, 0); + uint8 new_value = Bytes::rotateRight_8(value, 1); + + cpu->set_8(cpuRegister, new_value); + + cpu->flag_z = false; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = lsb; +} + +static void rotate_right_carry_r8_cb(CPU* cpu, Register cpuRegister) { + uint8 value = cpu->get_8(cpuRegister); + bool lsb = Bytes::getBit_8(value, 0); + uint8 new_value = Bytes::rotateRight_8(value, 1); + + cpu->set_8(cpuRegister, new_value); + + cpu->flag_z = new_value == 0; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = lsb; +} + +static void rotate_right_carry_r16(CPU* cpu, Memory* memory, Register cpuRegister) { + uint16 address = cpu->get_16(cpuRegister); + uint8 value = memory->get_8(address); + bool lsb = Bytes::getBit_8(value, 0); + uint8 new_value = Bytes::rotateRight_8(value,1 ); + + memory->set_8(address, new_value); + + cpu->flag_z = new_value == 0; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = lsb; +} + +static void disable_interrupts(CPU* cpu) { + cpu->interruptsEnabled = false; +} + +static void enable_interrupts(CPU* cpu) { + cpu->interruptsEnabled = true; +} + +static void load_r16_sp_n8(CPU* cpu, Register cpuRegister, uint8 value) { + int16 signed_value = Bytes::toSigned_8(value); + uint16 new_value = Bytes::wrappingSub_16(Bytes::wrappingAdd_16(cpu->sp, signed_value), 0); + cpu->set_16(cpuRegister, new_value); + + cpu->flag_z = false; + cpu->flag_n = false; + cpu->flag_h = Bytes::isHalfCarryAdd_8((cpu->sp & 0xFF), value); + cpu->flag_c = Bytes::isCarryAdd_8((cpu->sp & 0xFF), value); +} + +static void load_r8_ar8(CPU* cpu, Memory* memory, Register cpuRegister_1, Register cpuRegister_2) { + uint8 value = memory->get_8(0xFF00 + cpu->get_8(cpuRegister_2)); + cpu->set_8(cpuRegister_1, value); +} + +static void load_n16_sp(CPU* cpu, Memory* memory, uint16 arg_16) { + memory->set_16(arg_16, cpu->sp); +} + +static void jump_n16(CPU* cpu, uint16 value) { + cpu->pc = value; +} + +static void jump_ar16(CPU* cpu, Memory* memory, Register cpuRegister) { + uint16 address = cpu->get_16(cpuRegister); + jump_n16(cpu, address); +} + +static void add_sp_n8(CPU* cpu, uint8 value) { + int8 signed_value = Bytes::toSigned_8(value); + uint16 new_value = Bytes::wrappingSub_16(Bytes::wrappingAdd_16(cpu->sp, signed_value), 0); + uint8 old_value = cpu->sp; + cpu->sp = new_value; + + cpu->flag_z = false; + cpu->flag_n = false; + cpu->flag_h = Bytes::isHalfCarryAdd_8((old_value & 0xFF), value); + cpu->flag_c = Bytes::isCarryAdd_8((old_value & 0xFF), value); +} + +static uint8 and_(CPU* cpu, uint8 value_1, uint8 value_2) { + uint8 result = value_1 & value_2; + + cpu->flag_z = result == 0; + cpu->flag_n = false; + cpu->flag_h = true; + cpu->flag_c = false; + + return result; +} + +static uint16 add_16(CPU* cpu, uint16 value_1, uint16 value_2) { + uint16 result = Bytes::wrappingAdd_16(value_1, value_2); + + cpu->flag_n = false; + cpu->flag_h = Bytes::isHalfCarryAdd_16(value_1, value_2); + cpu->flag_c = Bytes::isCarryAdd_16(value_1, value_2); + + return result; +} + +static void and_r8_r16(CPU* cpu, Memory* memory, Register cpuRegister_1, Register cpuRegister_2) { + uint16 address = cpu->get_16(cpuRegister_2); + uint8 value_2 = memory->get_8(address); + uint8 value_1 = cpu->get_8(cpuRegister_1); + uint8 result = and_(cpu, value_1, value_2); + cpu->set_8(cpuRegister_1, result); +} + +static uint8 sub(CPU* cpu, uint8 value_1, uint8 value_2) { + uint8 result = Bytes::wrappingSub_8(value_1, value_2); + + cpu->flag_z = result == 0; + cpu->flag_n = true; + cpu->flag_h = Bytes::isHalfCarrySub_8(value_1, value_2); + cpu->flag_c = value_1 < value_2; + + return result; +} + +static void sub_r8_r16(CPU* cpu, Memory* memory, Register cpuRegister_1, Register cpuRegister_2) { + uint16 address = cpu->get_16(cpuRegister_2); + uint8 value = memory->get_8(address); + uint8 register_value_1 = cpu->get_8(cpuRegister_1); + uint8 result = sub(cpu, register_value_1, value); + + cpu->set_8(cpuRegister_1, result); +} + +static uint8 add(CPU* cpu, uint8 value_1, uint8 value_2) { + uint8 result = Bytes::wrappingAdd_8(value_1, value_2); + + cpu->flag_z = result == 0; + cpu->flag_n = false; + cpu->flag_h = Bytes::isHalfCarryAdd_8(value_1, value_2); + cpu->flag_c = Bytes::isCarryAdd_8(value_1, value_2); + + return result; +} + +static void add_r8_r16(CPU* cpu, Memory* memory, Register cpuRegister_1, Register cpuRegister_2) { + uint16 address = cpu->get_16(cpuRegister_2); + uint8 value = memory->get_8(address); + uint8 register_value_1 = cpu->get_8(cpuRegister_1); + uint8 result = add(cpu, register_value_1, value); + + cpu->set_8(cpuRegister_1, result); +} + +static void add_r16_r16(CPU* cpu, Register cpuRegister_1, Register cpuRegister_2) { + uint16 register_value_1 = cpu->get_16(cpuRegister_1); + uint16 register_value_2 = cpu->get_16(cpuRegister_2); + uint16 result = add_16(cpu, register_value_1, register_value_2); + + cpu->set_16(cpuRegister_1, result); +} + +static void decrement_sp(CPU* cpu) { + cpu->sp = Bytes::wrappingSub_16(cpu->sp, 1); +} + +static void add_r16_sp(CPU* cpu, Register cpuRegister) { + uint16 value_1 = cpu->get_16(cpuRegister); + uint16 value_2 = cpu->sp; + uint16 result = add_16(cpu, value_1, value_2); + + cpu->set_16(cpuRegister, result); +} + +static void return_(CPU* cpu, Memory* memory) { + uint8 lower = memory->get_8(cpu->sp); + uint8 upper = memory->get_8(cpu->sp + 1); + uint16 address = Bytes::join_8(upper, lower); + cpu->sp += 2; + jump_n16(cpu, address); +} + +static void return_enable_interrupts(CPU* cpu, Memory* memory) { + return_(cpu, memory); + enable_interrupts(cpu); +} + +static bool return_carry(CPU* cpu, Memory* memory) { + if (cpu->flag_c) { + return_(cpu, memory); + return true; + } + + return false; +} + +static bool return_not_carry(CPU* cpu, Memory* memory) { + if (!cpu->flag_c) { + return_(cpu, memory); + return true; + } + + return false; +} + +static bool return_zero(CPU* cpu, Memory* memory) { + if (cpu->flag_z) { + return_(cpu, memory); + return true; + } + + return false; +} + +static bool return_not_zero(CPU* cpu, Memory* memory) { + if (!cpu->flag_z) { + return_(cpu, memory); + return true; + } + + return false; +} + +static void halt(CPU* cpu) { + cpu->halt = true; +} + +static void stop(CPU* cpu) { +} + +static void toggle_carry(CPU* cpu) { + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = !cpu->flag_c; +} + +static void not_used() { + throw std::invalid_argument("Reached invalid instruction."); +} + +// Stack + +static void push_n16(CPU* cpu, Memory* memory, uint16 value) { + memory->set_8(cpu->sp - 1, Bytes::split_16_upper(value)); + memory->set_8(cpu->sp - 2, Bytes::split_16_lower(value)); + cpu->sp -= 2; +} + +static void push_r16(CPU* cpu, Memory* memory, Register cpuRegister) { + uint16 register_value = cpu->get_16(cpuRegister); + push_n16(cpu, memory, register_value); +} + +static void pop_r16(CPU* cpu, Memory* memory, Register cpuRegister) { + uint8 lower = memory->get_8(cpu->sp); + uint8 upper = memory->get_8(cpu->sp + 1); + uint16 value = Bytes::join_8(upper, lower); + + cpu->set_16(cpuRegister, value); + cpu->sp += 2; +} + +static void restart_n8(CPU* cpu, Memory* memory, uint8 value) { + uint16 next_instruction = cpu->pc; + push_n16(cpu, memory, next_instruction); + jump_n16(cpu, value); +} + +void Instructions::call(CPU* cpu, Memory* memory, uint16 value) { + uint16 next_instruction = cpu->pc; + push_n16(cpu, memory, next_instruction); + jump_n16(cpu, value); +} + +static bool call_carry_n16(CPU* cpu, Memory* memory, uint16 value) { + if (cpu->flag_c) { + Instructions::call(cpu, memory, value); + return true; + } + + return false; +} + +static bool call_not_carry_n16(CPU* cpu, Memory* memory, uint16 value) { + if (!cpu->flag_c) { + Instructions::call(cpu, memory, value); + return true; + } + + return false; +} + +static bool call_not_zero_n16(CPU* cpu, Memory* memory, uint16 value) { + if (!cpu->flag_z) { + Instructions::call(cpu, memory, value); + return true; + } + + return false; +} + +static bool call_zero_n16(CPU* cpu, Memory* memory, uint16 value) { + if (cpu->flag_z) { + Instructions::call(cpu, memory, value); + return true; + } + + return false; +} + +// Loads + +static void load_r8_an8(CPU* cpu, Memory* memory, Register cpuRegister, uint8 value) { + uint8 new_value = memory->get_8(0xFF00 + value); + cpu->set_8(cpuRegister, new_value); +} + +static void load_r8_r8(CPU* cpu, Register cpuRegister_1, Register cpuRegister_2) { + uint8 value = cpu->get_8(cpuRegister_2); + cpu->set_8(cpuRegister_1, value); +} + +static void load_r8_r16(CPU* cpu, Memory* memory, Register cpuRegister_1, Register cpuRegister_2) { + uint16 address = cpu->get_16(cpuRegister_2); + uint8 value = memory->get_8(address); + cpu->set_8(cpuRegister_1, value); +} + +static void load_r8_n8(CPU* cpu, Register cpuRegister, uint8 value) { + cpu->set_8(cpuRegister, value); +} + +static void load_r8_n16(CPU* cpu, Memory* memory, Register cpuRegister, uint16 value) { + uint8 new_value = memory->get_8(value); + cpu->set_8(cpuRegister, new_value); +} + +static void load_r16_r8(CPU* cpu, Memory* memory, Register cpuRegister_1, Register cpuRegister_2) { + uint16 address = cpu->get_16(cpuRegister_1); + uint8 value = cpu->get_8(cpuRegister_2); + memory->set_8(address, value); +} + +static void load_r16_n16(CPU* cpu, Register cpuRegister, uint16 value) { + cpu->set_16(cpuRegister, value); +} + +static void load_n16_r8(CPU* cpu, Memory* memory, uint16 address, Register cpuRegister) { + uint8 register_value = cpu->get_8(cpuRegister); + memory->set_8(address, register_value); +} + +static void load_high_n8_a(CPU* cpu, Memory* memory, uint8 value) { + uint16 address = 0xFF00 + value; + uint8 register_value = cpu->get_8(A); + memory->set_8(address, register_value); +} + +static void load_decrement_hl_a(CPU* cpu, Memory* memory) { + uint8 value = cpu->get_8(A); + uint16 address = cpu->get_16(HL); + memory->set_8(address, value); + cpu->set_16(HL, address - 1); +} + +static void load_sp_r16(CPU* cpu, Register cpuRegister) { + uint16 register_value = cpu->get_16(cpuRegister); + cpu->sp = register_value; +} + +static void load_r16_n8(CPU* cpu, Memory* memory, Register cpuRegister, uint8 value) { + uint16 address = cpu->get_16(cpuRegister); + memory->set_8(address, value); +} + +static void load_ar8_r8(CPU* cpu, Memory* memory, Register cpuRegister_1, Register cpuRegister_2) { + uint8 value = cpu->get_8(cpuRegister_2); + uint16 address = 0xFF00 + cpu->get_8(cpuRegister_1); + memory->set_8(address, value); +} + +// Math + +static void increment_r8(CPU* cpu, Register cpuRegister) { + uint8 register_value = cpu->get_8(cpuRegister); + uint8 value = Bytes::wrappingAdd_8(register_value, 1); + cpu->set_8(cpuRegister, value); + + cpu->flag_z = value == 0; + cpu->flag_n = false; + cpu->flag_h = Bytes::isHalfCarryAdd_8(register_value, 1); +} + +static void increment_ar16(CPU* cpu, Memory* memory, Register cpuRegister) { + uint16 address = cpu->get_16(cpuRegister); + uint8 value = memory->get_8(address); + uint8 new_value = Bytes::wrappingAdd_8(value, 1); + memory->set_8(address, new_value); + + cpu->flag_z = new_value == 0; + cpu->flag_n = false; + cpu->flag_h = Bytes::isHalfCarryAdd_8(value, 1); +} + +static void decrement_ar16(CPU* cpu, Memory* memory, Register cpuRegister) { + uint16 address = cpu->get_16(cpuRegister); + uint8 value = memory->get_8(address); + uint8 new_value = Bytes::wrappingSub_8(value, 1); + memory->set_8(address, new_value); + + cpu->flag_z = new_value == 0; + cpu->flag_n = true; + cpu->flag_h = Bytes::isHalfCarrySub_8(value, 1); +} + +static void increment_r16(CPU* cpu, Register cpuRegister) { + uint16 register_value = cpu->get_16(cpuRegister); + cpu->set_16(cpuRegister, Bytes::wrappingAdd_16(register_value, 1)); +} + +static void decrement_r16(CPU* cpu, Register cpuRegister) { + uint16 register_value = cpu->get_16(cpuRegister); + uint16 newValue = Bytes::wrappingSub_16(register_value, 1); + cpu->set_16(cpuRegister, newValue); +} + +static void add_carry_r8_n8(CPU* cpu, Register cpuRegister, uint8 value) { + uint8 carry_value = cpu->flag_c ? 1 : 0 ; + uint8 register_value = cpu->get_8(cpuRegister); + uint8 result = Bytes::wrappingAdd_8(Bytes::wrappingAdd_8(register_value, value), carry_value); + + cpu->flag_z = result == 0; + cpu->flag_n = false; + cpu->flag_h = Bytes::isHalfCarryAdd_8_three(register_value, value, carry_value); + cpu->flag_c = Bytes::isCarryAdd_8_three(register_value, value, carry_value); + + cpu->set_8(cpuRegister, result); +} + + +static void sub_carry_r8_n8(CPU* cpu, Register cpuRegister, uint8 value) { + uint8 carry_value = cpu->flag_c ? 1 : 0; + uint8 register_value = cpu->get_8(cpuRegister); + uint8 result = Bytes::wrappingSub_8(Bytes::wrappingSub_8(register_value, value), carry_value); + + cpu->flag_z = result == 0; + cpu->flag_n = true; + cpu->flag_h = Bytes::isHalfCarrySub_8_three(register_value, value, carry_value); + cpu->flag_c = Bytes::isCarrySub_8_three(register_value, value, carry_value); + + cpu->set_8(cpuRegister, result); +} + +static void add_cary_r8_r8(CPU* cpu, Register cpuRegister_1, Register cpuRegister_2) { + uint8 register_value_2 = cpu->get_8(cpuRegister_2); + add_carry_r8_n8(cpu, cpuRegister_1, register_value_2); +} + +static void add_carry_r8_r16(CPU* cpu, Memory* memory, Register cpuRegister_1, Register cpuRegister_2) { + uint16 address = cpu->get_16(cpuRegister_2); + uint8 value = memory->get_8(address); + add_carry_r8_n8(cpu, cpuRegister_1, value); +} + +static void sub_carry_r8_r16(CPU* cpu, Memory* memory, Register cpuRegister_1, Register cpuRegister_2) { + uint16 address = cpu->get_16(cpuRegister_2); + uint8 value = memory->get_8(address); + sub_carry_r8_n8(cpu, cpuRegister_1, value); +} + +static void add_r8_r8(CPU* cpu, Register cpuRegister_1, Register cpuRegister_2) { + uint8 register_value_1 = cpu->get_8(cpuRegister_1); + uint8 register_value_2 = cpu->get_8(cpuRegister_2); + uint8 result = add(cpu, register_value_1, register_value_2); + + cpu->set_8(cpuRegister_1, result); +} + +static void add_r8_n8(CPU* cpu, Register cpuRegister, uint8 value) { + uint8 register_value = cpu->get_8(cpuRegister); + uint8 result = add(cpu, register_value, value); + + cpu->set_8(cpuRegister, result); +} + +static void sub_carry_r8_r8(CPU* cpu, Register cpuRegister_1, Register cpuRegister_2) { + uint8 register_value_2 = cpu->get_8(cpuRegister_2); + sub_carry_r8_n8(cpu, cpuRegister_1, register_value_2); +} + +static void sub_r8_r8(CPU* cpu, Register cpuRegister_1, Register cpuRegister_2) { + uint8 register_value_1 = cpu->get_8(cpuRegister_1); + uint8 register_value_2 = cpu->get_8(cpuRegister_2); + uint8 result = sub(cpu, register_value_1, register_value_2); + + cpu->set_8(cpuRegister_1, result); +} + +static void sub_r8_n8(CPU* cpu, Register cpuRegister, uint8 value) { + uint8 register_value = cpu->get_8(cpuRegister); + uint8 result = sub(cpu, register_value, value); + + cpu->set_8(cpuRegister, result); +} + +static void compare_n8(CPU* cpu, uint8 value) { + uint8 register_value = cpu->get_8(A); + sub(cpu, register_value, value); +} + +static void compare_r8(CPU* cpu, Register cpuRegister) { + uint8 register_value = cpu->get_8(cpuRegister); + compare_n8(cpu, register_value); +} + +static void compare_r16(CPU* cpu, Memory* memory, Register cpuRegister) { + uint16 address = cpu->get_16(cpuRegister); + uint8 value = memory->get_8(address); + compare_n8(cpu, value); +} + +static void xor_8(CPU* cpu, uint8 value) { + uint8 result = value ^ cpu->get_8(A); + cpu->set_8(A, result); + + cpu->flag_z = result == 0; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = false; +} + +static void xor_r8_n8(CPU* cpu, Register cpuRegister, uint8 value) { + xor_8(cpu, value); +} + +static void xor_r8(CPU* cpu, Register cpuRegister) { + uint8 register_value = cpu->get_8(cpuRegister); + xor_8(cpu, register_value); +} + +static uint8 or_8(CPU* cpu, uint8 value_1, uint8 value_2) { + uint8 result = value_1 | value_2; + + cpu->flag_z = result == 0; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = false; + + return result; +} + +static void or_r16(CPU* cpu, Memory* memory, Register cpuRegister) { + uint8 register_value = cpu->get_8(A); + uint16 address = cpu->get_16(cpuRegister); + uint8 value = memory->get_8(address); + uint8 result = or_8(cpu, register_value, value); + cpu->set_8(A, result); +} + +static void xor_r16(CPU* cpu, Memory* memory, Register cpuRegister) { + uint16 address = cpu->get_16(cpuRegister); + uint8 value = memory->get_8(address); + xor_8(cpu, value); +} + +static void and_r8_r8(CPU* cpu, Register cpuRegister_1, Register cpuRegister_2) { + uint8 register_value_1 = cpu->get_8(cpuRegister_1); + uint8 register_value_2 = cpu->get_8(cpuRegister_2); + uint8 result = and_(cpu, register_value_1, register_value_2); + cpu->set_8(cpuRegister_1, result); +} + +static void and_r8_n8(CPU* cpu, Register cpuRegister, uint8 value) { + uint8 register_value = cpu->get_8(cpuRegister); + uint8 result = and_(cpu, register_value, value); + cpu->set_8(cpuRegister, result); +} + +static void or_r8_n8(CPU* cpu, Register cpuRegister, uint8 value) { + uint8 register_value_1 = cpu->get_8(cpuRegister); + uint8 result = register_value_1 | value; + cpu->set_8(cpuRegister, result); + + cpu->flag_z = result == 0; + cpu->flag_n = false; + cpu->flag_h = false; + cpu->flag_c = false; +} + +static void or_r8(CPU* cpu, Register cpuRegister) { + uint8 register_value_1 = cpu->get_8(A); + uint8 register_value_2 = cpu->get_8(cpuRegister); + uint8 result = or_8(cpu, register_value_1, register_value_2); + cpu->set_8(A, result); +} + +static void decrement_r8(CPU* cpu, Register cpuRegister) { + uint8 register_value = cpu->get_8(cpuRegister); + uint8 value = Bytes::wrappingSub_8(register_value, 1); + cpu->set_8(cpuRegister, value); + + cpu->flag_z = value == 0; + cpu->flag_n = true; + cpu->flag_h = Bytes::isHalfCarrySub_8(register_value, 1); +} + +// Jumps + +static bool jump_carry_n16(CPU* cpu, uint16 value) { + if (cpu->flag_c) { + jump_n16(cpu, value); + return true; + } + + return false; +} + +static bool jump_not_carry_n16(CPU* cpu, uint16 value) { + if (!cpu->flag_c) { + jump_n16(cpu, value); + return true; + } + + return false; +} + +static bool jump_not_zero_n16(CPU* cpu, uint16 value) { + if (!cpu->flag_z) { + jump_n16(cpu, value); + return true; + } + + return false; +} + +static bool jump_zero_n16(CPU* cpu, uint16 value) { + if (cpu->flag_z) { + jump_n16(cpu, value); + return true; + } + + return false; +} + +static void jump_right_n8(CPU* cpu, uint8 value) { + int8 signed_value = Bytes::toSigned_8(value); + uint16 new_pc = cpu->pc + signed_value; + cpu->pc = new_pc; +} + +static bool jump_right_not_zero_n8(CPU* cpu, uint8 value) { + if (cpu->flag_z == false) { + jump_right_n8(cpu, value); + return true; + } + + return false; +} + +static bool jump_right_zero_r8(CPU* cpu, uint8 value) { + if (cpu->flag_z) { + jump_right_n8(cpu, value); + return true; + } + + return false; +} + +static bool jump_right_not_carry_n8(CPU* cpu, uint8 value) { + if (!cpu->flag_c) { + jump_right_n8(cpu, value); + return true; + } + + return false; +} + +static bool jump_right_carry_n8(CPU* cpu, uint8 value) { + if (cpu->flag_c == true) { + jump_right_n8(cpu, value); + return true; + } + + return false; +} + +static bool run_cb(uint8 index, CPU *cpu, Memory *memory) { + switch(index) { + + case 0x00: + rotate_left_carry_r8_cb(cpu, B); + return true; + + case 0x01: + rotate_left_carry_r8_cb(cpu, C); + return true; + + case 0x02: + rotate_left_carry_r8_cb(cpu, D); + return true; + + case 0x03: + rotate_left_carry_r8_cb(cpu, E); + return true; + + case 0x04: + rotate_left_carry_r8_cb(cpu, H); + return true; + + case 0x05: + rotate_left_carry_r8_cb(cpu, L); + return true; + + case 0x06: + rotate_left_carry_r16(cpu, memory, HL); + return true; + + case 0x07: + rotate_left_carry_r8_cb(cpu, A); + return true; + + case 0x08: + rotate_right_carry_r8_cb(cpu, B); + return true; + + case 0x09: + rotate_right_carry_r8_cb(cpu, C); + return true; + + case 0x0a: + rotate_right_carry_r8_cb(cpu, D); + return true; + + case 0x0b: + rotate_right_carry_r8_cb(cpu, E); + return true; + + case 0x0c: + rotate_right_carry_r8_cb(cpu, H); + return true; + + case 0x0d: + rotate_right_carry_r8_cb(cpu, L); + return true; + + case 0x0e: + rotate_right_carry_r16(cpu, memory, HL); + return true; + + case 0x0f: + rotate_right_carry_r8_cb(cpu, A); + return true; + + case 0x10: + rotate_left_r8_cb(cpu, B); + return true; + + case 0x11: + rotate_left_r8_cb(cpu, C); + return true; + + case 0x12: + rotate_left_r8_cb(cpu, D); + return true; + + case 0x13: + rotate_left_r8_cb(cpu, E); + return true; + + case 0x14: + rotate_left_r8_cb(cpu, H); + return true; + + case 0x15: + rotate_left_r8_cb(cpu, L); + return true; + + case 0x16: + rotate_left_r16(cpu, memory, HL); + return true; + + case 0x17: + rotate_left_r8_cb(cpu, A); + return true; + + case 0x18: + rotate_right_r8_cb(cpu, B); + return true; + + case 0x19: + rotate_right_r8_cb(cpu, C); + return true; + + case 0x1a: + rotate_right_r8_cb(cpu, D); + return true; + + case 0x1b: + rotate_right_r8_cb(cpu, E); + return true; + + case 0x1c: + rotate_right_r8_cb(cpu, H); + return true; + + case 0x1d: + rotate_right_r8_cb(cpu, L); + return true; + + case 0x1e: + rotate_right_r16(cpu, memory, HL); + return true; + + case 0x1f: + rotate_right_r8_cb(cpu, A); + return true; + + case 0x20: + shift_left_r8(cpu, B); + return true; + + case 0x21: + shift_left_r8(cpu, C); + return true; + + case 0x22: + shift_left_r8(cpu, D); + return true; + + case 0x23: + shift_left_r8(cpu, E); + return true; + + case 0x24: + shift_left_r8(cpu, H); + return true; + + case 0x25: + shift_left_r8(cpu, L); + return true; + + case 0x26: + shift_left_r16(cpu, memory, HL); + return true; + + case 0x27: + shift_left_r8(cpu, A); + return true; + + case 0x28: + shift_right_weird_r8(cpu, B); + return true; + + case 0x29: + shift_right_weird_r8(cpu, C); + return true; + + case 0x2a: + shift_right_weird_r8(cpu, D); + return true; + + case 0x2b: + shift_right_weird_r8(cpu, E); + return true; + + case 0x2c: + shift_right_weird_r8(cpu, H); + return true; + + case 0x2d: + shift_right_weird_r8(cpu, L); + return true; + + case 0x2e: + shift_right_weird_r16(cpu, memory, HL); + return true; + + case 0x2f: + shift_right_weird_r8(cpu, A); + return true; + + case 0x30: + swap_r8(cpu, B); + return true; + + case 0x31: + swap_r8(cpu, C); + return true; + + case 0x32: + swap_r8(cpu, D); + return true; + + case 0x33: + swap_r8(cpu, E); + return true; + + case 0x34: + swap_r8(cpu, H); + return true; + + case 0x35: + swap_r8(cpu, L); + return true; + + case 0x36: + swap_r16(cpu, memory, HL); + return true; + + case 0x37: + swap_r8(cpu, A); + return true; + + case 0x38: + shift_right_r8(cpu, B); + return true; + + case 0x39: + shift_right_r8(cpu, C); + return true; + + case 0x3a: + shift_right_r8(cpu, D); + return true; + + case 0x3b: + shift_right_r8(cpu, E); + return true; + + case 0x3c: + shift_right_r8(cpu, H); + return true; + + case 0x3d: + shift_right_r8(cpu, L); + return true; + + case 0x3e: + shift_right_r16(cpu, memory, HL); + return true; + + case 0x3f: + shift_right_r8(cpu, A); + return true; + + case 0x40: + bit_r8(cpu, B, 0); + return true; + + case 0x41: + bit_r8(cpu, C, 0); + return true; + + case 0x42: + bit_r8(cpu, D, 0); + return true; + + case 0x43: + bit_r8(cpu, E, 0); + return true; + + case 0x44: + bit_r8(cpu, H, 0); + return true; + + case 0x45: + bit_r8(cpu, L, 0); + return true; + + case 0x46: + bit_r16(cpu, memory, HL, 0); + return true; + + case 0x47: + bit_r8(cpu, A, 0); + return true; + + case 0x48: + bit_r8(cpu, B, 1); + return true; + + case 0x49: + bit_r8(cpu, C, 1); + return true; + + case 0x4a: + bit_r8(cpu, D, 1); + return true; + + case 0x4b: + bit_r8(cpu, E, 1); + return true; + + case 0x4c: + bit_r8(cpu, H, 1); + return true; + + case 0x4d: + bit_r8(cpu, L, 1); + return true; + + case 0x4e: + bit_r16(cpu, memory, HL, 1); + return true; + + case 0x4f: + bit_r8(cpu, A, 1); + return true; + + case 0x50: + bit_r8(cpu, B, 2); + return true; + + case 0x51: + bit_r8(cpu, C, 2); + return true; + + case 0x52: + bit_r8(cpu, D, 2); + return true; + + case 0x53: + bit_r8(cpu, E, 2); + return true; + + case 0x54: + bit_r8(cpu, H, 2); + return true; + + case 0x55: + bit_r8(cpu, L, 2); + return true; + + case 0x56: + bit_r16(cpu, memory, HL, 2); + return true; + + case 0x57: + bit_r8(cpu, A, 2); + return true; + + case 0x58: + bit_r8(cpu, B, 3); + return true; + + case 0x59: + bit_r8(cpu, C, 3); + return true; + + case 0x5a: + bit_r8(cpu, D, 3); + return true; + + case 0x5b: + bit_r8(cpu, E, 3); + return true; + + case 0x5c: + bit_r8(cpu, H, 3); + return true; + + case 0x5d: + bit_r8(cpu, L, 3); + return true; + + case 0x5e: + bit_r16(cpu, memory, HL, 3); + return true; + + case 0x5f: + bit_r8(cpu, A, 3); + return true; + + case 0x60: + bit_r8(cpu, B, 4); + return true; + + case 0x61: + bit_r8(cpu, C, 4); + return true; + + case 0x62: + bit_r8(cpu, D, 4); + return true; + + case 0x63: + bit_r8(cpu, E, 4); + return true; + + case 0x64: + bit_r8(cpu, H, 4); + return true; + + case 0x65: + bit_r8(cpu, L, 4); + return true; + + case 0x66: + bit_r16(cpu, memory, HL, 4); + return true; + + case 0x67: + bit_r8(cpu, A, 4); + return true; + + case 0x68: + bit_r8(cpu, B, 5); + return true; + + case 0x69: + bit_r8(cpu, C, 5); + return true; + + case 0x6a: + bit_r8(cpu, D, 5); + return true; + + case 0x6b: + bit_r8(cpu, E, 5); + return true; + + case 0x6c: + bit_r8(cpu, H, 5); + return true; + + case 0x6d: + bit_r8(cpu, L, 5); + return true; + + case 0x6e: + bit_r16(cpu, memory, HL, 5); + return true; + + case 0x6f: + bit_r8(cpu, A, 5); + return true; + + case 0x70: + bit_r8(cpu, B, 6); + return true; + + case 0x71: + bit_r8(cpu, C, 6); + return true; + + case 0x72: + bit_r8(cpu, D, 6); + return true; + + case 0x73: + bit_r8(cpu, E, 6); + return true; + + case 0x74: + bit_r8(cpu, H, 6); + return true; + + case 0x75: + bit_r8(cpu, L, 6); + return true; + + case 0x76: + bit_r16(cpu, memory, HL, 6); + return true; + + case 0x77: + bit_r8(cpu, A, 6); + return true; + + case 0x78: + bit_r8(cpu, B, 7); + return true; + + case 0x79: + bit_r8(cpu, C, 7); + return true; + + case 0x7a: + bit_r8(cpu, D, 7); + return true; + + case 0x7b: + bit_r8(cpu, E, 7); + return true; + + case 0x7c: + bit_r8(cpu, H, 7); + return true; + + case 0x7d: + bit_r8(cpu, L, 7); + return true; + + case 0x7e: + bit_r16(cpu, memory, HL, 7); + return true; + + case 0x7f: + bit_r8(cpu, A, 7); + return true; + + case 0x80: + reset_r8(cpu, B, 0); + return true; + + case 0x81: + reset_r8(cpu, C, 0); + return true; + + case 0x82: + reset_r8(cpu, D, 0); + return true; + + case 0x83: + reset_r8(cpu, E, 0); + return true; + + case 0x84: + reset_r8(cpu, H, 0); + return true; + + case 0x85: + reset_r8(cpu, L, 0); + return true; + + case 0x86: + reset_r16(cpu, memory, HL, 0); + return true; + + case 0x87: + reset_r8(cpu, A, 0); + return true; + + case 0x88: + reset_r8(cpu, B, 1); + return true; + + case 0x89: + reset_r8(cpu, C, 1); + return true; + + case 0x8a: + reset_r8(cpu, D, 1); + return true; + + case 0x8b: + reset_r8(cpu, E, 1); + return true; + + case 0x8c: + reset_r8(cpu, H, 1); + return true; + + case 0x8d: + reset_r8(cpu, L, 1); + return true; + + case 0x8e: + reset_r16(cpu, memory, HL, 1); + return true; + + case 0x8f: + reset_r8(cpu, A, 1); + return true; + + case 0x90: + reset_r8(cpu, B, 2); + return true; + + case 0x91: + reset_r8(cpu, C, 2); + return true; + + case 0x92: + reset_r8(cpu, D, 2); + return true; + + case 0x93: + reset_r8(cpu, E, 2); + return true; + + case 0x94: + reset_r8(cpu, H, 2); + return true; + + case 0x95: + reset_r8(cpu, L, 2); + return true; + + case 0x96: + reset_r16(cpu, memory, HL, 2); + return true; + + case 0x97: + reset_r8(cpu, A, 2); + return true; + + case 0x98: + reset_r8(cpu, B, 3); + return true; + + case 0x99: + reset_r8(cpu, C, 3); + return true; + + case 0x9a: + reset_r8(cpu, D, 3); + return true; + + case 0x9b: + reset_r8(cpu, E, 3); + return true; + + case 0x9c: + reset_r8(cpu, H, 3); + return true; + + case 0x9d: + reset_r8(cpu, L, 3); + return true; + + case 0x9e: + reset_r16(cpu, memory, HL, 3); + return true; + + case 0x9f: + reset_r8(cpu, A, 3); + return true; + + case 0xa0: + reset_r8(cpu, B, 4); + return true; + + case 0xa1: + reset_r8(cpu, C, 4); + return true; + + case 0xa2: + reset_r8(cpu, D, 4); + return true; + + case 0xa3: + reset_r8(cpu, E, 4); + return true; + + case 0xa4: + reset_r8(cpu, H, 4); + return true; + + case 0xa5: + reset_r8(cpu, L, 4); + return true; + + case 0xa6: + reset_r16(cpu, memory, HL, 4); + return true; + + case 0xa7: + reset_r8(cpu, A, 4); + return true; + + case 0xa8: + reset_r8(cpu, B, 5); + return true; + + case 0xa9: + reset_r8(cpu, C, 5); + return true; + + case 0xaa: + reset_r8(cpu, D, 5); + return true; + + case 0xab: + reset_r8(cpu, E, 5); + return true; + + case 0xac: + reset_r8(cpu, H, 5); + return true; + + case 0xad: + reset_r8(cpu, L, 5); + return true; + + case 0xae: + reset_r16(cpu, memory, HL, 5); + return true; + + case 0xaf: + reset_r8(cpu, A, 5); + return true; + + case 0xb0: + reset_r8(cpu, B, 6); + return true; + + case 0xb1: + reset_r8(cpu, C, 6); + return true; + + case 0xb2: + reset_r8(cpu, D, 6); + return true; + + case 0xb3: + reset_r8(cpu, E, 6); + return true; + + case 0xb4: + reset_r8(cpu, H, 6); + return true; + + case 0xb5: + reset_r8(cpu, L, 6); + return true; + + case 0xb6: + reset_r16(cpu, memory, HL, 6); + return true; + + case 0xb7: + reset_r8(cpu, A, 6); + return true; + + case 0xb8: + reset_r8(cpu, B, 7); + return true; + + case 0xb9: + reset_r8(cpu, C, 7); + return true; + + case 0xba: + reset_r8(cpu, D, 7); + return true; + + case 0xbb: + reset_r8(cpu, E, 7); + return true; + + case 0xbc: + reset_r8(cpu, H, 7); + return true; + + case 0xbd: + reset_r8(cpu, L, 7); + return true; + + case 0xbe: + reset_r16(cpu, memory, HL, 7); + return true; + + case 0xbf: + reset_r8(cpu, A, 7); + return true; + + case 0xc0: + set_r8(cpu, B, 0); + return true; + + case 0xc1: + set_r8(cpu, C, 0); + return true; + + case 0xc2: + set_r8(cpu, D, 0); + return true; + + case 0xc3: + set_r8(cpu, E, 0); + return true; + + case 0xc4: + set_r8(cpu, H, 0); + return true; + + case 0xc5: + set_r8(cpu, L, 0); + return true; + + case 0xc6: + set_r16(cpu, memory, HL, 0); + return true; + + case 0xc7: + set_r8(cpu, A, 0); + return true; + + case 0xc8: + set_r8(cpu, B, 1); + return true; + + case 0xc9: + set_r8(cpu, C, 1); + return true; + + case 0xca: + set_r8(cpu, D, 1); + return true; + + case 0xcb: + set_r8(cpu, E, 1); + return true; + + case 0xcc: + set_r8(cpu, H, 1); + return true; + + case 0xcd: + set_r8(cpu, L, 1); + return true; + + case 0xce: + set_r16(cpu, memory, HL, 1); + return true; + + case 0xcf: + set_r8(cpu, A, 1); + return true; + + case 0xd0: + set_r8(cpu, B, 2); + return true; + + case 0xd1: + set_r8(cpu, C, 2); + return true; + + case 0xd2: + set_r8(cpu, D, 2); + return true; + + case 0xd3: + set_r8(cpu, E, 2); + return true; + + case 0xd4: + set_r8(cpu, H, 2); + return true; + + case 0xd5: + set_r8(cpu, L, 2); + return true; + + case 0xd6: + set_r16(cpu, memory, HL, 2); + return true; + + case 0xd7: + set_r8(cpu, A, 2); + return true; + + case 0xd8: + set_r8(cpu, B, 3); + return true; + + case 0xd9: + set_r8(cpu, C, 3); + return true; + + case 0xda: + set_r8(cpu, D, 3); + return true; + + case 0xdb: + set_r8(cpu, E, 3); + return true; + + case 0xdc: + set_r8(cpu, H, 3); + return true; + + case 0xdd: + set_r8(cpu, L, 3); + return true; + + case 0xde: + set_r16(cpu, memory, HL, 3); + return true; + + case 0xdf: + set_r8(cpu, A, 3); + return true; + + case 0xe0: + set_r8(cpu, B, 4); + return true; + + case 0xe1: + set_r8(cpu, C, 4); + return true; + + case 0xe2: + set_r8(cpu, D, 4); + return true; + + case 0xe3: + set_r8(cpu, E, 4); + return true; + + case 0xe4: + set_r8(cpu, H, 4); + return true; + + case 0xe5: + set_r8(cpu, L, 4); + return true; + + case 0xe6: + set_r16(cpu, memory, HL, 4); + return true; + + case 0xe7: + set_r8(cpu, A, 4); + return true; + + case 0xe8: + set_r8(cpu, B, 5); + return true; + + case 0xe9: + set_r8(cpu, C, 5); + return true; + + case 0xea: + set_r8(cpu, D, 5); + return true; + + case 0xeb: + set_r8(cpu, E, 5); + return true; + + case 0xec: + set_r8(cpu, H, 5); + return true; + + case 0xed: + set_r8(cpu, L, 5); + return true; + + case 0xee: + set_r16(cpu, memory, HL, 5); + return true; + + case 0xef: + set_r8(cpu, A, 5); + return true; + + case 0xf0: + set_r8(cpu, B, 6); + return true; + + case 0xf1: + set_r8(cpu, C, 6); + return true; + + case 0xf2: + set_r8(cpu, D, 6); + return true; + + case 0xf3: + set_r8(cpu, E, 6); + return true; + + case 0xf4: + set_r8(cpu, H, 6); + return true; + + case 0xf5: + set_r8(cpu, L, 6); + return true; + + case 0xf6: + set_r16(cpu, memory, HL, 6); + return true; + + case 0xf7: + set_r8(cpu, A, 6); + return true; + + case 0xf8: + set_r8(cpu, B, 7); + return true; + + case 0xf9: + set_r8(cpu, C, 7); + return true; + + case 0xfa: + set_r8(cpu, D, 7); + return true; + + case 0xfb: + set_r8(cpu, E, 7); + return true; + + case 0xfc: + set_r8(cpu, H, 7); + return true; + + case 0xfd: + set_r8(cpu, L, 7); + return true; + + case 0xfe: + set_r16(cpu, memory, HL, 7); + return true; + + case 0xff: + set_r8(cpu, A, 7); + return true; + + default: + throw std::invalid_argument("Invalid CB instruction."); + } +} + +instructionInfo Instructions::getInfo(uint8 index, uint8 arg) { + return index == 0xCB ? CB_INSTRUCTION_INFO[arg] : CORE_INSTRUCTION_INFO[index]; +} + +bool Instructions::run(uint8 index, CPU *cpu, Memory *memory, uint8 arg_8, uint16 arg_16) { + switch(index) { + case 0x00: + return true; + case 0x01: load_r16_n16(cpu, BC, arg_16); return true; + case 0x02: load_r16_r8(cpu, memory, BC, A); return true; + case 0x03: increment_r16(cpu, BC); return true; + case 0x04: increment_r8(cpu, B); return true; + case 0x05: decrement_r8(cpu, B); return true; + case 0x06: load_r8_n8(cpu, B, arg_8); return true; + case 0x07: rotate_left_carry_r8(cpu, A); return true; + case 0x08: load_n16_sp(cpu, memory, arg_16); return true; + case 0x09: add_r16_r16(cpu, HL, BC); return true; + case 0x0a: load_r8_r16(cpu, memory, A, BC); return true; + case 0x0b: decrement_r16(cpu, BC); return true; + case 0x0c: increment_r8(cpu, C); return true; + case 0x0d: decrement_r8(cpu, C); return true; + case 0x0e: load_r8_n8(cpu, C, arg_8); return true; + case 0x0f: rotate_right_carry_r8(cpu, A); return true; + case 0x10: stop(cpu); return true; + case 0x11: load_r16_n16(cpu, DE, arg_16); return true; + case 0x12: load_r16_r8(cpu, memory, DE, A); return true; + case 0x13: increment_r16(cpu, DE); return true; + case 0x14: increment_r8(cpu, D); return true; + case 0x15: decrement_r8(cpu, D); return true; + case 0x16: load_r8_n8(cpu, D, arg_8); return true; + case 0x17: rotate_left_r8(cpu, A); return true; + case 0x18: jump_right_n8(cpu, arg_8); return true; + case 0x19: add_r16_r16(cpu, HL, DE); return true; + case 0x1a: load_r8_r16(cpu, memory, A, DE); return true; + case 0x1b: decrement_r16(cpu, DE); return true; + case 0x1c: increment_r8(cpu, E); return true; + case 0x1d: decrement_r8(cpu, E); return true; + case 0x1e: load_r8_n8(cpu, E, arg_8); return true; + case 0x1f: rotate_right_r8(cpu, A); return true; + case 0x20: return jump_right_not_zero_n8(cpu, arg_8); + case 0x21: load_r16_n16(cpu, HL, arg_16); return true; + case 0x22: load_increment_r16_r8(cpu, memory, HL, A); return true; + case 0x23: increment_r16(cpu, HL); return true; + case 0x24: increment_r8(cpu, H); return true; + case 0x25: decrement_r8(cpu, H); return true; + case 0x26: load_r8_n8(cpu, H, arg_8); return true; + case 0x27: decimal_r8(cpu, A); return true; + case 0x28: return jump_right_zero_r8(cpu, arg_8); + case 0x29: add_r16_r16(cpu, HL, HL); return true; + case 0x2a: load_increment_r8_ar16(cpu, memory, A, HL); return true; + case 0x2b: decrement_r16(cpu, HL); return true; + case 0x2c: increment_r8(cpu, L); return true; + case 0x2d: decrement_r8(cpu, L); return true; + case 0x2e: load_r8_n8(cpu, L, arg_8); return true; + case 0x2f: complement_r8(cpu, A); return true; + case 0x30: return jump_right_not_carry_n8(cpu, arg_8); + case 0x31: load_sp_n16(cpu, arg_16); return true; + case 0x32: load_decrement_hl_a(cpu, memory); return true; + case 0x33: increment_sp(cpu); return true; + case 0x34: increment_ar16(cpu, memory, HL); return true; + case 0x35: decrement_ar16(cpu, memory, HL); return true; + case 0x36: load_r16_n8(cpu, memory, HL, arg_8); return true; + case 0x37: set_carry(cpu); return true; + case 0x38: return jump_right_carry_n8(cpu, arg_8); + case 0x39: add_r16_sp(cpu, HL); return true; + case 0x3a: load_decrement_r8_ar16(cpu, memory, A, HL); return true; + case 0x3b: decrement_sp(cpu); return true; + case 0x3c: increment_r8(cpu, A); return true; + case 0x3d: decrement_r8(cpu, A); return true; + case 0x3e: load_r8_n8(cpu, A, arg_8); return true; + case 0x3f: toggle_carry(cpu); return true; + case 0x40: load_r8_r8(cpu, B, B); return true; // 0x40 + case 0x41: load_r8_r8(cpu, B, C); return true; // 0x41 + case 0x42: load_r8_r8(cpu, B, D); return true; // 0x42 + case 0x43: load_r8_r8(cpu, B, E); return true; // 0x43 + case 0x44: load_r8_r8(cpu, B, H); return true; // 0x44 + case 0x45: load_r8_r8(cpu, B, L); return true; // 0x45 + case 0x46: load_r8_r16(cpu, memory, B, HL); return true; + case 0x47: load_r8_r8(cpu, B, A); return true; + case 0x48: load_r8_r8(cpu, C, B); return true; // 0x48 + case 0x49: load_r8_r8(cpu, C, C); return true; // 0x49 + case 0x4a: load_r8_r8(cpu, C, D); return true; // 0x4A + case 0x4b: load_r8_r8(cpu, C, E); return true; // 0x4B + case 0x4c: load_r8_r8(cpu, C, H); return true; // 0x4C + case 0x4d: load_r8_r8(cpu, C, L); return true; // 0x4D + case 0x4e: load_r8_r16(cpu, memory, C, HL); return true; + case 0x4f: load_r8_r8(cpu, C, A); return true; + case 0x50: load_r8_r8(cpu, D, B); return true; // 0x50 + case 0x51: load_r8_r8(cpu, D, C); return true; // 0x51 + case 0x52: load_r8_r8(cpu, D, D); return true; // 0x52 + case 0x53: load_r8_r8(cpu, D, E); return true; // 0x53 + case 0x54: load_r8_r8(cpu, D, H); return true; // 0x54 + case 0x55: load_r8_r8(cpu, D, L); return true; // 0x55 + case 0x56: load_r8_r16(cpu, memory, D, HL); return true; + case 0x57: load_r8_r8(cpu, D, A); return true; + case 0x58: load_r8_r8(cpu, E, B); return true; // 0x58 + case 0x59: load_r8_r8(cpu, E, C); return true; // 0x59 + case 0x5a: load_r8_r8(cpu, E, D); return true; // 0x5A + case 0x5b: load_r8_r8(cpu, E, E); return true; // 0x5B + case 0x5c: load_r8_r8(cpu, E, H); return true; // 0x5C + case 0x5d: load_r8_r8(cpu, E, L); return true; // 0x5D + case 0x5e: load_r8_r16(cpu, memory, E, HL); return true; + case 0x5f: load_r8_r8(cpu, E, A); return true; + case 0x60: load_r8_r8(cpu, H, B); return true; // 0x60 + case 0x61: load_r8_r8(cpu, H, C); return true; // 0x61 + case 0x62: load_r8_r8(cpu, H, D); return true; // 0x62 + case 0x63: load_r8_r8(cpu, H, E); return true; // 0x63 + case 0x64: load_r8_r8(cpu, H, H); return true; // 0x64 + case 0x65: load_r8_r8(cpu, H, L); return true; // 0x65 + case 0x66: load_r8_r16(cpu, memory, H, HL); return true; + case 0x67: load_r8_r8(cpu, H, A); return true; + case 0x68: load_r8_r8(cpu, L, B); return true; // 0x68 + case 0x69: load_r8_r8(cpu, L, C); return true; // 0x69 + case 0x6a: load_r8_r8(cpu, L, D); return true; // 0x6A + case 0x6b: load_r8_r8(cpu, L, E); return true; // 0x6B + case 0x6c: load_r8_r8(cpu, L, H); return true; // 0x6C + case 0x6d: load_r8_r8(cpu, L, L); return true; // 0x6D + case 0x6e: load_r8_r16(cpu, memory, L, HL); return true; + case 0x6f: load_r8_r8(cpu, L, A); return true; + case 0x70: load_r16_r8(cpu, memory, HL, B); return true; + case 0x71: load_r16_r8(cpu, memory, HL, C); return true; + case 0x72: load_r16_r8(cpu, memory, HL, D); return true; + case 0x73: load_r16_r8(cpu, memory, HL, E); return true; + case 0x74: load_r16_r8(cpu, memory, HL, H); return true; + case 0x75: load_r16_r8(cpu, memory, HL, L); return true; + case 0x76: halt(cpu); return true; + case 0x77: load_r16_r8(cpu, memory, HL, A); return true; + case 0x78: load_r8_r8(cpu, A, B); return true; // 0x78 + case 0x79: load_r8_r8(cpu, A, C); return true; // 0x79 + case 0x7a: load_r8_r8(cpu, A, D); return true; // 0x7A + case 0x7b: load_r8_r8(cpu, A, E); return true; // 0x7B + case 0x7c: load_r8_r8(cpu, A, H); return true; // 0x7C + case 0x7d: load_r8_r8(cpu, A, L); return true; // 0x7D + case 0x7e: load_r8_r16(cpu, memory, A, HL); return true; + case 0x7f: load_r8_r8(cpu, A, A); return true; // 0x7F + case 0x80: add_r8_r8(cpu, A, B); return true; + case 0x81: add_r8_r8(cpu, A, C); return true; + case 0x82: add_r8_r8(cpu, A, D); return true; + case 0x83: add_r8_r8(cpu, A, E); return true; + case 0x84: add_r8_r8(cpu, A, H); return true; + case 0x85: add_r8_r8(cpu, A, L); return true; + case 0x86: add_r8_r16(cpu, memory, A, HL); return true; + case 0x87: add_r8_r8(cpu, A, A); return true; + case 0x88: add_cary_r8_r8(cpu, A, B); return true; + case 0x89: add_cary_r8_r8(cpu, A, C); return true; + case 0x8a: add_cary_r8_r8(cpu, A, D); return true; + case 0x8b: add_cary_r8_r8(cpu, A, E); return true; + case 0x8c: add_cary_r8_r8(cpu, A, H); return true; + case 0x8d: add_cary_r8_r8(cpu, A, L); return true; + case 0x8e: add_carry_r8_r16(cpu, memory, A, HL); return true; + case 0x8f: add_cary_r8_r8(cpu, A, A); return true; + case 0x90: sub_r8_r8(cpu, A, B); return true; + case 0x91: sub_r8_r8(cpu, A, C); return true; + case 0x92: sub_r8_r8(cpu, A, D); return true; + case 0x93: sub_r8_r8(cpu, A, E); return true; + case 0x94: sub_r8_r8(cpu, A, H); return true; + case 0x95: sub_r8_r8(cpu, A, L); return true; + case 0x96: sub_r8_r16(cpu, memory, A, HL); return true; + case 0x97: sub_r8_r8(cpu, A, A); return true; + case 0x98: sub_carry_r8_r8(cpu, A, B); return true; + case 0x99: sub_carry_r8_r8(cpu, A, C); return true; + case 0x9a: sub_carry_r8_r8(cpu, A, D); return true; + case 0x9b: sub_carry_r8_r8(cpu, A, E); return true; + case 0x9c: sub_carry_r8_r8(cpu, A, H); return true; + case 0x9d: sub_carry_r8_r8(cpu, A, L); return true; + case 0x9e: sub_carry_r8_r16(cpu, memory, A, HL); return true; + case 0x9f: sub_carry_r8_r8(cpu, A, A); return true; + case 0xa0: and_r8_r8(cpu, A, B); return true; + case 0xa1: and_r8_r8(cpu, A, C); return true; + case 0xa2: and_r8_r8(cpu, A, D); return true; + case 0xa3: and_r8_r8(cpu, A, E); return true; + case 0xa4: and_r8_r8(cpu, A, H); return true; + case 0xa5: and_r8_r8(cpu, A, L); return true; + case 0xa6: and_r8_r16(cpu, memory, A, HL); return true; + case 0xa7: and_r8_r8(cpu, A, A); return true; + case 0xa8: xor_r8(cpu, B); return true; + case 0xa9: xor_r8(cpu, C); return true; + case 0xaa: xor_r8(cpu, D); return true; + case 0xab: xor_r8(cpu, E); return true; + case 0xac: xor_r8(cpu, H); return true; + case 0xad: xor_r8(cpu, L); return true; + case 0xae: xor_r16(cpu, memory, HL); return true; + case 0xaf: xor_r8(cpu, A); return true; + case 0xb0: or_r8(cpu, B); return true; + case 0xb1: or_r8(cpu, C); return true; + case 0xb2: or_r8(cpu, D); return true; + case 0xb3: or_r8(cpu, E); return true; + case 0xb4: or_r8(cpu, H); return true; + case 0xb5: or_r8(cpu, L); return true; + case 0xb6: or_r16(cpu, memory, HL); return true; + case 0xb7: or_r8(cpu, A); return true; + case 0xb8: compare_r8(cpu, B); return true; + case 0xb9: compare_r8(cpu, C); return true; + case 0xba: compare_r8(cpu, D); return true; + case 0xbb: compare_r8(cpu, E); return true; + case 0xbc: compare_r8(cpu, H); return true; + case 0xbd: compare_r8(cpu, L); return true; + case 0xbe: compare_r16(cpu, memory, HL); return true; + case 0xbf: compare_r8(cpu, A); return true; + case 0xc0: return return_not_zero(cpu, memory); + case 0xc1: pop_r16(cpu, memory, BC); return true; + case 0xc2: return jump_not_zero_n16(cpu, arg_16); + case 0xc3: jump_n16(cpu, arg_16); return true; + case 0xc4: return call_not_zero_n16(cpu, memory, arg_16); + case 0xc5: push_r16(cpu, memory, BC); return true; + case 0xc6: add_r8_n8(cpu, A, arg_8); return true; + case 0xc7: restart_n8(cpu, memory, 0x00); return true; + case 0xc8: return return_zero(cpu, memory); + case 0xc9: return_(cpu, memory); return true; + case 0xca: return jump_zero_n16(cpu, arg_16); + case 0xcb: return run_cb(arg_8, cpu, memory); + case 0xcc: return call_zero_n16(cpu, memory, arg_16); + case 0xcd: call(cpu, memory, arg_16); return true; + case 0xce: add_carry_r8_n8(cpu, A, arg_8); return true; + case 0xcf: restart_n8(cpu, memory, 0x08); return true; + case 0xd0: return return_not_carry(cpu, memory); + case 0xd1: pop_r16(cpu, memory, DE); return true; + case 0xd2: return jump_not_carry_n16(cpu, arg_16); + case 0xd3: not_used(); return true; + case 0xd4: return call_not_carry_n16(cpu, memory, arg_16); + case 0xd5: push_r16(cpu, memory, DE); return true; + case 0xd6: sub_r8_n8(cpu, A, arg_8); return true; + case 0xd7: restart_n8(cpu, memory, 0x10); return true; + case 0xd8: return return_carry(cpu, memory); + case 0xd9: return_enable_interrupts(cpu, memory); return true; + case 0xda: return jump_carry_n16(cpu, arg_16); + case 0xdb: not_used(); return true; + case 0xdc: + return call_carry_n16(cpu, memory, arg_16); + + + case 0xdd: + not_used(); + return true; + + case 0xde: + sub_carry_r8_n8(cpu, A, arg_8); + return true; + + case 0xdf: + restart_n8(cpu, memory, 0x18); + return true; + + case 0xe0: + load_high_n8_a(cpu, memory, arg_8); + return true; + + case 0xe1: + pop_r16(cpu, memory, HL); + return true; + + case 0xe2: + load_ar8_r8(cpu, memory, C, A); + return true; + + case 0xe3: + not_used(); + return true; + + case 0xe4: + not_used(); + return true; + + case 0xe5: + push_r16(cpu, memory, HL); + return true; + + case 0xe6: + and_r8_n8(cpu, A, arg_8); + return true; + + case 0xe7: + restart_n8(cpu, memory, 0x20); + return true; + + case 0xe8: + add_sp_n8(cpu, arg_8); + return true; + + case 0xe9: + jump_ar16(cpu, memory, HL); + return true; + + case 0xea: + load_n16_r8(cpu, memory, arg_16, A); + return true; + + case 0xeb: + not_used(); + return true; + + case 0xec: + not_used(); + return true; + + case 0xed: + not_used(); + return true; + + case 0xee: + xor_r8_n8(cpu, A, arg_8); + return true; + + case 0xef: + restart_n8(cpu, memory, 0x28); + return true; + + case 0xf0: + load_r8_an8(cpu, memory, A, arg_8); + return true; + + case 0xf1: + pop_r16(cpu, memory, AF); + return true; + + case 0xf2: + load_r8_ar8(cpu, memory, A, C); + return true; + + case 0xf3: + disable_interrupts(cpu); + return true; + + case 0xf4: + not_used(); + return true; + + case 0xf5: + push_r16(cpu, memory, AF); + return true; + + case 0xf6: + or_r8_n8(cpu, A, arg_8); + return true; + + case 0xf7: + restart_n8(cpu, memory, 0x30); + return true; + + case 0xf8: + load_r16_sp_n8(cpu, HL, arg_8); + return true; + + case 0xf9: + load_sp_r16(cpu, HL); + return true; + + case 0xfa: + load_r8_n16(cpu, memory, A, arg_16); + return true; + + case 0xfb: + enable_interrupts(cpu); + return true; + + case 0xfc: + not_used(); + return true; + + case 0xfd: + not_used(); + return true; + + case 0xfe: + compare_n8(cpu, arg_8); + return true; + + case 0xff: + restart_n8(cpu, memory, 0x38); + return true; + + default: + throw std::invalid_argument("Invalid instruction."); + } +} diff --git a/src/LogFile.cpp b/src/LogFile.cpp new file mode 100644 index 0000000..903377c --- /dev/null +++ b/src/LogFile.cpp @@ -0,0 +1,30 @@ +// +// Created by matthew on 06/07/2020. +// + +#include "LogFile.h" + +LogFile::LogFile(std::string path) { + file.open(path); +} + +void LogFile::write(const std::string fmt, ...) { + int size = ((int)fmt.size()) * 2 + 50; // Use a rubric appropriate for your code + std::string str; + va_list ap; + while (1) { // Maximum two passes on a POSIX system... + str.resize(size); + va_start(ap, fmt); + int n = vsnprintf((char *)str.data(), size, fmt.c_str(), ap); + va_end(ap); + if (n > -1 && n < size) { // Everything worked + str.resize(n); + break; + } + if (n > -1) // Needed size returned + size = n + 1; // For null char + else + size *= 2; // Guess at a larger size (OS specific) + } + file << str << "\n"; +} diff --git a/src/Memory.cpp b/src/Memory.cpp new file mode 100644 index 0000000..1efdb6d --- /dev/null +++ b/src/Memory.cpp @@ -0,0 +1,108 @@ +// +// Created by matthew on 05/07/2020. +// + +#include "Memory.h" +#include "Bytes.h" +#include "MemoryMap.h" + +const uint16 ADDRESS_DMA_TRANSFER = 0xFF46; +const uint16 ADDRESS_SPRITE_INFO_START = 0xFE00; + +void Memory::init(CoreMemory *coreMemory, Rom *rom, MemoryHook *cpu, MemoryHook *gpu, MemoryHook* timer) { + this->coreMemory = coreMemory; + this->rom = rom; + this->cpu = cpu; + this->gpu = gpu; + this->timer = timer; + + set_8(LCD_CONTROL, 0x91); + set_8(ADDRESS_JOYPAD, 0x0F); +} + +uint8 Memory::get_8(uint16 address) { + uint8 nibble_1 = (address & 0xF000) >> 12; + + switch(nibble_1) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + return rom->get_8(address); + case 8: + case 9: + case 0xA: + case 0xB: + case 0xC: + case 0xD: + case 0xE: + case 0xF: + if(address == ADDRESS_INTERRUPT_ENABLE || address == ADDRESS_INTERRUPT_FLAGS) { + return cpu->get_8(address); + } else if (address == ADDRESS_JOYPAD) { + return column | 0x0F; + } else if (address >= 0xFF04 && address <= 0xFF07) { + return timer->get_8(address); + } else if (address == ADDRESS_STAT || address == ADDRESS_TARGET_LINE || address == ADDRESS_LINE) { + return gpu->get_8(address); + } + default: + return coreMemory->get_8(address); + } +} + +void Memory::set_8(uint16 address, uint8 value) { + if((address >= 0x0000 && address <= 0x7FFF)) { + rom->set_8(address, value); + return; + } else if (address >= 0xFF04 && address <= 0xFF07) { + timer->set_8(address, value); + return; + } else if (address == ADDRESS_DMA_TRANSFER) { + dma(value); + return; + } else if (address == LCD_CONTROL || address == ADDRESS_STAT || address == ADDRESS_TARGET_LINE || address == ADDRESS_LINE) { + coreMemory->set_8(address, value); + gpu->set_8(address, value); + return; + } else if (address >= TILE_SET_1_START && address < TILE_SET_0_END) { + coreMemory->set_8(address, value); + gpu->set_8(address, value); + } else if (address == ADDRESS_INTERRUPT_ENABLE || address == ADDRESS_INTERRUPT_FLAGS) { + cpu->set_8(address, value); + return; + } else if (address == ADDRESS_JOYPAD) { + uint8 columnValue = value & 0x30; + bool column_1 = Bytes::getBit_8(columnValue, 4); + bool column_2 = Bytes::getBit_8(columnValue, 5); + column = column_1 ? 0 : column_2 ? 1 : column; + } + + coreMemory->set_8(address, value); +} + +void Memory::set_16(uint16 address, uint16 value) { + set_8(address, Bytes::split_16_lower(value)); + set_8(address + 1, Bytes::split_16_upper(value)); +} + +void Memory::flag_interrupt(uint8 bit) { + cpu->flag_interrupt(bit); +} + +void Memory::dma(uint8 value) { + uint8 start = Bytes::join_8(value, 0x00); + uint8 end = Bytes::join_8(value, 0x9F) + 1; + + for(uint16 sourceAddress = start; sourceAddress < end; sourceAddress++) { + uint16 relative = sourceAddress - start; + uint16 targetAddress = ADDRESS_SPRITE_INFO_START + relative; + uint8 sourceValue = get_8(sourceAddress); + set_8(targetAddress, sourceValue); + } +} + diff --git a/src/MonochromePalette.cpp b/src/MonochromePalette.cpp new file mode 100644 index 0000000..8387db6 --- /dev/null +++ b/src/MonochromePalette.cpp @@ -0,0 +1,30 @@ +// +// Created by matthew on 06/07/2020. +// + +#include "MonochromePalette.h" +#include "Types.h" +#include "Bytes.h" + +const uint32 PALETTE_WHITE = 0x00D0F8E0; +const uint32 LIGHT_GREY = 0x0070C088; +const uint32 DARK_GREY = 0x00566834; +const uint32 BLACK = 0x00201808; + +static uint32 getColour(bool first, bool second) { + if(!first && !second) return PALETTE_WHITE; + if(first && !second) return LIGHT_GREY; + if(!first && second) return DARK_GREY; + return BLACK; +} + +palette MonochromePalette::get(uint8 value) { + palette palette; + + palette.colours[0] = getColour(Bytes::getBit_8(value, 0), Bytes::getBit_8(value, 1)); + palette.colours[1] = getColour(Bytes::getBit_8(value, 2), Bytes::getBit_8(value, 3)); + palette.colours[2] = getColour(Bytes::getBit_8(value, 4), Bytes::getBit_8(value, 5)); + palette.colours[3] = getColour(Bytes::getBit_8(value, 6), Bytes::getBit_8(value, 7)); + + return palette; +} diff --git a/src/Pixels.cpp b/src/Pixels.cpp new file mode 100644 index 0000000..23bc409 --- /dev/null +++ b/src/Pixels.cpp @@ -0,0 +1,56 @@ +// +// Created by matthew on 05/07/2020. +// + +#include "Pixels.h" + +uint32 Pixels::get(uint16 x, uint16 y) { + return data[getIndex(x, y)]; +} + +void Pixels::set(uint16 x, uint16 y, uint32 pixel) { + data[getIndex(x, y)] = pixel; +} + +void Pixels::setLine(uint16 y, uint32 *pixels, uint16 offset, uint16 width) { + uint16 startIndex = y * this->width + offset; + + for(uint16 x = 0; x < width; x++) { + data[startIndex++] = pixels[x]; + } +} + +void Pixels::clearLine(uint16 y, uint16 offset, uint16 width) { + uint16 startIndex = y * width + offset; + + for(uint16 x = 0; x < width; x++) { + data[startIndex++] = WHITE; + } +} + +uint32 *Pixels::getLine(uint16 y, uint16 offset, uint16 width) { + uint32* pixels = new uint32[this->width]; + uint16 lineStart = y * this->width; + uint16 sourceIndex = lineStart + offset; + uint16 lineEnd = lineStart + this->width; + + for(uint16 x = 0; x < width; x++) { + pixels[x] = data[sourceIndex++]; + + if(sourceIndex == lineEnd) { + sourceIndex = lineStart; + } + } + + return pixels; +} + +uint16 Pixels::getIndex(uint16 x, uint16 y) { + return y * width + x; +} + +Pixels::Pixels(uint16 width, uint16 height) { + this->width = width; + this->height = height; + data = new uint32[width * height]; +} diff --git a/src/Rom.cpp b/src/Rom.cpp new file mode 100644 index 0000000..bdf36b0 --- /dev/null +++ b/src/Rom.cpp @@ -0,0 +1,33 @@ +// +// Created by matthew on 04/07/2020. +// + +#include "Rom.h" +#include + +const uint16 ADDRESS_TITLE_START = 0x0134; +const uint16 ROM_SIZE = 0x8000; + +Rom::Rom(std::string filename) { + std::ifstream file (filename, std::ios::binary); + file.read((char*) &this->rom, ROM_SIZE); + this->name = readName(this->rom); +} + +uint8 Rom::get_8(uint16 address) { + if(address < ROM_SIZE) { + return this->rom[address]; + } + + throw std::invalid_argument("Attempted to read from invalid ROM address."); +} + +bool Rom::set_8(uint16 address, uint8 value) { + return false; +} + +std::string Rom::readName(uint8* rom) { + char name[15]; + strcpy(name, (const char*) rom + ADDRESS_TITLE_START); + return std::string(name); +} diff --git a/src/Tile.cpp b/src/Tile.cpp new file mode 100644 index 0000000..8c90177 --- /dev/null +++ b/src/Tile.cpp @@ -0,0 +1,56 @@ +// +// Created by matthew on 06/07/2020. +// + +#include "Tile.h" +#include "Bytes.h" + +static void getColours(Memory* memory, uint16 start, uint32* colourIndexes, bool large, bool alternateBank) { + uint16 y = 0; + uint16 bytesEnd = large ? TILE_SIZE_LARGE * 2 : TILE_SIZE * 2; + + for(uint16 index = 0; index < bytesEnd; index += 2) { + uint8 byte_1 = memory->coreMemory->get_8(start + index); + uint8 byte_2 = memory->coreMemory->get_8(start + index + 1); + uint16 x = 0; + + for(uint8 i = 0; i < TILE_SIZE; i++) { + uint8 bit = TILE_SIZE - 1 - i; + bool first = Bytes::getBit_8(byte_1, bit); + bool second = Bytes::getBit_8(byte_2, bit); + uint16 xy = y * TILE_SIZE + x; + + if(!first && !second) { + colourIndexes[xy] = 0; + } else if(first && !second) { + colourIndexes[xy] = 1; + } else if(!first && second) { + colourIndexes[xy] = 2; + } else if(first && second) { + colourIndexes[xy] = 3; + } + + x += 1; + } + + y += 1; + } +} + +Tile::Tile(Memory *memory, uint16 start, bool large, bool alternateBank) { + this->large = large; + getColours(memory, start, colourIndexes, false, false); +} + +void Tile::drawLine(Pixels *pixels, palette palette, uint16 localY, uint16 targetX, uint16 targetY, + bool flipX, bool flipY) { + for(uint8 x = 0; x < TILE_SIZE; x++) { + uint16 tileX = flipX ? TILE_SIZE - 1 - x : x; + uint16 tileY = flipY ? TILE_SIZE - 1 - localY : localY; + uint8 colourIndex = colourIndexes[tileY * TILE_SIZE + tileX]; + uint32 pixel = palette.colours[colourIndex]; + uint16 finalX = targetX + x; + + pixels->set(finalX, targetY, pixel); + } +} diff --git a/src/TileMap.cpp b/src/TileMap.cpp new file mode 100644 index 0000000..55480d6 --- /dev/null +++ b/src/TileMap.cpp @@ -0,0 +1,33 @@ +// +// Created by matthew on 06/07/2020. +// + +#include "TileMap.h" + +const uint16 WIDTH = TILE_COUNT * TILE_SIZE; +const uint16 HEIGHT = WIDTH; + +TileMap::TileMap(Memory *memory, uint16 start, uint16 end) : pixels(WIDTH, HEIGHT) { + this->memory = memory; + this->start = start; + this->end = end; +} + +uint16 TileMap::getIndex(uint16 tileX, uint16 tileY) { + return memory->coreMemory->get_8(tileY * 32 + tileX + start); +} + +uint16 TileMap::getAttributeIndex(uint16 tileX, uint16 tileY) { + return memory->coreMemory->get_8(tileY * 32 + tileX + start); +} + +void TileMap::drawTile(uint16 tileIndexX, uint16 tileIndexY, palette palette, TileSet *tileSet) { + uint16 tileIndex = getIndex(tileIndexX, tileIndexY); + Tile* tile = tileSet->getTile(memory, tileIndex, false, false); + uint16 targetX = tileIndexX * TILE_SIZE; + + for(uint8 y = 0; y < TILE_SIZE; y++) { + uint16 targetY = tileIndexY * TILE_SIZE + y; + tile->drawLine(&pixels, palette, y, targetX, targetY, false, false); + } +} diff --git a/src/TileSet.cpp b/src/TileSet.cpp new file mode 100644 index 0000000..33308a1 --- /dev/null +++ b/src/TileSet.cpp @@ -0,0 +1,41 @@ +// +// Created by matthew on 06/07/2020. +// + +#include "TileSet.h" +#include "Bytes.h" + +TileSet::TileSet(uint16 start, bool isSigned) { + this->start = start; + this->isSigned = isSigned; +} + +Tile* TileSet::getTile(Memory *memory, uint8 index, bool large, bool alternateBank) { + uint16 actualIndex = isSigned ? Bytes::wrappingAdd_8(Bytes::toSigned_8(index), 128) : index; + bool tileCached = tileCacheSet[actualIndex]; + + if(tileCached) { + Tile* cachedTile = tileCache[actualIndex]; + return cachedTile; + } + + uint16 tileStart = actualIndex * 16; + Tile* tile = new Tile(memory, start + tileStart, large, alternateBank); + tileCache[actualIndex] = tile; + tileCacheSet[actualIndex] = true; + + return tile; +} + +void TileSet::clearCache() { + for(uint16 i = 0; i < 256; i++) { + bool isCached = tileCacheSet[i]; + + if(isCached) { + Tile* oldTile = tileCache[i]; + delete oldTile; + } + + tileCacheSet[i] = false; + } +} diff --git a/src/Timer.cpp b/src/Timer.cpp new file mode 100644 index 0000000..9c9af5a --- /dev/null +++ b/src/Timer.cpp @@ -0,0 +1,81 @@ +// +// Created by matthew on 05/07/2020. +// + +#include "Timer.h" +#include "Bytes.h" +#include "MemoryMap.h" + +const uint16 SPEEDS[] = {1024, 16, 64, 256}; + +void Timer::step(uint16 cycleCount, Memory *memory) { + for(uint16 i = 0; i < cycleCount; i++) { + dividerCycleCount += 1; + + if(dividerCycleCount == 256) { + divider = Bytes::wrappingAdd_8(divider, 1); + dividerCycleCount = 0; + } + + if(running) { + counterCycleCount += 1; + + if(counterCycleCount == speed) { + counter = Bytes::wrappingAdd_8(counter, 1); + counterCycleCount = 0; + + if(counter == 0) { + counter = modulo; + memory->flag_interrupt(INTERRUPT_BIT_TIMER); + } + } + } + } +} + +uint8 Timer::get_8(uint16 address) { + uint16 relativeAddress = address - ADDRESS_DIVIDER; + uint8 control = 0; + + switch(relativeAddress) { + case 0: + return divider; + case 1: + return counter; + case 2: + return modulo; + case 3: + + if(speed == SPEEDS[1] || speed == SPEEDS[3]) { + control = Bytes::setBit_8(control, 0); + } + if(speed == SPEEDS[2] || speed == SPEEDS[3]) { + control = Bytes::setBit_8(control, 1); + } + if(running) { + control = Bytes::setBit_8(control, 2); + } + return control; + default: + throw std::invalid_argument("Invalid address passed to Timer.get_8"); + } +} + +bool Timer::set_8(uint16 address, uint8 value) { + uint16 relativeAddress = address - ADDRESS_DIVIDER; + + switch(relativeAddress) { + case 0: + divider = 0; + break; + case 1: + counter = value; + break; + case 3: + speed = SPEEDS[value & 0x3]; + running = Bytes::getBit_8(value, 2); + break; + } + + return true; +} diff --git a/src/VRAM.cpp b/src/VRAM.cpp new file mode 100644 index 0000000..b41284a --- /dev/null +++ b/src/VRAM.cpp @@ -0,0 +1,24 @@ +// +// Created by matthew on 06/07/2020. +// + +#include "VRAM.h" +#include "MemoryMap.h" + +void VRAM::setBank(uint8 value) { + bank = value; +} + +uint8 VRAM::get_8(uint16 address) { + return get_8(address, bank); +} + +uint8 VRAM::get_8(uint16 address, uint8 bank) { + uint16 relative = address - ADDRESS_VRAM_START; + return data[bank][relative]; +} + +void VRAM::set_8(uint16 address, uint8 value) { + uint16 relative = address - ADDRESS_VRAM_START; + data[bank][relative] = value; +}