├── images ├── start.png └── tetris.png ├── .gitignore ├── src ├── cpu │ ├── include │ │ └── cpu │ │ │ ├── cpu.fwd.h │ │ │ ├── cpu_interface.h │ │ │ ├── utils.h │ │ │ ├── register │ │ │ ├── register_interface.h │ │ │ └── register.h │ │ │ └── cpu.h │ ├── CMakeLists.txt │ └── src │ │ └── register │ │ └── register.cpp ├── gameboy │ ├── include │ │ └── gameboy │ │ │ ├── gameboy.fwd.h │ │ │ └── gameboy.h │ ├── CMakeLists.txt │ └── src │ │ └── gameboy.cpp ├── gpu │ ├── include │ │ └── gpu │ │ │ ├── gpu.fwd.h │ │ │ ├── gpu_interface.h │ │ │ ├── utils.h │ │ │ └── gpu.h │ ├── CMakeLists.txt │ └── src │ │ └── gpu.cpp ├── memory │ ├── include │ │ └── memory │ │ │ ├── memory.fwd.h │ │ │ ├── memory_interface.h │ │ │ ├── utils.h │ │ │ └── memory.h │ ├── CMakeLists.txt │ └── src │ │ └── memory.cpp ├── cartridge │ ├── include │ │ └── cartridge │ │ │ ├── cartridge.fwd.h │ │ │ ├── cartridge_metadata.h │ │ │ ├── cartridge.h │ │ │ ├── instruction_parser.h │ │ │ └── utils.h │ ├── CMakeLists.txt │ └── src │ │ ├── cartridge_metadata.cpp │ │ ├── instruction_parser.cpp │ │ └── cartridge.cpp ├── debugger │ ├── include │ │ └── debugger │ │ │ ├── debugger.fwd.h │ │ │ ├── debugger_interface.h │ │ │ ├── disassembly_loader.h │ │ │ ├── cli_debugger.h │ │ │ └── debugger_core.h │ ├── CMakeLists.txt │ └── src │ │ ├── debugger_core.cpp │ │ └── cli_debugger.cpp ├── controller │ ├── CMakeLists.txt │ ├── include │ │ └── controller │ │ │ ├── utils.h │ │ │ ├── controller_interface.h │ │ │ └── controller.h │ └── src │ │ └── controller.cpp ├── util │ ├── CMakeLists.txt │ ├── include │ │ └── util │ │ │ ├── helpers.h │ │ │ ├── log.h │ │ │ └── argv_generator.h │ └── src │ │ ├── argv_generator.cpp │ │ ├── log.cpp │ │ └── helpers.cpp ├── video │ ├── include │ │ └── video │ │ │ ├── video_interface.h │ │ │ └── video.h │ ├── CMakeLists.txt │ └── src │ │ └── video.cpp └── main │ └── main.cpp ├── tests ├── tests.cpp ├── memory │ └── mocks │ │ └── memory_mock.h ├── CMakeLists.txt └── cpu │ ├── mocks │ └── register_mock.h │ ├── arithmetic_opcode_test.cpp │ └── register_test.cpp ├── .gitmodules ├── cmake ├── FindClangFormat.cmake ├── clang-format.cmake └── FindSFML.cmake ├── LICENSE ├── README.md ├── .circleci └── config.yml ├── CMakeLists.txt ├── .clang-format └── CODE_OF_CONDUCT.md /images/start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venkat24/tvp/HEAD/images/start.png -------------------------------------------------------------------------------- /images/tetris.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venkat24/tvp/HEAD/images/tetris.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build* 2 | tvp 3 | compile_commands.json 4 | .idea 5 | cmake-build-debug 6 | 7 | -------------------------------------------------------------------------------- /src/cpu/include/cpu/cpu.fwd.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file cpu.fwd.h 3 | * Forward declares the CPU Class 4 | */ 5 | 6 | #pragma once 7 | 8 | namespace cpu { 9 | 10 | class CPU; 11 | 12 | } // namespace cpu 13 | -------------------------------------------------------------------------------- /src/gameboy/include/gameboy/gameboy.fwd.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file gameboy.fwd.h 3 | * Forward declares the Gameboy class 4 | */ 5 | 6 | namespace gameboy { 7 | 8 | class Gameboy; 9 | 10 | } // namespace gameboy -------------------------------------------------------------------------------- /src/gpu/include/gpu/gpu.fwd.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file gpu.fwd.h 3 | * Forward declares the GPU Class 4 | */ 5 | 6 | #pragma once 7 | 8 | namespace gpu { 9 | 10 | class GPU; 11 | 12 | } // namespace gpu 13 | -------------------------------------------------------------------------------- /src/memory/include/memory/memory.fwd.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file memory.fwd.h 3 | * Forward declares the Memory Class 4 | */ 5 | 6 | #pragma once 7 | 8 | namespace memory { 9 | 10 | class Memory; 11 | 12 | } // namespace memory 13 | -------------------------------------------------------------------------------- /src/cartridge/include/cartridge/cartridge.fwd.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file cartridge.fwd.h 3 | * Forwarf Declares the Cartridge class 4 | */ 5 | 6 | #pragma once 7 | 8 | namespace cartridge { 9 | 10 | class Cartridge; 11 | 12 | } // namespace cartridge 13 | -------------------------------------------------------------------------------- /src/debugger/include/debugger/debugger.fwd.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file debugger.fwd.h 3 | * Forward Declares the DebuggerCore Class 4 | */ 5 | 6 | #pragma once 7 | 8 | namespace debugger { 9 | 10 | class DebuggerCore; 11 | 12 | } // namespace debugger 13 | -------------------------------------------------------------------------------- /tests/tests.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | using namespace std; 4 | 5 | TEST(Test, TestingTest) { EXPECT_EQ(1 + 1, 2); } 6 | 7 | int main(int argc, char *argv[]) { 8 | testing::InitGoogleTest(&argc, argv); 9 | return RUN_ALL_TESTS(); 10 | } 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ext/googletest"] 2 | path = ext/googletest 3 | url = https://github.com/google/googletest 4 | [submodule "ext/cxxopts"] 5 | path = ext/cxxopts 6 | url = https://github.com/jarro2783/cxxopts 7 | [submodule "ext/span"] 8 | path = ext/span 9 | url = https://github.com/tcbrindle/span 10 | -------------------------------------------------------------------------------- /src/debugger/include/debugger/debugger_interface.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file debugger_interface.h 3 | * Declares the DebuggerCore Interface Class 4 | */ 5 | 6 | namespace debugger { 7 | class IDebugger { 8 | public: 9 | virtual ~IDebugger(){}; 10 | 11 | /** 12 | * Increments the clock by 1 13 | */ 14 | virtual void tick() = 0; 15 | }; 16 | } // namespace debugger -------------------------------------------------------------------------------- /src/controller/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5.1) 2 | project(controller) 3 | 4 | set(SOURCE_FILES 5 | src/controller.cpp 6 | ) 7 | 8 | add_library(controller STATIC ${SOURCE_FILES}) 9 | 10 | include_directories(${MODULE_INCLUDE_DIRS}) 11 | 12 | target_include_directories(controller PUBLIC 13 | $ 14 | ) 15 | -------------------------------------------------------------------------------- /src/gpu/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5.1) 2 | project(gpu) 3 | 4 | set(SOURCE_FILES 5 | src/gpu.cpp 6 | ) 7 | 8 | include_directories(${MODULE_INCLUDE_DIRS}) 9 | 10 | add_library(gpu STATIC ${SOURCE_FILES}) 11 | 12 | target_link_libraries(gpu util) 13 | 14 | target_include_directories(gpu PUBLIC 15 | $ 16 | ) 17 | -------------------------------------------------------------------------------- /src/util/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5.1) 2 | project(util) 3 | 4 | set(SOURCE_FILES 5 | src/log.cpp 6 | src/helpers.cpp 7 | src/argv_generator.cpp) 8 | 9 | include_directories(${MODULE_INCLUDE_DIRS}) 10 | 11 | add_library(util STATIC ${SOURCE_FILES}) 12 | 13 | target_include_directories(util PUBLIC 14 | $ 15 | ) 16 | -------------------------------------------------------------------------------- /src/controller/include/controller/utils.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file utils.h 3 | * Utils for the Controller class 4 | */ 5 | 6 | #pragma once 7 | 8 | namespace controller { 9 | 10 | /** 11 | * Represents each physical button on the GameBoy 12 | */ 13 | enum class Button { 14 | RIGHT, 15 | LEFT, 16 | UP, 17 | DOWN, 18 | A, 19 | B, 20 | SELECT, 21 | START, 22 | }; 23 | 24 | } // namespace controller 25 | -------------------------------------------------------------------------------- /src/memory/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5.1) 2 | project(memory) 3 | 4 | set(SOURCE_FILES 5 | src/memory.cpp 6 | ) 7 | 8 | include_directories(${MODULE_INCLUDE_DIRS}) 9 | 10 | add_library(memory STATIC ${SOURCE_FILES}) 11 | 12 | target_link_libraries(memory util) 13 | 14 | target_include_directories(memory PUBLIC 15 | $ 16 | ) 17 | -------------------------------------------------------------------------------- /src/cpu/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5.1) 2 | project(cpu) 3 | 4 | set(SOURCE_FILES 5 | src/cpu.cpp 6 | src/opcodes.cpp 7 | src/register/register.cpp 8 | ) 9 | 10 | include_directories(${MODULE_INCLUDE_DIRS}) 11 | 12 | add_library(cpu STATIC ${SOURCE_FILES}) 13 | 14 | target_link_libraries(cpu util) 15 | 16 | target_include_directories(cpu PUBLIC 17 | $ 18 | ) 19 | -------------------------------------------------------------------------------- /src/gameboy/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5.1) 2 | project(gameboy) 3 | 4 | set(SOURCE_FILES 5 | src/gameboy.cpp 6 | ) 7 | 8 | include_directories(${MODULE_INCLUDE_DIRS}) 9 | 10 | add_library(gameboy STATIC ${SOURCE_FILES}) 11 | 12 | target_link_libraries(gameboy util cpu gpu video memory controller cartridge) 13 | 14 | target_include_directories(gameboy PUBLIC 15 | $ 16 | ) 17 | -------------------------------------------------------------------------------- /src/debugger/include/debugger/disassembly_loader.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file disasembly_loader.h 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | namespace debugger { 11 | 12 | /** 13 | * This class uses a the MGBDIS disassembler script, invoked through 14 | * the Python system library 15 | */ 16 | class DisassemblyLoader { 17 | private: 18 | public: 19 | DisassemblyLoader(); 20 | 21 | void disassemble(std::vector data); 22 | }; 23 | 24 | } // namespace debugger -------------------------------------------------------------------------------- /src/cartridge/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5.1) 2 | project(cartridge) 3 | 4 | set(SOURCE_FILES 5 | src/cartridge_metadata.cpp 6 | src/cartridge.cpp 7 | src/instruction_parser.cpp 8 | ) 9 | 10 | include_directories(${MODULE_INCLUDE_DIRS}) 11 | 12 | add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES}) 13 | 14 | target_include_directories(${PROJECT_NAME} PUBLIC 15 | $ 16 | $ 17 | ) -------------------------------------------------------------------------------- /src/debugger/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5.1) 2 | project(debugger) 3 | 4 | set(SOURCE_FILES 5 | src/debugger_core.cpp 6 | src/cli_debugger.cpp 7 | ) 8 | 9 | add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES}) 10 | 11 | include_directories(${MODULE_INCLUDE_DIRS} include) 12 | target_include_directories(${PROJECT_NAME} PUBLIC 13 | $ 14 | $ 15 | ) 16 | target_link_libraries(${PROJECT_NAME} cxxopts) 17 | -------------------------------------------------------------------------------- /src/video/include/video/video_interface.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file video_interface.h 3 | * Declares the Interface for Video - to display a buffer to the screen 4 | */ 5 | #pragma once 6 | 7 | #include "gpu/utils.h" 8 | 9 | #include 10 | 11 | namespace video { 12 | 13 | class VideoInterface { 14 | public: 15 | /** 16 | * This method takes a VideoBuffer array, and outputs it to the display 17 | * 18 | * @param v_buffer Buffer to display 19 | */ 20 | virtual void paint(gpu::VideoBuffer &v_buffer) = 0; 21 | }; 22 | 23 | } // namespace video 24 | -------------------------------------------------------------------------------- /tests/memory/mocks/memory_mock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cpu/cpu_interface.h" 4 | #include "gpu/gpu_interface.h" 5 | #include "memory/memory_interface.h" 6 | 7 | #include 8 | #include 9 | 10 | using namespace std; 11 | using namespace memory; 12 | 13 | class MemoryMock : public MemoryInterface { 14 | MOCK_CONST_METHOD1(read, uint8_t(Address)); 15 | MOCK_METHOD2(write, void(Address, uint8_t)); 16 | MOCK_METHOD1(set_cpu, void(cpu::CPUInterface *_cpu)); 17 | MOCK_METHOD1(set_gpu, void(gpu::GPUInterface *_gpu)); 18 | }; 19 | -------------------------------------------------------------------------------- /cmake/FindClangFormat.cmake: -------------------------------------------------------------------------------- 1 | # Utility to find clang-format 2 | 3 | string(REPLACE ":" ";" _PATH $ENV{PATH}) 4 | 5 | foreach(p ${_PATH}) 6 | file(GLOB cand ${p}/clang-format-[0-9]* ${p}/clang-format) 7 | if(cand) 8 | set(CLANG_FORMAT_EXECUTABLE ${cand}) 9 | set(CLANG_FORMAT_FOUND ON) 10 | execute_process(COMMAND ${CLANG_FORMAT_EXECUTABLE} -version OUTPUT_VARIABLE clang_out ) 11 | string(REGEX MATCH .*\(version[^\n]*\)\n version ${clang_out}) 12 | set(CLANG_FORMAT_VERSION ${CMAKE_MATCH_1}) 13 | break() 14 | else() 15 | set(CLANG_FORMAT_FOUND OFF) 16 | endif() 17 | 18 | endforeach() 19 | -------------------------------------------------------------------------------- /src/util/include/util/helpers.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file helpers.h 3 | * Declares some nifty helper functions 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | #include "memory/utils.h" 10 | 11 | #pragma once 12 | 13 | /** 14 | * Returns hexadecimal string representation for a bunch of integer types 15 | */ 16 | template std::string num_to_hex(T i); 17 | Address string_to_address(std::string num); 18 | 19 | bool string_replace(std::string &str, const std::string &from, 20 | const std::string &to); 21 | 22 | std::string get_mnemonic(uint8_t opcode); 23 | std::string get_cb_mnemonic(uint8_t opcode); 24 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5.1) 2 | project(tests) 3 | 4 | include(GoogleTest) 5 | 6 | include_directories( 7 | . 8 | ${CMAKE_SOURCE_DIR}/src/cpu/include 9 | ${CMAKE_SOURCE_DIR}/src/gpu/include 10 | ${CMAKE_SOURCE_DIR}/src/memory/include 11 | ${CMAKE_SOURCE_DIR}/src/util/include 12 | ) 13 | 14 | set(SOURCE_FILES 15 | tests.cpp 16 | 17 | # CPU 18 | cpu/register_test.cpp 19 | #cpu/arithmetic_opcode_test.cpp 20 | ) 21 | 22 | add_executable(tests ${SOURCE_FILES}) 23 | target_link_libraries(tests cpu memory gpu gtest gmock) 24 | gtest_add_tests(tests "" AUTO) 25 | 26 | install(TARGETS tests 27 | RUNTIME DESTINATION bin 28 | ) 29 | -------------------------------------------------------------------------------- /src/video/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5.1) 2 | project(video) 3 | 4 | # Build static libs in Windows 5 | if (WIN32) 6 | set(SFML_STATIC_LIBRARIES TRUE) 7 | 8 | # This lets the user override SFML_ROOT 9 | cmake_policy(SET CMP0074 NEW) 10 | endif() 11 | 12 | find_package(SFML 2 REQUIRED graphics window system) 13 | 14 | set(SOURCE_FILES 15 | src/video.cpp 16 | ) 17 | 18 | include_directories(${SFML_INCLUDE_DIR} ${MODULE_INCLUDE_DIRS}) 19 | 20 | add_library(video STATIC ${SOURCE_FILES}) 21 | 22 | target_link_libraries(video util ${SFML_DEPENDENCIES} ${SFML_LIBRARIES}) 23 | 24 | target_include_directories(video PUBLIC 25 | $ 26 | ) 27 | -------------------------------------------------------------------------------- /cmake/clang-format.cmake: -------------------------------------------------------------------------------- 1 | # Creates additional target to perform clang-format run, requires clang-format 2 | 3 | # Find clang format 4 | find_package(ClangFormat) 5 | 6 | if(CLANG_FORMAT_FOUND) 7 | message("-- Found clang-format") 8 | message("-- clang-format executable location: ${CLANG_FORMAT_EXECUTABLE}") 9 | message("-- clang-format version: ${CLANG_FORMAT_VERSION}") 10 | else() 11 | message(SEND_ERROR "clang-format executable not found") 12 | endif() 13 | 14 | # Get all project files 15 | file(GLOB_RECURSE ALL_SOURCE_FILES src/*.cpp src/*.h src/*.proto test/*.cpp test/*.h) 16 | 17 | # Add target to build 18 | add_custom_target( 19 | clangformat 20 | COMMAND ${CLANG_FORMAT_EXECUTABLE} 21 | -style=file 22 | -i 23 | ${ALL_SOURCE_FILES} 24 | ) 25 | -------------------------------------------------------------------------------- /src/cpu/include/cpu/cpu_interface.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file cpu_interface.h 3 | * Declares the interface for a CPU 4 | */ 5 | 6 | #include "cpu/register/register_interface.h" 7 | #include "cpu/utils.h" 8 | #include 9 | 10 | #pragma once 11 | 12 | namespace cpu { 13 | 14 | /** 15 | * Define the interface for changing the CPU state 16 | * The only allowed external actions are to feed in a new opcode, 17 | * and increment the clock with a tick 18 | */ 19 | class CPUInterface { 20 | public: 21 | /** 22 | * Virtual Destructor 23 | */ 24 | virtual ~CPUInterface(){}; 25 | 26 | /** 27 | * Increments the clock by 1 28 | */ 29 | virtual ClockCycles tick() = 0; 30 | 31 | /** 32 | * Getter for the Interrupt Enable register 33 | */ 34 | virtual IReg *get_interrupt_enable() = 0; 35 | 36 | /** 37 | * Getter for the Interrupt Flag Register 38 | */ 39 | virtual IReg *get_interrupt_flag() = 0; 40 | }; 41 | 42 | } // namespace cpu 43 | -------------------------------------------------------------------------------- /src/cartridge/src/cartridge_metadata.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file cartridge_metadata.cpp 3 | * Defines the CartridgeMetadata class 4 | */ 5 | 6 | #include "cartridge/cartridge_metadata.h" 7 | 8 | #include 9 | #include 10 | 11 | namespace cartridge { 12 | 13 | CartridgeMetadata::CartridgeMetadata(std::vector &data) { 14 | check_logo_validity(data); 15 | title = std::string(data.begin() + 0x0134, data.begin() + 0x0143); 16 | cgb_flag = data[0x0143]; 17 | old_licensee_code = data[0x014B]; 18 | sgb_flag = data[0x0146]; 19 | cartridge_type = data[0x0147]; 20 | rom_size = data[0x0148]; 21 | cartridge_ram = data[0x0149]; 22 | dest_code = data[0x014A]; 23 | } 24 | 25 | void CartridgeMetadata::check_logo_validity(std::vector &data) { 26 | for (auto i = 0; i < nintendo_logo.size(); i++) { 27 | if (data[nintendo_logo_start_address + i] != nintendo_logo[i]) { 28 | is_logo_valid = false; 29 | return; 30 | } 31 | } 32 | is_logo_valid = true; 33 | } 34 | } // namespace cartridge 35 | -------------------------------------------------------------------------------- /src/controller/include/controller/controller_interface.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file controller_interface.h 3 | * Declares an Interface for the Controller class 4 | */ 5 | 6 | #include "controller/utils.h" 7 | 8 | #include 9 | 10 | #pragma once 11 | 12 | namespace controller { 13 | 14 | class ControllerInterface { 15 | public: 16 | /** 17 | * Take an 8-bit value and set the writable bits 18 | * 19 | * @param value 8-bit value to write 20 | */ 21 | virtual void set_value(uint8_t value) = 0; 22 | 23 | /** 24 | * Return an 8-bit value representing the register state 25 | * 26 | * @return 8-bit value 27 | */ 28 | virtual uint8_t get_value() = 0; 29 | 30 | /** 31 | * Action to press a button 32 | * 33 | * @param button Button to press 34 | */ 35 | virtual void press_button(Button button) = 0; 36 | 37 | /** 38 | * Action to release a button 39 | * 40 | * @param button Button to release 41 | */ 42 | virtual void release_button(Button button) = 0; 43 | }; 44 | 45 | } // namespace controller 46 | -------------------------------------------------------------------------------- /src/util/include/util/log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file log.h 3 | * Declares the Log Class 4 | */ 5 | 6 | #include 7 | 8 | #pragma once 9 | 10 | /** 11 | * List of the different possible logging levels 12 | */ 13 | enum class LogLevel { VERBOSE, INFO, WARN, ERROR, FATAL }; 14 | 15 | /** 16 | * Static class to just dump stuff to std::out with pretty output 17 | */ 18 | class Log { 19 | private: 20 | /** 21 | * True if the logger is enabled, false otherwise 22 | */ 23 | inline static bool enabled; 24 | 25 | public: 26 | Log() = delete; 27 | 28 | static void Enable(); 29 | static void Disable(); 30 | 31 | /// Log something. Defaults to LogLevel::INFO if no level given 32 | static void log(std::string message, LogLevel log_level = LogLevel::INFO); 33 | 34 | /// These classes just call log() with some log level 35 | static void verbose(std::string message); 36 | static void info(std::string message); 37 | static void warn(std::string message); 38 | static void error(std::string message); 39 | static void fatal(std::string message); 40 | }; 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Venkatraman Srikanth 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/debugger/include/debugger/cli_debugger.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file cli_debugger.h 3 | * Declares the CliDebugger Class for CLI Debugger Operations 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "debugger/debugger.fwd.h" 9 | #include "debugger/debugger_interface.h" 10 | #include "debugger_core.h" 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | namespace debugger { 18 | 19 | class CliDebugger : public IDebugger { 20 | private: 21 | /** 22 | * An unique pointer instance to the debugger_core 23 | */ 24 | std::unique_ptr debugger_core; 25 | 26 | /** 27 | * A Command parser provided by cxxopts 28 | */ 29 | cxxopts::Options command_parser; 30 | 31 | public: 32 | /** 33 | * @brief: Overrides tick() and we take user input in this function 34 | */ 35 | void tick() override; 36 | 37 | /** 38 | * @brief Parses commands and passes control to the debugger_core 39 | * @return A status true or false, indicating whether the given operation 40 | * succeeded or not 41 | */ 42 | bool run_command(int argc, char **argv); 43 | 44 | CliDebugger(std::unique_ptr debugger_core); 45 | }; 46 | } // namespace debugger -------------------------------------------------------------------------------- /src/memory/include/memory/memory_interface.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file memory_interface.h 3 | * Declares the interface for accessing main memory 4 | */ 5 | 6 | #include "cpu/cpu_interface.h" 7 | #include "gpu/gpu_interface.h" 8 | #include "memory/utils.h" 9 | #include 10 | 11 | #pragma once 12 | 13 | namespace memory { 14 | 15 | class MemoryInterface { 16 | public: 17 | /** 18 | * Virtual Destructor 19 | */ 20 | virtual ~MemoryInterface(){}; 21 | 22 | /** 23 | * Read a byte from the specified location 24 | * 25 | * @param address Location to read byte from 26 | * @return Value of the byte 27 | */ 28 | virtual uint8_t read(Address address) const = 0; 29 | 30 | /** 31 | * Write a byte to the specified location 32 | * 33 | * @param address Location to write byte to 34 | * @param data Value of the byte 35 | */ 36 | virtual void write(Address address, uint8_t data) = 0; 37 | 38 | /** 39 | * Set the CPU Object pointer for this class 40 | */ 41 | virtual void set_cpu(cpu::CPUInterface *cpu) = 0; 42 | 43 | /** 44 | * Set the GPU Object pointer for this class 45 | */ 46 | virtual void set_gpu(gpu::GPUInterface *gpu) = 0; 47 | }; 48 | 49 | } // namespace memory 50 | -------------------------------------------------------------------------------- /src/gpu/include/gpu/gpu_interface.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file gpu_interface.h 3 | * Declares the interface for a GPU 4 | */ 5 | 6 | #include "cpu/register/register_interface.h" 7 | #include "cpu/utils.h" 8 | 9 | #include 10 | 11 | #pragma once 12 | 13 | namespace gpu { 14 | 15 | class GPUInterface { 16 | public: 17 | /** 18 | * Performs GPU operations corresponding to the given number of CPU cycles 19 | * This is necessary since the CPU and GPU operate in parallel. The GPU must 20 | * only perform as much work as the CPU has (i.e. in the same number of 21 | * cycles) when we emulate them serially 22 | */ 23 | virtual void tick(cpu::ClockCycles cycles) = 0; 24 | 25 | /// Getters for the 12 GPU registers 26 | virtual cpu::IReg *get_lcdc() = 0; 27 | virtual cpu::IReg *get_stat() = 0; 28 | virtual cpu::IReg *get_scy() = 0; 29 | virtual cpu::IReg *get_scx() = 0; 30 | virtual cpu::IReg *get_ly() = 0; 31 | virtual cpu::IReg *get_lyc() = 0; 32 | virtual cpu::IReg *get_wy() = 0; 33 | virtual cpu::IReg *get_wx() = 0; 34 | virtual cpu::IReg *get_bgp() = 0; 35 | virtual cpu::IReg *get_obp0() = 0; 36 | virtual cpu::IReg *get_obp1() = 0; 37 | virtual cpu::IReg *get_dma() = 0; 38 | }; 39 | 40 | } // namespace gpu 41 | -------------------------------------------------------------------------------- /src/util/include/util/argv_generator.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file argv_generator.h 3 | * Declares the ArgvGenerator class 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace std; 13 | 14 | /** 15 | * Converts a string command into a (argc, argv) pair 16 | */ 17 | class ArgvGenerator { 18 | private: 19 | /** 20 | * Argc - Number of args 21 | */ 22 | int argc; 23 | 24 | /** 25 | * Argv - List of char*s with the split command 26 | */ 27 | char **argv; 28 | 29 | public: 30 | /** 31 | * Constructs argc and argv from the given string 32 | * @param input Command to parse 33 | */ 34 | ArgvGenerator(string command); 35 | 36 | /** 37 | * Returns argc and argv 38 | * @return Tuple with argc and argv 39 | */ 40 | tuple get(); 41 | 42 | /** 43 | * Deletes resources 44 | */ 45 | ~ArgvGenerator(); 46 | 47 | // The following are not strictly required, but must be defined as good 48 | // practice to comply with the Rule of 5 49 | 50 | /** 51 | * Copy constructor 52 | */ 53 | ArgvGenerator(const ArgvGenerator &other); 54 | 55 | /** 56 | * Copy assignment operator 57 | */ 58 | ArgvGenerator &operator=(const ArgvGenerator &other); 59 | 60 | // No move constructors here 61 | ArgvGenerator(ArgvGenerator &&other) = delete; 62 | ArgvGenerator &operator=(ArgvGenerator &&other) = delete; 63 | }; -------------------------------------------------------------------------------- /src/cartridge/include/cartridge/cartridge_metadata.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file cartridge_metadata.h 3 | * Declares the CartridgeMetadata class 4 | */ 5 | 6 | #include "cartridge/utils.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #pragma once 13 | 14 | namespace cartridge { 15 | 16 | class CartridgeMetadata { 17 | public: 18 | /** 19 | * The title of the Cartridge. Address from: 0x0134 to: 0x0143 20 | */ 21 | std::string title; 22 | 23 | /** 24 | * A flag for checking whether the logo is valid or not 25 | */ 26 | bool is_logo_valid; 27 | 28 | /** 29 | * CGB Flag Address: 0x0143 30 | */ 31 | uint8_t cgb_flag; 32 | 33 | /** 34 | * Old Licensee Code Address: 0x014B 35 | */ 36 | uint8_t old_licensee_code; 37 | 38 | /** 39 | * SGB Flag Address: 0x0146 40 | */ 41 | uint8_t sgb_flag; 42 | 43 | /** 44 | * Cartridge Type Address: 0x0147 45 | */ 46 | uint8_t cartridge_type; 47 | 48 | /** 49 | * ROM Size Address: 0x0148 50 | */ 51 | uint8_t rom_size; 52 | 53 | /** 54 | * External RAM size Address: 0x0149 55 | */ 56 | uint8_t cartridge_ram; 57 | 58 | /** 59 | * Destination Code Address: 0x014A 60 | */ 61 | uint8_t dest_code; 62 | 63 | CartridgeMetadata(std::vector &data); 64 | 65 | /** 66 | * Checks if the Logo in the Boot ROM matches the Official Nintendo Logo 67 | * 68 | * @param data The Data of the Cartridge 69 | */ 70 | void check_logo_validity(std::vector &data); 71 | }; 72 | 73 | } // namespace cartridge 74 | -------------------------------------------------------------------------------- /src/cpu/include/cpu/utils.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file utils.h 3 | * Declares helpers and utils for the CPU class 4 | */ 5 | 6 | #include "memory/utils.h" 7 | 8 | #include 9 | 10 | #pragma once 11 | 12 | namespace cpu { 13 | 14 | /** 15 | * Represents a single CPU opcode 16 | */ 17 | using OpCode = uint8_t; 18 | 19 | /** 20 | * Type alias to represent clock cycles 21 | */ 22 | using ClockCycles = uint64_t; 23 | 24 | /** 25 | * Flag Register bits reference: 26 | * ZERO -> Set when the result of the previous transaction was zero 27 | * SUBTRACT -> Set when the previous operation was a subract operation 28 | * HALFCARRY -> Set when the previous operation carried from bit 4 29 | * CARRY -> Set when the previous operation carries from bit 8 30 | */ 31 | namespace flag { 32 | enum FlagBits : uint8_t { 33 | // Number corresponds to the bit number of the Flag register 34 | ZERO = 7, 35 | SUBTRACT = 6, 36 | HALFCARRY = 5, 37 | CARRY = 4 38 | }; 39 | } // namespace flag 40 | 41 | /** 42 | * Describes the different user-toggleable interrupts that can trigger the CPU 43 | * The enum number corresponds to the bit number in the interrupt registers 44 | */ 45 | enum class Interrupt { 46 | VBLANK = 0, 47 | LCD_STAT = 1, 48 | TIMER = 2, 49 | SERIAL = 3, 50 | JOYPAD = 4 51 | }; 52 | 53 | const std::array interrupt_vector = { 54 | 0x0040, // VBLANK 55 | 0x0048, // LCD_STAT 56 | 0x0050, // TIMER 57 | 0x0058, // SERIAL 58 | 0x0060 // JOYPAD 59 | }; 60 | 61 | } // namespace cpu 62 | -------------------------------------------------------------------------------- /tests/cpu/mocks/register_mock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cpu/register/register_interface.h" 4 | 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | using namespace cpu; 10 | 11 | class RegisterMock : public RegisterInterface { 12 | public: 13 | MOCK_METHOD1(set, void(uint8_t)); 14 | MOCK_CONST_METHOD0(get, uint8_t()); 15 | MOCK_METHOD2(set_bit, void(uint8_t, bool)); 16 | MOCK_CONST_METHOD1(get_bit, bool(uint8_t)); 17 | MOCK_METHOD0(operator_increment, void()); 18 | MOCK_METHOD0(operator_decrement, void()); 19 | virtual void operator++(__attribute__((unused)) int i) { 20 | operator_increment(); 21 | } 22 | virtual void operator++() { operator_increment(); } 23 | virtual void operator--(__attribute__((unused)) int i) { 24 | operator_decrement(); 25 | } 26 | virtual void operator--() { operator_decrement(); } 27 | }; 28 | 29 | class DoubleRegisterMock : public DoubleRegisterInterface { 30 | public: 31 | MOCK_METHOD1(set, void(uint16_t)); 32 | MOCK_CONST_METHOD0(get, uint16_t()); 33 | MOCK_CONST_METHOD0(get_high, uint8_t()); 34 | MOCK_CONST_METHOD0(get_low, uint8_t()); 35 | MOCK_METHOD2(set_bit, void(uint8_t, bool)); 36 | MOCK_CONST_METHOD1(get_bit, bool(uint8_t)); 37 | MOCK_METHOD0(operator_increment, void()); 38 | MOCK_METHOD0(operator_decrement, void()); 39 | virtual void operator++(__attribute__((unused)) int i) { 40 | operator_increment(); 41 | } 42 | virtual void operator++() { operator_increment(); } 43 | virtual void operator--(__attribute__((unused)) int i) { 44 | operator_decrement(); 45 | } 46 | virtual void operator--() { operator_decrement(); } 47 | }; 48 | -------------------------------------------------------------------------------- /src/video/include/video/video.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file video.h 3 | * Declares the Video class for display and GUI features 4 | */ 5 | #pragma once 6 | 7 | #include "cartridge/cartridge_metadata.h" 8 | #include "controller/controller_interface.h" 9 | #include "gpu/utils.h" 10 | #include "video/video_interface.h" 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | namespace video { 17 | 18 | class Video : public VideoInterface { 19 | private: 20 | /** 21 | * Window context that the application will be rendered in 22 | */ 23 | std::unique_ptr window; 24 | 25 | /** 26 | * Window texture, that serves as an in-memory buffer of the output pixels 27 | */ 28 | std::unique_ptr window_texture; 29 | 30 | /** 31 | * Window image, that the texture is loaded 32 | */ 33 | std::unique_ptr window_image; 34 | 35 | /** 36 | * Window sprite that the texture is loaded to 37 | */ 38 | std::unique_ptr window_sprite; 39 | 40 | /** 41 | * Pointer to controller instance 42 | */ 43 | controller::ControllerInterface *controller; 44 | 45 | /** 46 | * Pointer to cartridge instance 47 | */ 48 | cartridge::CartridgeMetadata *cartridge_metadata; 49 | 50 | /** 51 | * Processes all external events like key presses 52 | */ 53 | void event_handler(); 54 | 55 | public: 56 | /** 57 | * Constructor 58 | */ 59 | Video(controller::ControllerInterface *controller, 60 | cartridge::CartridgeMetadata *cartridge_metadata); 61 | 62 | /** 63 | * Print the contents of the buffer to the terminal 64 | */ 65 | void paint(gpu::VideoBuffer &v_buffer); 66 | }; 67 | 68 | } // namespace video 69 | -------------------------------------------------------------------------------- /src/controller/include/controller/controller.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file controller.h 3 | * Declares the Controller class representing the game controller 4 | */ 5 | 6 | #include "controller/controller_interface.h" 7 | #include "controller/utils.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #pragma once 14 | 15 | namespace controller { 16 | 17 | /** 18 | * Class representing the game joypad and buttons 19 | */ 20 | class Controller : public ControllerInterface { 21 | private: 22 | /** 23 | * Array of flags representing the state of each button 24 | * 0 -> RIGHT 25 | * 1 -> LEFT 26 | * 2 -> UP 27 | * 3 -> DOWN 28 | * 4 -> A 29 | * 5 -> B 30 | * 6 -> SELECT 31 | * 7 -> START 32 | */ 33 | std::array buttons; 34 | 35 | /** 36 | * If the button flag is set, read from A, B, START, SELECT 37 | */ 38 | bool button_flag; 39 | 40 | /** 41 | * If the direction flag is set, read from UP, DOWN, LEFT, RIGHT 42 | */ 43 | bool direction_flag; 44 | 45 | /** 46 | * Get the index corresponding to the given button 47 | * 48 | * @param button 49 | * @return uint8_t 50 | */ 51 | int button_index(Button button); 52 | 53 | /** 54 | * @brief Set the button state for some button 55 | * 56 | * @param button Button to set 57 | * @param value Value to set 58 | */ 59 | void set_button(Button button, bool value); 60 | 61 | public: 62 | Controller(); 63 | 64 | /** 65 | * @see ControllerInterface#set_value 66 | */ 67 | void set_value(uint8_t value) override; 68 | 69 | /** 70 | * @see ControllerInterface#get_value 71 | */ 72 | uint8_t get_value() override; 73 | 74 | /** 75 | * @see ControllerInterface#press_button 76 | */ 77 | void press_button(Button button) override; 78 | 79 | /** 80 | * @see ControllerInterface#release_button 81 | */ 82 | void release_button(Button button) override; 83 | }; 84 | 85 | } // namespace controller 86 | -------------------------------------------------------------------------------- /src/cartridge/include/cartridge/cartridge.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file cartridge.h 3 | * Declares the Cartridge class 4 | */ 5 | 6 | #include "cartridge/cartridge_metadata.h" 7 | #include "memory/utils.h" 8 | 9 | #include "debugger/debugger.fwd.h" 10 | #include "instruction_parser.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #pragma once 19 | 20 | namespace cartridge { 21 | 22 | /** 23 | * Holds the game ROM data 24 | */ 25 | class Cartridge { 26 | private: 27 | /** 28 | * The Data stored in this cartridge 29 | */ 30 | std::vector data; 31 | 32 | /** 33 | * Holds game related metadata extracted from this cartridge 34 | */ 35 | std::unique_ptr metadata; 36 | 37 | public: 38 | Cartridge(std::string filepath); 39 | 40 | /** 41 | * Read a value from the given address in the cartridge 42 | * 43 | * @param address Address to read from 44 | * @return uint8_t 45 | */ 46 | uint8_t read(Address address); 47 | 48 | /** 49 | * Write data to the given address in the cartridge 50 | * 51 | * @param address Address to write to 52 | * @param data Byte to write 53 | */ 54 | void write(Address address, uint8_t data); 55 | 56 | /** 57 | * Peek a number of lines, starting from an address 58 | * @param start_addr Address to begin reading from 59 | * @param lines Number of lines to read 60 | * @return An ordered map of addresses, and parsed lines at those addresses 61 | */ 62 | map peek(Address start_addr, int lines); 63 | 64 | /** 65 | * Displays the cartridge metadata 66 | */ 67 | void display_metadata(); 68 | 69 | /** 70 | * Get a pointer to the CartridgeMetadata instance 71 | */ 72 | CartridgeMetadata *get_metadata(); 73 | 74 | /** 75 | * DebuggerCore may read private members of this class 76 | */ 77 | friend class debugger::DebuggerCore; 78 | }; 79 | 80 | } // namespace cartridge 81 | -------------------------------------------------------------------------------- /src/controller/src/controller.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file controller.cpp 3 | * Defines the Controller class 4 | */ 5 | 6 | #include "controller/controller.h" 7 | 8 | #include 9 | #include 10 | 11 | namespace controller { 12 | 13 | Controller::Controller() : buttons(std::array{}) {} 14 | 15 | int Controller::button_index(Button button) { 16 | // Return the corresponding index 17 | switch (button) { 18 | case Button::RIGHT: 19 | return 0; 20 | case Button::LEFT: 21 | return 1; 22 | case Button::UP: 23 | return 2; 24 | case Button::DOWN: 25 | return 3; 26 | case Button::A: 27 | return 4; 28 | case Button::B: 29 | return 5; 30 | case Button::SELECT: 31 | return 6; 32 | case Button::START: 33 | return 7; 34 | } 35 | 36 | return {}; 37 | } 38 | 39 | void Controller::set_value(uint8_t value) { 40 | // The only settable values are the 4th and 5th bits 41 | bool direction_bit = (1 << 4) & value; 42 | bool button_bit = (1 << 5) & value; 43 | 44 | // 0 is set, 1 is not set, so flip the bits 45 | direction_flag = !direction_bit; 46 | button_flag = !button_bit; 47 | } 48 | 49 | uint8_t Controller::get_value() { 50 | // The first 4 bits don't matter, and the next 4 bits are reversed 51 | std::bitset<8> byte(0x0F); 52 | 53 | // Assign flag values 54 | byte[4] = !direction_flag; 55 | byte[5] = !button_flag; 56 | 57 | // Read directions by default (0-3) 58 | // Read buttons if button_flag is set (4-7) 59 | uint8_t base_index = button_flag ? 4 : 0; 60 | 61 | // Assign requested inputs into the result byte 62 | for (int i = 0; i < 4; ++i) 63 | byte[i] = !buttons[(int)base_index + i]; 64 | 65 | return static_cast(byte.to_ulong()); 66 | } 67 | 68 | void Controller::set_button(Button button, bool value) { 69 | // This will be called by the input interface controller 70 | buttons[button_index(button)] = value; 71 | } 72 | 73 | void Controller::press_button(Button button) { set_button(button, true); } 74 | 75 | void Controller::release_button(Button button) { set_button(button, false); } 76 | 77 | } // namespace controller 78 | -------------------------------------------------------------------------------- /src/memory/include/memory/utils.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file utils.h 3 | * Declares helpers and utils for the Memory class 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | #pragma once 10 | 11 | /** 12 | * Type alias representing an address in memory 13 | */ 14 | using Address = uint16_t; 15 | 16 | /** 17 | * Boot ROM to load initial GameBoy BIOS 18 | */ 19 | const auto boot = std::array{ 20 | 0x31, 0xFE, 0xFF, 0xAF, 0x21, 0xFF, 0x9F, 0x32, 0xCB, 0x7C, 0x20, 0xFB, 21 | 0x21, 0x26, 0xFF, 0x0E, 0x11, 0x3E, 0x80, 0x32, 0xE2, 0x0C, 0x3E, 0xF3, 22 | 0xE2, 0x32, 0x3E, 0x77, 0x77, 0x3E, 0xFC, 0xE0, 0x47, 0x11, 0x04, 0x01, 23 | 0x21, 0x10, 0x80, 0x1A, 0xCD, 0x95, 0x00, 0xCD, 0x96, 0x00, 0x13, 0x7B, 24 | 0xFE, 0x34, 0x20, 0xF3, 0x11, 0xD8, 0x00, 0x06, 0x08, 0x1A, 0x13, 0x22, 25 | 0x23, 0x05, 0x20, 0xF9, 0x3E, 0x19, 0xEA, 0x10, 0x99, 0x21, 0x2F, 0x99, 26 | 0x0E, 0x0C, 0x3D, 0x28, 0x08, 0x32, 0x0D, 0x20, 0xF9, 0x2E, 0x0F, 0x18, 27 | 0xF3, 0x67, 0x3E, 0x64, 0x57, 0xE0, 0x42, 0x3E, 0x91, 0xE0, 0x40, 0x04, 28 | 0x1E, 0x02, 0x0E, 0x0C, 0xF0, 0x44, 0xFE, 0x90, 0x20, 0xFA, 0x0D, 0x20, 29 | 0xF7, 0x1D, 0x20, 0xF2, 0x0E, 0x13, 0x24, 0x7C, 0x1E, 0x83, 0xFE, 0x62, 30 | 0x28, 0x06, 0x1E, 0xC1, 0xFE, 0x64, 0x20, 0x06, 0x7B, 0xE2, 0x0C, 0x3E, 31 | 0x87, 0xE2, 0xF0, 0x42, 0x90, 0xE0, 0x42, 0x15, 0x20, 0xD2, 0x05, 0x20, 32 | 0x4F, 0x16, 0x20, 0x18, 0xCB, 0x4F, 0x06, 0x04, 0xC5, 0xCB, 0x11, 0x17, 33 | 0xC1, 0xCB, 0x11, 0x17, 0x05, 0x20, 0xF5, 0x22, 0x23, 0x22, 0x23, 0xC9, 34 | 0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B, 0x03, 0x73, 0x00, 0x83, 35 | 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E, 36 | 0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99, 0xBB, 0xBB, 0x67, 0x63, 37 | 0x6E, 0x0E, 0xEC, 0xCC, 0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E, 38 | 0x3C, 0x42, 0xB9, 0xA5, 0xB9, 0xA5, 0x42, 0x3C, 0x21, 0x04, 0x01, 0x11, 39 | 0xA8, 0x00, 0x1A, 0x13, 0xBE, 0x00, 0x00, 0x23, 0x7D, 0xFE, 0x34, 0x20, 40 | 0xF5, 0x06, 0x19, 0x78, 0x86, 0x23, 0x05, 0x20, 0xFB, 0x86, 0x00, 0x00, 41 | 0x3E, 0x01, 0xE0, 0x50}; 42 | -------------------------------------------------------------------------------- /tests/cpu/arithmetic_opcode_test.cpp: -------------------------------------------------------------------------------- 1 | #include "cpu/cpu.h" 2 | #include "cpu/mocks/register_mock.h" 3 | #include "memory/mocks/memory_mock.h" 4 | 5 | #include 6 | 7 | using namespace testing; 8 | using namespace cpu; 9 | using namespace memory; 10 | using namespace std; 11 | 12 | class CPUArithmeticOpcodeTest : public Test { 13 | protected: 14 | unique_ptr cpu; 15 | 16 | unique_ptr memory; 17 | 18 | unique_ptr a; 19 | unique_ptr b; 20 | unique_ptr c; 21 | unique_ptr d; 22 | unique_ptr e; 23 | unique_ptr f; 24 | unique_ptr h; 25 | unique_ptr l; 26 | 27 | unique_ptr af; 28 | unique_ptr bc; 29 | unique_ptr de; 30 | unique_ptr hl; 31 | 32 | unique_ptr sp; 33 | unique_ptr pc; 34 | 35 | CPUArithmeticOpcodeTest() { 36 | a = make_unique(); 37 | b = make_unique(); 38 | c = make_unique(); 39 | d = make_unique(); 40 | e = make_unique(); 41 | f = make_unique(); 42 | h = make_unique(); 43 | l = make_unique(); 44 | sp = make_unique(); 45 | pc = make_unique(); 46 | af = make_unique(); 47 | bc = make_unique(); 48 | de = make_unique(); 49 | hl = make_unique(); 50 | 51 | memory = make_unique(); 52 | 53 | cpu = make_unique(move(a), move(b), move(c), move(d), move(e), 54 | move(f), move(h), move(l), move(af), move(bc), 55 | move(de), move(hl), move(sp), move(pc), 56 | memory.get()); 57 | } 58 | }; 59 | 60 | // TEST_F(CPUArithmeticOpcodeTest, t) { 61 | // // This is abandoned for now, cause it's mindfuck to actually write 62 | // // Until I find a nicer way to write tests, I'm not touching this 63 | // } 64 | -------------------------------------------------------------------------------- /src/util/src/argv_generator.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file argv_generator.cpp 3 | * Defines the ArgvGenerator class 4 | */ 5 | 6 | #include "util/argv_generator.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | ArgvGenerator::ArgvGenerator(string command) { 13 | std::vector argv_vec; 14 | 15 | // Split command by spaces 16 | stringstream string_splitter(command); 17 | string temp_string; 18 | 19 | while (string_splitter >> temp_string) { 20 | argv_vec.push_back(temp_string); 21 | } 22 | 23 | // Argc = Number of strings in split command 24 | this->argc = argv_vec.size(); 25 | 26 | // Convert each string into a null-terminated char* 27 | this->argv = new char *[this->argc]; 28 | for (int i = 0; i < argv_vec.size(); ++i) { 29 | const string &arg_string = argv_vec[i]; 30 | 31 | // Generate char* from string 32 | char *arg = new char[arg_string.size() + 1]; 33 | copy(arg_string.begin(), arg_string.end(), arg); 34 | arg[arg_string.length()] = '\0'; 35 | 36 | // Set ptr in argv 37 | this->argv[i] = arg; 38 | } 39 | } 40 | 41 | tuple ArgvGenerator::get() { return {argc, argv}; } 42 | 43 | ArgvGenerator::~ArgvGenerator() { 44 | // Delete each string in argv array 45 | while (--argc != 0) { 46 | delete[] argv[argc]; 47 | } 48 | 49 | // Delete main array 50 | delete[] argv; 51 | } 52 | 53 | ArgvGenerator::ArgvGenerator(const ArgvGenerator &other) : argc(other.argc) { 54 | // Allocate mem and copy argv 55 | argv = new char *[argc]; 56 | for (int i = 0; i < argc; ++i) { 57 | char *arg = new char[strlen(other.argv[i])]; 58 | strcpy(arg, other.argv[i]); 59 | 60 | argv[i] = arg; 61 | } 62 | } 63 | 64 | ArgvGenerator &ArgvGenerator::operator=(const ArgvGenerator &other) { 65 | // Ensure this is not a self-assignment 66 | if (&other == this) { 67 | // Clean up the existing contents 68 | while (--argc != 0) { 69 | delete[] argv[argc]; 70 | } 71 | 72 | delete[] argv; 73 | argv = nullptr; 74 | 75 | // Copy new contents 76 | argv = new char *[argc]; 77 | for (int i = 0; i < argc; ++i) { 78 | char *arg = new char[strlen(other.argv[i])]; 79 | strcpy(arg, other.argv[i]); 80 | 81 | argv[i] = arg; 82 | } 83 | } 84 | 85 | return *this; 86 | } 87 | -------------------------------------------------------------------------------- /src/main/main.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file main.cpp 3 | * Main entrypoint for the tvp executable 4 | */ 5 | 6 | #include "debugger/cli_debugger.h" 7 | #include "debugger/debugger_core.h" 8 | #include "gameboy/gameboy.h" 9 | 10 | #include 11 | 12 | using namespace std; 13 | using namespace cpu; 14 | using namespace gpu; 15 | using namespace video; 16 | using namespace memory; 17 | using namespace gameboy; 18 | using namespace debugger; 19 | using namespace cartridge; 20 | using namespace controller; 21 | 22 | int main(int argc, char *argv[]) { 23 | ios_base::sync_with_stdio(false); 24 | Log::Disable(); 25 | 26 | auto cmdline_args_parser = 27 | cxxopts::Options("tvp", "Welcome to tvp - The GameBoy Emulator!"); 28 | 29 | // clang-format off 30 | // TODO: ClangFormat for this style is bad, fix rules 31 | cmdline_args_parser.add_options() 32 | ("r,rom", "Path to a GameBoy ROM file - REQUIRED", 33 | cxxopts::value()) 34 | ("d,debug", "Enable the debugger", 35 | cxxopts::value()->default_value("false")) 36 | ("h,help", "Print this information"); 37 | // clang-format on 38 | 39 | auto parsed_args = cmdline_args_parser.parse(argc, argv); 40 | 41 | // Print help info if -h is passed 42 | auto help = parsed_args["help"].as(); 43 | if (help) { 44 | cout << cmdline_args_parser.help(); 45 | exit(0); 46 | } 47 | 48 | // Get ROM path and fail with help message if arg not parsed 49 | string rom_path = ""; 50 | try { 51 | rom_path = parsed_args["rom"].as(); 52 | } catch (exception &e) { 53 | cout << cmdline_args_parser.help(); 54 | exit(1); 55 | } 56 | 57 | // Create main gameboy instance 58 | auto gameboy = make_unique(rom_path); 59 | 60 | // Turn on debugging if needed 61 | auto debugger_on = parsed_args["debug"].as(); 62 | if (not debugger_on) { 63 | // Start GameBoy normally 64 | for (auto i = 0; /*Infinite Loop*/; i++) { 65 | gameboy->tick(); 66 | } 67 | } else { 68 | // Start GameBoy with DebuggerCore 69 | auto debugger_core = std::make_unique(std::move(gameboy)); 70 | auto cli_debugger = 71 | std::make_unique(std::move(debugger_core)); 72 | Log::info("tvp DebuggerCore Started"); 73 | for (auto i = 0; /*Infinite Loop*/; i++) { 74 | cli_debugger->tick(); 75 | } 76 | } 77 | 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /src/util/src/log.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file log.cpp 3 | * Defines the logger functions for program-wide logging 4 | */ 5 | 6 | #include "util/log.h" 7 | 8 | #include 9 | 10 | using namespace std; 11 | 12 | void Log::Enable() { enabled = true; } 13 | 14 | void Log::Disable() { enabled = false; } 15 | 16 | #if defined(_WIN32) || defined(WIN32) 17 | 18 | void Log::log(string message, LogLevel log_level) { 19 | if (!enabled) 20 | return; 21 | 22 | switch (log_level) { 23 | case LogLevel::VERBOSE: 24 | cout << " [VERB] "; 25 | break; 26 | 27 | case LogLevel::INFO: 28 | cout << " [INFO] "; 29 | break; 30 | 31 | case LogLevel::WARN: 32 | cout << " [WARN] "; 33 | break; 34 | 35 | case LogLevel::ERROR: 36 | cout << " [ERR!] "; 37 | break; 38 | 39 | case LogLevel::FATAL: 40 | cout << " [DEAD] "; 41 | break; 42 | } 43 | 44 | cout << message << endl; 45 | 46 | if (log_level == LogLevel::FATAL) 47 | exit(1); 48 | } 49 | 50 | #else 51 | 52 | // Define ANSI color codes 53 | namespace color { 54 | const auto GRAY = "\033[1;37m"; 55 | const auto BLUE = "\033[1;34m"; 56 | const auto YELLOW = "\033[1;33m"; 57 | const auto RED = "\033[1;31m"; 58 | const auto PURPLE = "\033[1;35m"; 59 | 60 | const auto END = "\033[0m"; 61 | }; // namespace color 62 | 63 | void Log::log(string message, LogLevel log_level) { 64 | if (!enabled) 65 | return; 66 | 67 | switch (log_level) { 68 | case LogLevel::VERBOSE: 69 | cout << color::GRAY << " [VERB] " << color::END; 70 | break; 71 | 72 | case LogLevel::INFO: 73 | cout << color::BLUE << " [INFO] " << color::END; 74 | break; 75 | 76 | case LogLevel::WARN: 77 | cout << color::YELLOW << " [WARN] " << color::END; 78 | break; 79 | 80 | case LogLevel::ERROR: 81 | cout << color::RED << " [ERR!] " << color::END; 82 | break; 83 | 84 | case LogLevel::FATAL: 85 | cout << color::PURPLE << " [DEAD] " << color::END; 86 | break; 87 | } 88 | 89 | cout << message << endl; 90 | 91 | if (log_level == LogLevel::FATAL) 92 | exit(1); 93 | } 94 | 95 | #endif 96 | 97 | void Log::verbose(string message) { log(message, LogLevel::VERBOSE); } 98 | 99 | void Log::info(string message) { log(message, LogLevel::INFO); } 100 | 101 | void Log::warn(string message) { log(message, LogLevel::WARN); } 102 | 103 | void Log::error(string message) { log(message, LogLevel::ERROR); } 104 | 105 | void Log::fatal(string message) { log(message, LogLevel::FATAL); } 106 | -------------------------------------------------------------------------------- /src/gameboy/include/gameboy/gameboy.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file gameboy.h 3 | * Declares the Gameboy class 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "controller/controller.h" 9 | #include "cpu/cpu.h" 10 | #include "cpu/register/register.h" 11 | #include "gpu/gpu.h" 12 | #include "gpu/utils.h" 13 | #include "memory/memory.h" 14 | #include "util/helpers.h" 15 | #include "util/log.h" 16 | #include "video/video.h" 17 | 18 | #include "debugger/debugger.fwd.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | using namespace std; 28 | using namespace cpu; 29 | using namespace gpu; 30 | using namespace video; 31 | using namespace memory; 32 | using namespace cartridge; 33 | using namespace controller; 34 | 35 | namespace gameboy { 36 | 37 | /** 38 | * Gameboy class that initializes and contains the complete application 39 | */ 40 | class Gameboy { 41 | public: 42 | /** 43 | * Cartridge instance 44 | */ 45 | std::unique_ptr cartridge; 46 | 47 | /** 48 | * Controller instance 49 | */ 50 | std::unique_ptr controller; 51 | 52 | /** 53 | * Video instance 54 | */ 55 | std::unique_ptr