From e6df451eca342fbc4f1d431f7ce7e50ae0ecd2a9 Mon Sep 17 00:00:00 2001 From: Tony Di Nucci Date: Wed, 27 Mar 2019 00:14:44 +0000 Subject: [PATCH] initial --- .gitignore | 2 + CMakeLists.txt | 33 +++ CMakeLists.txt.in | 15 ++ src/instruction/instruction-executor.cpp | 38 +++ src/instruction/instruction-executor.h | 27 +++ src/machine.cpp | 24 ++ src/machine.h | 31 +++ src/main.cpp | 42 ++++ src/memory.cpp | 14 ++ src/memory.h | 21 ++ src/opcode/handler/flag-opcode-handler.cpp | 48 ++++ src/opcode/handler/flag-opcode-handler.h | 31 +++ src/opcode/handler/ldx-opcode-handler.cpp | 30 +++ src/opcode/handler/ldx-opcode-handler.h | 33 +++ .../handler/register-opcode-handler.cpp | 54 +++++ src/opcode/handler/register-opcode-handler.h | 34 +++ src/opcode/opcode-handler-directory.cpp | 31 +++ src/opcode/opcode-handler-directory.h | 24 ++ src/opcode/opcode-handler.cpp | 12 + src/opcode/opcode-handler.h | 28 +++ src/program.cpp | 19 ++ src/program.h | 21 ++ src/register/register-manager.cpp | 39 ++++ src/register/register-manager.h | 36 +++ src/register/register.cpp | 30 +++ src/register/register.h | 28 +++ src/register/status-register.cpp | 65 ++++++ src/register/status-register.h | 29 +++ test/flags-opcode-handler-test.cpp | 112 +++++++++ test/instruction-executor-test.cpp | 29 +++ test/machine-test.cpp | 27 +++ test/register-opcode-handler-test.cpp | 219 ++++++++++++++++++ test/register-test.cpp | 8 + test/status-register-test.cpp | 170 ++++++++++++++ 34 files changed, 1404 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 CMakeLists.txt.in create mode 100644 src/instruction/instruction-executor.cpp create mode 100644 src/instruction/instruction-executor.h create mode 100644 src/machine.cpp create mode 100644 src/machine.h create mode 100644 src/main.cpp create mode 100644 src/memory.cpp create mode 100644 src/memory.h create mode 100644 src/opcode/handler/flag-opcode-handler.cpp create mode 100644 src/opcode/handler/flag-opcode-handler.h create mode 100644 src/opcode/handler/ldx-opcode-handler.cpp create mode 100644 src/opcode/handler/ldx-opcode-handler.h create mode 100644 src/opcode/handler/register-opcode-handler.cpp create mode 100644 src/opcode/handler/register-opcode-handler.h create mode 100644 src/opcode/opcode-handler-directory.cpp create mode 100644 src/opcode/opcode-handler-directory.h create mode 100644 src/opcode/opcode-handler.cpp create mode 100644 src/opcode/opcode-handler.h create mode 100644 src/program.cpp create mode 100644 src/program.h create mode 100644 src/register/register-manager.cpp create mode 100644 src/register/register-manager.h create mode 100644 src/register/register.cpp create mode 100644 src/register/register.h create mode 100644 src/register/status-register.cpp create mode 100644 src/register/status-register.h create mode 100644 test/flags-opcode-handler-test.cpp create mode 100644 test/instruction-executor-test.cpp create mode 100644 test/machine-test.cpp create mode 100644 test/register-opcode-handler-test.cpp create mode 100644 test/register-test.cpp create mode 100644 test/status-register-test.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..990936c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +cmake-build-debug/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..af5f4e5 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.13) +project(6502_emulator) + +set(CMAKE_CXX_STANDARD 14) + +configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt) +execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download ) +if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") +endif() +execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download ) +if(result) + message(FATAL_ERROR "Build step for googletest failed: ${result}") +endif() + +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + +add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src + ${CMAKE_CURRENT_BINARY_DIR}/googletest-build + EXCLUDE_FROM_ALL) + +file(GLOB_RECURSE test_src "test/**.h" "test/**.cpp" "src/**.h" "src/**.cpp") +list(FILTER test_src EXCLUDE REGEX ".*/6502-emulator/src/main.cpp$") +add_executable(6502_emulator_test ${test_src} test/machine-test.cpp test/flags-opcode-handler-test.cpp) +target_link_libraries(6502_emulator_test gtest_main) +add_test(NAME run_tests COMMAND 6502_emulator_test) + +file(GLOB_RECURSE emulator_src "src/**.h" "src/**.cpp") +add_executable(6502_emulator ${emulator_src}) diff --git a/CMakeLists.txt.in b/CMakeLists.txt.in new file mode 100644 index 0000000..98ce4c3 --- /dev/null +++ b/CMakeLists.txt.in @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.13) + +project(googletest-download NONE) + +include(ExternalProject) +ExternalProject_Add(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG master + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" + ) \ No newline at end of file diff --git a/src/instruction/instruction-executor.cpp b/src/instruction/instruction-executor.cpp new file mode 100644 index 0000000..d3abc41 --- /dev/null +++ b/src/instruction/instruction-executor.cpp @@ -0,0 +1,38 @@ +#include "instruction-executor.h" +#include + +#include + +void dump_cpu_state(shared_ptr reg_man) { + cout << "--------------" << endl; + cout << "A=0x" << hex << (int) reg_man->get_accumulator()->get_value() << + " X=0x" << hex << (int) reg_man->get_x_index()->get_value() << + " Y=0x" << hex << (int) reg_man->get_y_index()->get_value() << endl; + + cout << "SP=0x" << hex << (int) reg_man->get_stack_pointer()->get_value() << + " PC=0x" << hex << (int) reg_man->get_program_counter()->get_value() << endl; + + cout << "NV-BDIZC" << endl << + reg_man->get_status_register()->get_value() << endl; +} + +InstructionExecutor::InstructionExecutor(shared_ptr program, shared_ptr reg_man, + shared_ptr opcode_handler_dir) { + this->program = program; + this->reg_man = reg_man; + this->opcode_handler_dir = opcode_handler_dir; +} + +void InstructionExecutor::execute() { + auto pc = reg_man->get_program_counter(); + + do { + auto opcode = program->get_byte_at(pc->get_value()); + auto handler = opcode_handler_dir->get_handler(opcode); + + handler->execute(); + + dump_cpu_state(reg_man); + + } while (pc->get_value() < program->get_size()); +} \ No newline at end of file diff --git a/src/instruction/instruction-executor.h b/src/instruction/instruction-executor.h new file mode 100644 index 0000000..f06e9cf --- /dev/null +++ b/src/instruction/instruction-executor.h @@ -0,0 +1,27 @@ +#ifndef INC_6502_EMULATOR_INSTRUCTION_EXECUTOR_H +#define INC_6502_EMULATOR_INSTRUCTION_EXECUTOR_H + +#include "../program.h" +#include "../register/register-manager.h" +#include "../opcode/opcode-handler-directory.h" + +#include +#include + +using namespace std; + +class InstructionExecutor { +public: + explicit InstructionExecutor(shared_ptr program, shared_ptr reg_man, + shared_ptr opcode_handler_dir); + + void execute(); + +private: + shared_ptr program; + shared_ptr reg_man; + shared_ptr opcode_handler_dir; +}; + + +#endif //INC_6502_EMULATOR_INSTRUCTION_EXECUTOR_H diff --git a/src/machine.cpp b/src/machine.cpp new file mode 100644 index 0000000..4e63c9a --- /dev/null +++ b/src/machine.cpp @@ -0,0 +1,24 @@ +#include "machine.h" + +#include + +Machine::Machine(shared_ptr program) { + this->program = program; + memory = make_shared(Memory()); + reg_man = make_shared(RegisterManager()); + opcode_handler_dir = make_shared(OpcodeHandlerDirectory(program, reg_man)); + instruction_executor = + make_unique(InstructionExecutor(program, reg_man, opcode_handler_dir)); +} + +shared_ptr Machine::get_memory() { + return memory; +} + +shared_ptr Machine::get_reg_man() { + return reg_man; +} + +void Machine::execute() { + instruction_executor->execute(); +} \ No newline at end of file diff --git a/src/machine.h b/src/machine.h new file mode 100644 index 0000000..4639331 --- /dev/null +++ b/src/machine.h @@ -0,0 +1,31 @@ +#ifndef INC_6502_EMULATOR_MACHINE_H +#define INC_6502_EMULATOR_MACHINE_H + +#include "program.h" +#include "memory.h" +#include "register/register-manager.h" +#include "opcode/opcode-handler-directory.h" +#include "instruction/instruction-executor.h" +#include + +using namespace std; + +class Machine { +public: + explicit Machine(shared_ptr program); + + shared_ptr get_reg_man(); + shared_ptr get_memory(); + + void execute(); + +private: + shared_ptr program; + shared_ptr memory; + shared_ptr reg_man; + shared_ptr opcode_handler_dir; + shared_ptr instruction_executor; +}; + + +#endif //INC_6502_EMULATOR_MACHINE_H diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..4c9421d --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,42 @@ +#include "register/register-manager.h" + +#include +#include + +using namespace std; + +void stack_test(string astring) { + astring.push_back('x'); +} + +void heap_test(shared_ptr astring){ + astring->push_back('y'); +} + +int main() { + auto reg = unique_ptr>(new Register("PC")); + + cout << "Register: " << reg->get_name() << " " << to_string(56) << endl; + + string astr = "hello"; + shared_ptr bstr = make_shared("there"); + + cout << astr << " - " << bstr->c_str() << endl; + + stack_test(astr); + heap_test(bstr); + + cout << astr << " - " << bstr->c_str() << endl; + + auto reg_man = new RegisterManager(); + auto accum = reg_man->get_accumulator(); + + cout << "****** " << accum->get_name() << " ******" << endl; + cout << "****** " << reg_man->get_x_index()->get_name() << " ******" << endl; + cout << "****** " << reg_man->get_y_index()->get_name() << " ******" << endl; + cout << "****** " << reg_man->get_stack_pointer()->get_name() << " ******" << endl; + cout << "****** " << reg_man->get_status_register()->get_name() << " ******" << endl; + cout << "****** " << reg_man->get_program_counter()->get_name() << " ******" << endl; + + return 0; +} \ No newline at end of file diff --git a/src/memory.cpp b/src/memory.cpp new file mode 100644 index 0000000..24b6476 --- /dev/null +++ b/src/memory.cpp @@ -0,0 +1,14 @@ +#include "memory.h" +#include + +Memory::Memory() { + bytes = make_shared>(vector((1024 * 64) - 1)); +} + +uint8_t Memory::get_byte_at(uint16_t index) { + auto size = bytes->size(); + if (size == 0 || bytes->size() - 1 < index) + throw runtime_error("Attempted to read past end of memory"); + + return bytes->at(index); +} \ No newline at end of file diff --git a/src/memory.h b/src/memory.h new file mode 100644 index 0000000..d824a42 --- /dev/null +++ b/src/memory.h @@ -0,0 +1,21 @@ +#ifndef INC_6502_EMULATOR_MEMORY_H +#define INC_6502_EMULATOR_MEMORY_H + + +#include +#include + +using namespace std; + +class Memory { +public: + explicit Memory(); + + uint8_t get_byte_at(uint16_t index); + +private: + shared_ptr> bytes; +}; + + +#endif //INC_6502_EMULATOR_MEMORY_H diff --git a/src/opcode/handler/flag-opcode-handler.cpp b/src/opcode/handler/flag-opcode-handler.cpp new file mode 100644 index 0000000..f1c7c10 --- /dev/null +++ b/src/opcode/handler/flag-opcode-handler.cpp @@ -0,0 +1,48 @@ +#include "flag-opcode-handler.h" + +#include +#include + +const uint8_t FlagOpcodeHandler::CLC; +const uint8_t FlagOpcodeHandler::SEC; +const uint8_t FlagOpcodeHandler::CLI; +const uint8_t FlagOpcodeHandler::SEI; +const uint8_t FlagOpcodeHandler::CLV; +const uint8_t FlagOpcodeHandler::CLD; +const uint8_t FlagOpcodeHandler::SED; + +void FlagOpcodeHandler::execute() { + auto instructionIndex = reg_man->get_program_counter()->get_value(); + auto opcode = program->get_byte_at(instructionIndex); + auto statusReg = reg_man->get_status_register(); + + switch (opcode) { + case CLC: + statusReg->set_carry(false); + break; + case SEC: + statusReg->set_carry(true); + break; + case CLI: + statusReg->set_interupt_disable(false); + break; + case SEI: + statusReg->set_interupt_disable(true); + break; + case CLV: + statusReg->set_overflow(false); + break; + case CLD: + statusReg->set_decimal(false); + break; + case SED: + statusReg->set_decimal(true); + break; + default: + stringstream stream; + stream << "Unexpected opcode 0x" << hex << opcode; + throw runtime_error(stream.str()); + } + + move_program_counter(1); +} \ No newline at end of file diff --git a/src/opcode/handler/flag-opcode-handler.h b/src/opcode/handler/flag-opcode-handler.h new file mode 100644 index 0000000..5241358 --- /dev/null +++ b/src/opcode/handler/flag-opcode-handler.h @@ -0,0 +1,31 @@ +#ifndef INC_6502_EMULATOR_FLAG_OPCODE_HANDLER_H +#define INC_6502_EMULATOR_FLAG_OPCODE_HANDLER_H + +#include "../../program.h" +#include "../opcode-handler.h" + +class FlagOpcodeHandler : public OpcodeHandler { +public: + static const uint8_t CLC = 0x18; + static const uint8_t SEC = 0x38; + static const uint8_t CLI = 0x58; + static const uint8_t SEI = 0x78; + static const uint8_t CLV = 0xB8; + static const uint8_t CLD = 0xD8; + static const uint8_t SED = 0xF8; + + explicit FlagOpcodeHandler(shared_ptr program, shared_ptr reg_man) : + OpcodeHandler(program, reg_man) { + handled_opcodes->push_back(CLC); + handled_opcodes->push_back(SEC); + handled_opcodes->push_back(CLI); + handled_opcodes->push_back(SEI); + handled_opcodes->push_back(CLV); + handled_opcodes->push_back(CLD); + handled_opcodes->push_back(SED); + } + + virtual void execute() override; +}; + +#endif //INC_6502_EMULATOR_FLAG_OPCODE_HANDLER_H diff --git a/src/opcode/handler/ldx-opcode-handler.cpp b/src/opcode/handler/ldx-opcode-handler.cpp new file mode 100644 index 0000000..109da32 --- /dev/null +++ b/src/opcode/handler/ldx-opcode-handler.cpp @@ -0,0 +1,30 @@ +#include "ldx-opcode-handler.h" +#include + +const uint8_t LdxOpcodeHandler::IMMEDIATE; +const uint8_t LdxOpcodeHandler::ZERO_PAGE; +const uint8_t LdxOpcodeHandler::ZERO_PAGE_X; +const uint8_t LdxOpcodeHandler::ABSOLUTE; +const uint8_t LdxOpcodeHandler::ABSOLUTE_X; +const uint8_t LdxOpcodeHandler::ABSOLUTE_Y; +const uint8_t LdxOpcodeHandler::INDIRECT_X; +const uint8_t LdxOpcodeHandler::INDIRECT_Y; + +void LdxOpcodeHandler::execute() { + auto instructionIndex = reg_man->get_program_counter()->get_value(); + auto opcode = program->get_byte_at(instructionIndex); + auto byte2 = program->get_byte_at(instructionIndex + 1); + auto accumulator = reg_man->get_accumulator(); + + switch (opcode) { + case IMMEDIATE: + accumulator->set_value(byte2); + move_program_counter(2); + break; + + default: + stringstream stream; + stream << "Unexpected opcode 0x" << hex << opcode; + throw runtime_error(stream.str()); + } +} \ No newline at end of file diff --git a/src/opcode/handler/ldx-opcode-handler.h b/src/opcode/handler/ldx-opcode-handler.h new file mode 100644 index 0000000..dff22e1 --- /dev/null +++ b/src/opcode/handler/ldx-opcode-handler.h @@ -0,0 +1,33 @@ +#ifndef INC_6502_EMULATOR_LDX_OPCODE_HANDLER_H +#define INC_6502_EMULATOR_LDX_OPCODE_HANDLER_H + +#include "../../program.h" +#include "../opcode-handler.h" + +class LdxOpcodeHandler : public OpcodeHandler { +public: + static const uint8_t IMMEDIATE = 0xA9; + static const uint8_t ZERO_PAGE = 0xA5; + static const uint8_t ZERO_PAGE_X = 0xB5; + static const uint8_t ABSOLUTE = 0xAD; + static const uint8_t ABSOLUTE_X = 0xBD; + static const uint8_t ABSOLUTE_Y = 0xB9; + static const uint8_t INDIRECT_X = 0xA1; + static const uint8_t INDIRECT_Y = 0xB1; + + explicit LdxOpcodeHandler(shared_ptr program, shared_ptr reg_man) : + OpcodeHandler(program, reg_man) { + handled_opcodes->push_back(IMMEDIATE); + handled_opcodes->push_back(ZERO_PAGE); + handled_opcodes->push_back(ZERO_PAGE_X); + handled_opcodes->push_back(ABSOLUTE); + handled_opcodes->push_back(ABSOLUTE_X); + handled_opcodes->push_back(ABSOLUTE_Y); + handled_opcodes->push_back(INDIRECT_X); + handled_opcodes->push_back(INDIRECT_Y); + } + + virtual void execute() override; +}; + +#endif //INC_6502_EMULATOR_LDX_OPCODE_HANDLER_H diff --git a/src/opcode/handler/register-opcode-handler.cpp b/src/opcode/handler/register-opcode-handler.cpp new file mode 100644 index 0000000..0b8f61b --- /dev/null +++ b/src/opcode/handler/register-opcode-handler.cpp @@ -0,0 +1,54 @@ +#include "register-opcode-handler.h" + +#include +#include + +const uint8_t RegisterOpcodeHandler::TAX; +const uint8_t RegisterOpcodeHandler::TXA; +const uint8_t RegisterOpcodeHandler::DEX; +const uint8_t RegisterOpcodeHandler::INX; +const uint8_t RegisterOpcodeHandler::TAY; +const uint8_t RegisterOpcodeHandler::TYA; +const uint8_t RegisterOpcodeHandler::DEY; +const uint8_t RegisterOpcodeHandler::INY; + +void RegisterOpcodeHandler::execute() { + auto instructionIndex = reg_man->get_program_counter()->get_value(); + auto opcode = program->get_byte_at(instructionIndex); + auto accumulator = reg_man->get_accumulator(); + auto x_index = reg_man->get_x_index(); + auto y_index = reg_man->get_y_index(); + + switch (opcode) { + case TAX: + x_index->set_value(accumulator->get_value()); + break; + case TXA: + accumulator->set_value(x_index->get_value()); + break; + case DEX: + x_index->set_value(x_index->get_value() - 1); + break; + case INX: + x_index->set_value(x_index->get_value() + 1); + break; + case TAY: + y_index->set_value(accumulator->get_value()); + break; + case TYA: + accumulator->set_value(y_index->get_value()); + break; + case DEY: + y_index->set_value(y_index->get_value() - 1); + break; + case INY: + y_index->set_value(y_index->get_value() + 1); + break; + default: + stringstream stream; + stream << "Unexpected opcode 0x" << hex << opcode; + throw runtime_error(stream.str()); + } + + move_program_counter(1); +} diff --git a/src/opcode/handler/register-opcode-handler.h b/src/opcode/handler/register-opcode-handler.h new file mode 100644 index 0000000..2d1892a --- /dev/null +++ b/src/opcode/handler/register-opcode-handler.h @@ -0,0 +1,34 @@ +#ifndef INC_6502_EMULATOR_REGISTER_OPCODE_HANDLER_H +#define INC_6502_EMULATOR_REGISTER_OPCODE_HANDLER_H + +#include "../../program.h" +#include "../opcode-handler.h" + +class RegisterOpcodeHandler : public OpcodeHandler { +public: + static const uint8_t TAX = 0xAA; + static const uint8_t TXA = 0x8A; + static const uint8_t DEX = 0xCA; + static const uint8_t INX = 0xE8; + static const uint8_t TAY = 0xA8; + static const uint8_t TYA = 0x98; + static const uint8_t DEY = 0x88; + static const uint8_t INY = 0xC8; + + explicit RegisterOpcodeHandler(shared_ptr program, shared_ptr reg_man) : + OpcodeHandler(program, reg_man) { + handled_opcodes->push_back(TAX); + handled_opcodes->push_back(TXA); + handled_opcodes->push_back(DEX); + handled_opcodes->push_back(INX); + handled_opcodes->push_back(TAY); + handled_opcodes->push_back(TYA); + handled_opcodes->push_back(DEY); + handled_opcodes->push_back(INY); + } + + virtual void execute() override; +}; + + +#endif //INC_6502_EMULATOR_REGISTER_OPCODE_HANDLER_H diff --git a/src/opcode/opcode-handler-directory.cpp b/src/opcode/opcode-handler-directory.cpp new file mode 100644 index 0000000..4d41717 --- /dev/null +++ b/src/opcode/opcode-handler-directory.cpp @@ -0,0 +1,31 @@ +#include "opcode-handler-directory.h" +#include "handler/flag-opcode-handler.h" +#include "handler/register-opcode-handler.h" +#include "handler/ldx-opcode-handler.h" + +#include +#include + +OpcodeHandlerDirectory::OpcodeHandlerDirectory(shared_ptr program, shared_ptr reg_man) { + handlers = make_unique>>(); + + register_handler(make_shared(program, reg_man)); + register_handler(make_shared(program, reg_man)); + register_handler(make_shared(program, reg_man)); +} + +void OpcodeHandlerDirectory::register_handler(shared_ptr handler) { + for (auto opcode: *handler->get_handled_opcodes()) { + handlers->insert({opcode, handler}); + } +} + +shared_ptr OpcodeHandlerDirectory::get_handler(uint8_t opcode) { + if (handlers->find(opcode) == handlers->end()) { + stringstream stream; + stream << "No handler registered for opcode 0x" << hex << opcode; + throw runtime_error(stream.str()); + } + + return handlers->at(opcode); +} \ No newline at end of file diff --git a/src/opcode/opcode-handler-directory.h b/src/opcode/opcode-handler-directory.h new file mode 100644 index 0000000..df2cf36 --- /dev/null +++ b/src/opcode/opcode-handler-directory.h @@ -0,0 +1,24 @@ +#ifndef INC_6502_EMULATOR_OPCODE_HANDLER_DIRECTORY_H +#define INC_6502_EMULATOR_OPCODE_HANDLER_DIRECTORY_H + +#include "../program.h" +#include "opcode-handler.h" +#include +#include +#include + +using namespace std; + +class OpcodeHandlerDirectory { +public: + explicit OpcodeHandlerDirectory(shared_ptr program, shared_ptr reg_man); + + shared_ptr get_handler(uint8_t opcode); +private: + unique_ptr>> handlers; + + void register_handler(shared_ptr handler); +}; + + +#endif //INC_6502_EMULATOR_OPCODE_HANDLER_DIRECTORY_H diff --git a/src/opcode/opcode-handler.cpp b/src/opcode/opcode-handler.cpp new file mode 100644 index 0000000..3d80203 --- /dev/null +++ b/src/opcode/opcode-handler.cpp @@ -0,0 +1,12 @@ +#include "../program.h" +#include "opcode-handler.h" + +OpcodeHandler::OpcodeHandler(shared_ptr program, shared_ptr reg_man) { + this->program = program; + this->reg_man = reg_man; +} + +void OpcodeHandler::move_program_counter(uint8_t forward_by) { + auto pc = reg_man->get_program_counter(); + pc->set_value(pc->get_value() + forward_by); +} \ No newline at end of file diff --git a/src/opcode/opcode-handler.h b/src/opcode/opcode-handler.h new file mode 100644 index 0000000..b7f7e88 --- /dev/null +++ b/src/opcode/opcode-handler.h @@ -0,0 +1,28 @@ +#ifndef INC_6502_EMULATOR_OPCODE_HANDLER_H +#define INC_6502_EMULATOR_OPCODE_HANDLER_H + +#include "../program.h" +#include "../register/register-manager.h" +#include +#include + +using namespace std; + +class OpcodeHandler { +public: + OpcodeHandler(shared_ptr program, shared_ptr reg_man); + + shared_ptr> get_handled_opcodes() { return handled_opcodes; } + + virtual void execute() = 0; + +protected: + shared_ptr> handled_opcodes = make_shared>(); + shared_ptr program; + shared_ptr reg_man; + + void move_program_counter(uint8_t forward_by); +}; + + +#endif //INC_6502_EMULATOR_OPCODE_HANDLER_H diff --git a/src/program.cpp b/src/program.cpp new file mode 100644 index 0000000..d97eced --- /dev/null +++ b/src/program.cpp @@ -0,0 +1,19 @@ +#include "program.h" + +#include + +Program::Program(shared_ptr> bytes) { + this->bytes = bytes; +} + +uint16_t Program::get_size() { + return bytes->size(); +} + +uint8_t Program::get_byte_at(uint16_t index) { + auto size = bytes->size(); + if (size == 0 || bytes->size() - 1 < index) + throw runtime_error("Attempted to read past end of program"); + + return bytes->at(index); +} \ No newline at end of file diff --git a/src/program.h b/src/program.h new file mode 100644 index 0000000..95ac59d --- /dev/null +++ b/src/program.h @@ -0,0 +1,21 @@ +#ifndef INC_6502_EMULATOR_PROGRAM_H +#define INC_6502_EMULATOR_PROGRAM_H + +#include +#include + +using namespace std; + +class Program { +public: + explicit Program(shared_ptr> bytes); + + uint8_t get_byte_at(uint16_t index); + uint16_t get_size(); + +private: + shared_ptr> bytes; +}; + + +#endif //INC_6502_EMULATOR_PROGRAM_H diff --git a/src/register/register-manager.cpp b/src/register/register-manager.cpp new file mode 100644 index 0000000..9ad3b23 --- /dev/null +++ b/src/register/register-manager.cpp @@ -0,0 +1,39 @@ +#include "register-manager.h" +#include "register.h" +#include +#include + +using namespace std; + +RegisterManager::RegisterManager() { + accumulator = std::make_shared>(Register("A")); + x_index = make_shared>(Register("X")); + y_index = make_shared>(Register("Y")); + stack_pointer = make_shared>(Register("SP")); + status_register = make_shared(StatusRegister()); + program_counter = make_shared>(Register("PC")); +} + +shared_ptr> RegisterManager::get_accumulator() { + return accumulator; +} + +shared_ptr> RegisterManager::get_x_index() { + return x_index; +} + +shared_ptr> RegisterManager::get_y_index() { + return y_index; +} + +shared_ptr> RegisterManager::get_stack_pointer() { + return stack_pointer; +} + +shared_ptr RegisterManager::get_status_register() { + return status_register; +} + +shared_ptr> RegisterManager::get_program_counter() { + return program_counter; +} \ No newline at end of file diff --git a/src/register/register-manager.h b/src/register/register-manager.h new file mode 100644 index 0000000..288c962 --- /dev/null +++ b/src/register/register-manager.h @@ -0,0 +1,36 @@ +#ifndef INC_6502_EMULATOR_REGISTER_MANAGER_H +#define INC_6502_EMULATOR_REGISTER_MANAGER_H + +#include "register.h" +#include "status-register.h" +#include + +using namespace std; + +class RegisterManager { +public: + explicit RegisterManager(); + + shared_ptr> get_accumulator(); + + shared_ptr> get_x_index(); + + shared_ptr> get_y_index(); + + shared_ptr> get_stack_pointer(); + + shared_ptr get_status_register(); + + shared_ptr> get_program_counter(); + +private: + shared_ptr> accumulator; + shared_ptr> x_index; + shared_ptr> y_index; + shared_ptr> stack_pointer; + shared_ptr status_register; + shared_ptr> program_counter; +}; + + +#endif //INC_6502_EMULATOR_REGISTER_MANAGER_H diff --git a/src/register/register.cpp b/src/register/register.cpp new file mode 100644 index 0000000..55473d0 --- /dev/null +++ b/src/register/register.cpp @@ -0,0 +1,30 @@ +#include "register.h" +#include +#include + +using namespace std; + +template +Register::Register(string name) { + this->name = name; + this->value = 0; +} + +template +string Register::get_name() { + return name; +} + +template +T Register::get_value() { + return value; +} + +template +void Register::set_value(T value) { + this->value = value; +} + +template class Register; +template class Register; +template class Register>; \ No newline at end of file diff --git a/src/register/register.h b/src/register/register.h new file mode 100644 index 0000000..54f0c71 --- /dev/null +++ b/src/register/register.h @@ -0,0 +1,28 @@ +#ifndef INC_6502_EMULATOR_REGISTER_H +#define INC_6502_EMULATOR_REGISTER_H + +#include +#include + +using namespace std; + +template +class Register +{ +public: + explicit Register(string name); + + string get_name(); + + T get_value(); + void set_value(T value); + +protected: + T value; + +private: + string name; +}; + + +#endif //INC_6502_EMULATOR_REGISTER_H diff --git a/src/register/status-register.cpp b/src/register/status-register.cpp new file mode 100644 index 0000000..3090870 --- /dev/null +++ b/src/register/status-register.cpp @@ -0,0 +1,65 @@ +#include "status-register.h" + +const uint8_t CARRY_FLAG = 0; +const uint8_t ZERO_FLAG = 1; +const uint8_t INTERUPT_DISABLE_FLAG = 2; +const uint8_t DECIMAL_FLAG = 3; +const uint8_t BREAK_FLAG = 4; +const uint8_t OVERFLOW_FLAG = 6; +const uint8_t NEGATIVE_FLAG = 7; + +bool StatusRegister::is_carry_set() { + return this->value[CARRY_FLAG]; +} + +bool StatusRegister::is_zero_set() { + return this->value[ZERO_FLAG]; +} + +bool StatusRegister::is_interupt_disable_set() { + return this->value[INTERUPT_DISABLE_FLAG]; +} + +bool StatusRegister::is_decimal_set() { + return this->value[DECIMAL_FLAG]; +} + +bool StatusRegister::is_break_set() { + return this->value[BREAK_FLAG]; +} + +bool StatusRegister::is_overflow_set() { + return this->value[OVERFLOW_FLAG]; +} + +bool StatusRegister::is_negative_set() { + return this->value[NEGATIVE_FLAG]; +} + +void StatusRegister::set_carry(bool state) { + this->value[CARRY_FLAG] = state; +} + +void StatusRegister::set_zero(bool state) { + this->value[ZERO_FLAG] = state; +} + +void StatusRegister::set_interupt_disable(bool state) { + this->value[INTERUPT_DISABLE_FLAG] = state; +} + +void StatusRegister::set_decimal(bool state) { + this->value[DECIMAL_FLAG] = state; +} + +void StatusRegister::set_break(bool state) { + this->value[BREAK_FLAG] = state; +} + +void StatusRegister::set_overflow(bool state) { + this->value[OVERFLOW_FLAG] = state; +} + +void StatusRegister::set_negative(bool state) { + this->value[NEGATIVE_FLAG] = state; +} \ No newline at end of file diff --git a/src/register/status-register.h b/src/register/status-register.h new file mode 100644 index 0000000..12401bd --- /dev/null +++ b/src/register/status-register.h @@ -0,0 +1,29 @@ +#ifndef INC_6502_EMULATOR_STATUS_REGISTER_H +#define INC_6502_EMULATOR_STATUS_REGISTER_H + +#include "register.h" +#include + +class StatusRegister : public Register> { +public: + explicit StatusRegister() : Register>("P") {} + + bool is_carry_set(); + bool is_zero_set(); + bool is_interupt_disable_set(); + bool is_decimal_set(); + bool is_break_set(); + bool is_overflow_set(); + bool is_negative_set(); + + void set_carry(bool state); + void set_zero(bool state); + void set_interupt_disable(bool state); + void set_decimal(bool state); + void set_break(bool state); + void set_overflow(bool state); + void set_negative(bool state); +}; + + +#endif //INC_6502_EMULATOR_STATUS_REGISTER_H diff --git a/test/flags-opcode-handler-test.cpp b/test/flags-opcode-handler-test.cpp new file mode 100644 index 0000000..3ed2837 --- /dev/null +++ b/test/flags-opcode-handler-test.cpp @@ -0,0 +1,112 @@ +#include "gtest/gtest.h" +#include "../src/machine.h" +#include "../src/program.h" +#include +#include +#include +#include + +using namespace std; + +const uint8_t CLC = 0x18; +const uint8_t SEC = 0x38; +const uint8_t CLI = 0x58; +const uint8_t SEI = 0x78; +const uint8_t CLV = 0xB8; +const uint8_t CLD = 0xD8; +const uint8_t SED = 0xF8; + +unique_ptr ft_get_machine(shared_ptr> code){ + auto program = make_shared(Program(code)); + auto machine = make_unique(Machine(program)); + return machine; +} + +TEST(FlagOpcodeHandlerTest, ClearCarry) { + auto code = make_shared>(vector{CLC}); + auto machine = ft_get_machine(code); + auto status = machine->get_reg_man()->get_status_register(); + + ASSERT_EQ(0, status->is_carry_set()); + + status->set_carry(true); + ASSERT_EQ(1, status->is_carry_set()); + + machine->execute(); + ASSERT_EQ(0, status->is_carry_set()); +} + +TEST(FlagOpcodeHandlerTest, SetCarry) { + auto code = make_shared>(vector{SEC}); + auto machine = ft_get_machine(code); + auto status = machine->get_reg_man()->get_status_register(); + + ASSERT_EQ(0, status->is_carry_set()); + + machine->execute(); + ASSERT_EQ(1, status->is_carry_set()); +} + +TEST(FlagOpcodeHandlerTest, ClearInteruptDisable) { + auto code = make_shared>(vector{CLI}); + auto machine = ft_get_machine(code); + auto status = machine->get_reg_man()->get_status_register(); + + ASSERT_EQ(0, status->is_interupt_disable_set()); + + status->set_interupt_disable(true); + ASSERT_EQ(1, status->is_interupt_disable_set()); + + machine->execute(); + ASSERT_EQ(0, status->is_interupt_disable_set()); +} + +TEST(FlagOpcodeHandlerTest, SetInteruptDisable) { + auto code = make_shared>(vector{SEI}); + auto machine = ft_get_machine(code); + auto status = machine->get_reg_man()->get_status_register(); + + ASSERT_EQ(0, status->is_interupt_disable_set()); + + machine->execute(); + ASSERT_EQ(1, status->is_interupt_disable_set()); +} + +TEST(FlagOpcodeHandlerTest, ClearOverflow) { + auto code = make_shared>(vector{CLV}); + auto machine = ft_get_machine(code); + auto status = machine->get_reg_man()->get_status_register(); + + ASSERT_EQ(0, status->is_overflow_set()); + + status->set_overflow(true); + ASSERT_EQ(1, status->is_overflow_set()); + + machine->execute(); + ASSERT_EQ(0, status->is_overflow_set()); +} + +TEST(FlagOpcodeHandlerTest, ClearDecimal) { + auto code = make_shared>(vector{CLD}); + auto machine = ft_get_machine(code); + auto status = machine->get_reg_man()->get_status_register(); + + ASSERT_EQ(0, status->is_decimal_set()); + + status->set_decimal(true); + ASSERT_EQ(1, status->is_decimal_set()); + + machine->execute(); + ASSERT_EQ(0, status->is_decimal_set()); +} + +TEST(FlagOpcodeHandlerTest, SetDecimal) { + auto code = make_shared>(vector{SED}); + auto machine = ft_get_machine(code); + auto status = machine->get_reg_man()->get_status_register(); + + ASSERT_EQ(0, status->is_decimal_set()); + + machine->execute(); + ASSERT_EQ(1, status->is_decimal_set()); +} diff --git a/test/instruction-executor-test.cpp b/test/instruction-executor-test.cpp new file mode 100644 index 0000000..6d7e22a --- /dev/null +++ b/test/instruction-executor-test.cpp @@ -0,0 +1,29 @@ +#include "gtest/gtest.h" +#include "../src/program.h" +#include "../src/instruction/instruction-executor.h" +#include "../src/register/register-manager.h" +#include "../src/opcode/opcode-handler-directory.h" +#include +#include +#include +#include + +using namespace std; + +const uint8_t CLC = 0x18; +const uint8_t SEC = 0x38; + +const uint8_t LDX_IMMEDIATE = 0xA9; + +TEST(InstructionExecutor, Execute) { + auto reg_man = make_shared(RegisterManager()); + auto code = make_shared>(vector + {SEC, + CLC + }); + auto program = make_shared(Program(code)); + auto opcode_handler_dir = make_shared(OpcodeHandlerDirectory(program, reg_man)); + auto executor = make_unique(InstructionExecutor(program, reg_man, opcode_handler_dir)); + + executor->execute(); +} diff --git a/test/machine-test.cpp b/test/machine-test.cpp new file mode 100644 index 0000000..2851bb6 --- /dev/null +++ b/test/machine-test.cpp @@ -0,0 +1,27 @@ +#include "gtest/gtest.h" +#include "../src/machine.h" +#include "../src/program.h" +#include +#include +#include +#include + +using namespace std; + +const uint8_t CLC = 0x18; +const uint8_t SEC = 0x38; +const uint8_t CLI = 0x58; +const uint8_t SEI = 0x78; +const uint8_t CLV = 0xB8; +const uint8_t CLD = 0xD8; +const uint8_t SED = 0xF8; + +const uint8_t LDX_IMMEDIATE = 0xA9; + +TEST(Machine, Execute) { + auto code = make_shared>(vector{SEC, SEI, SED, CLI, CLV, CLD, CLC, LDX_IMMEDIATE, 0x6B}); + auto program = make_shared(Program(code)); + auto machine = make_unique(Machine(program)); + + machine->execute(); +} diff --git a/test/register-opcode-handler-test.cpp b/test/register-opcode-handler-test.cpp new file mode 100644 index 0000000..a7b28fb --- /dev/null +++ b/test/register-opcode-handler-test.cpp @@ -0,0 +1,219 @@ +#include "gtest/gtest.h" +#include "../src/machine.h" +#include "../src/program.h" + +#include +#include + +using namespace std; + +const uint8_t TAX = 0xAA; +const uint8_t TXA = 0x8A; +const uint8_t DEX = 0xCA; +const uint8_t INX = 0xE8; +const uint8_t TAY = 0xA8; +const uint8_t TYA = 0x98; +const uint8_t DEY = 0x88; +const uint8_t INY = 0xC8; + +unique_ptr rt_get_machine(shared_ptr> code) { + auto program = make_shared(Program(code)); + auto machine = make_unique(Machine(program)); + return machine; +} + +TEST(RegisterOpcodeHandler, TransferAtoX) { + auto code = make_shared>(vector{TAX}); + auto machine = rt_get_machine(code); + auto reg_man = machine->get_reg_man(); + + auto aval = reg_man->get_accumulator()->get_value(); + auto xval = reg_man->get_x_index()->get_value(); + ASSERT_EQ(0, aval); + ASSERT_EQ(0, xval); + + machine->execute(); + aval = reg_man->get_accumulator()->get_value(); + xval = reg_man->get_x_index()->get_value(); + ASSERT_EQ(0, aval); + ASSERT_EQ(0, xval); + + reg_man->get_program_counter()->set_value(0); + reg_man->get_accumulator()->set_value(0x3e); + machine->execute(); + aval = reg_man->get_accumulator()->get_value(); + xval = reg_man->get_x_index()->get_value(); + ASSERT_EQ(0x3e, aval); + ASSERT_EQ(0x3e, xval); +} + +TEST(RegisterOpcodeHandler, TransferXtoA) { + auto code = make_shared>(vector{TXA}); + auto machine = rt_get_machine(code); + auto reg_man = machine->get_reg_man(); + + auto aval = reg_man->get_accumulator()->get_value(); + auto xval = reg_man->get_x_index()->get_value(); + ASSERT_EQ(0, aval); + ASSERT_EQ(0, xval); + + machine->execute(); + aval = reg_man->get_accumulator()->get_value(); + xval = reg_man->get_x_index()->get_value(); + ASSERT_EQ(0, aval); + ASSERT_EQ(0, xval); + + reg_man->get_program_counter()->set_value(0); + reg_man->get_x_index()->set_value(0x4f); + machine->execute(); + aval = reg_man->get_accumulator()->get_value(); + xval = reg_man->get_x_index()->get_value(); + ASSERT_EQ(0x4f, aval); + ASSERT_EQ(0x4f, xval); +} + +TEST(RegisterOpcodeHandler, DecrementX) { + auto code = make_shared>(vector{DEX}); + auto machine = rt_get_machine(code); + auto reg_man = machine->get_reg_man(); + + auto xval = reg_man->get_x_index()->get_value(); + ASSERT_EQ(0, xval); + + machine->execute(); + xval = reg_man->get_x_index()->get_value(); + ASSERT_EQ(0xff, xval); + + reg_man->get_program_counter()->set_value(0); + machine->execute(); + xval = reg_man->get_x_index()->get_value(); + ASSERT_EQ(0xfe, xval); + + reg_man->get_program_counter()->set_value(0); + reg_man->get_x_index()->set_value(0x45); + machine->execute(); + xval = reg_man->get_x_index()->get_value(); + ASSERT_EQ(0x44, xval); +} + +TEST(RegisterOpcodeHandler, IncrementX) { + auto code = make_shared>(vector{INX}); + auto machine = rt_get_machine(code); + auto reg_man = machine->get_reg_man(); + + auto xval = reg_man->get_x_index()->get_value(); + ASSERT_EQ(0, xval); + + machine->execute(); + xval = reg_man->get_x_index()->get_value(); + ASSERT_EQ(0x01, xval); + + reg_man->get_program_counter()->set_value(0); + machine->execute(); + xval = reg_man->get_x_index()->get_value(); + ASSERT_EQ(0x02, xval); + + reg_man->get_program_counter()->set_value(0); + reg_man->get_x_index()->set_value(0x45); + machine->execute(); + xval = reg_man->get_x_index()->get_value(); + ASSERT_EQ(0x46, xval); +} + +TEST(RegisterOpcodeHandler, TransferAtoY) { + auto code = make_shared>(vector{TAY}); + auto machine = rt_get_machine(code); + auto reg_man = machine->get_reg_man(); + + auto aval = reg_man->get_accumulator()->get_value(); + auto yval = reg_man->get_y_index()->get_value(); + ASSERT_EQ(0, aval); + ASSERT_EQ(0, yval); + + machine->execute(); + aval = reg_man->get_accumulator()->get_value(); + yval = reg_man->get_y_index()->get_value(); + ASSERT_EQ(0, aval); + ASSERT_EQ(0, yval); + + reg_man->get_program_counter()->set_value(0); + reg_man->get_accumulator()->set_value(0xaf); + machine->execute(); + aval = reg_man->get_accumulator()->get_value(); + yval = reg_man->get_y_index()->get_value(); + ASSERT_EQ(0xaf, aval); + ASSERT_EQ(0xaf, yval); +} + +TEST(RegisterOpcodeHandler, TransferYtoA) { + auto code = make_shared>(vector{TYA}); + auto machine = rt_get_machine(code); + auto reg_man = machine->get_reg_man(); + + auto aval = reg_man->get_accumulator()->get_value(); + auto yval = reg_man->get_y_index()->get_value(); + ASSERT_EQ(0, aval); + ASSERT_EQ(0, yval); + + machine->execute(); + aval = reg_man->get_accumulator()->get_value(); + yval = reg_man->get_y_index()->get_value(); + ASSERT_EQ(0, aval); + ASSERT_EQ(0, yval); + + reg_man->get_program_counter()->set_value(0); + reg_man->get_y_index()->set_value(0xff); + machine->execute(); + aval = reg_man->get_accumulator()->get_value(); + yval = reg_man->get_y_index()->get_value(); + ASSERT_EQ(0xff, aval); + ASSERT_EQ(0xff, yval); +} + +TEST(RegisterOpcodeHandler, DecrementY) { + auto code = make_shared>(vector{DEY}); + auto machine = rt_get_machine(code); + auto reg_man = machine->get_reg_man(); + + auto yval = reg_man->get_y_index()->get_value(); + ASSERT_EQ(0, yval); + + machine->execute(); + yval = reg_man->get_y_index()->get_value(); + ASSERT_EQ(0xff, yval); + + reg_man->get_program_counter()->set_value(0); + machine->execute(); + yval = reg_man->get_y_index()->get_value(); + ASSERT_EQ(0xfe, yval); + + reg_man->get_program_counter()->set_value(0); + reg_man->get_y_index()->set_value(0x3b); + machine->execute(); + yval = reg_man->get_y_index()->get_value(); + ASSERT_EQ(0x3a, yval); +} + +TEST(RegisterOpcodeHandler, IncrementY) { + auto code = make_shared>(vector{INY}); + auto machine = rt_get_machine(code); + auto reg_man = machine->get_reg_man(); + + auto yval = reg_man->get_y_index()->get_value(); + ASSERT_EQ(0, yval); + + machine->execute(); + yval = reg_man->get_y_index()->get_value(); + ASSERT_EQ(0x01, yval); + + reg_man->get_program_counter()->set_value(0); + machine->execute(); + yval = reg_man->get_y_index()->get_value(); + ASSERT_EQ(0x02, yval); + + reg_man->get_program_counter()->set_value(0); + reg_man->get_y_index()->set_value(0x3b); + machine->execute(); + yval = reg_man->get_y_index()->get_value(); + ASSERT_EQ(0x3c, yval); +} diff --git a/test/register-test.cpp b/test/register-test.cpp new file mode 100644 index 0000000..93f5413 --- /dev/null +++ b/test/register-test.cpp @@ -0,0 +1,8 @@ +#include "gtest/gtest.h" +#include "../src/register/register.h" + +TEST(Construct, Equals) { + auto reg = new Register("PC"); + + EXPECT_EQ("PC", reg->get_name()); +} diff --git a/test/status-register-test.cpp b/test/status-register-test.cpp new file mode 100644 index 0000000..fc6c418 --- /dev/null +++ b/test/status-register-test.cpp @@ -0,0 +1,170 @@ +#include "gtest/gtest.h" +#include "../src/register/status-register.h" +#include +#include +#include + +using namespace std; + +string get_bit_pattern(StatusRegister* reg) { + stringstream stream; + stream << reg->get_value(); + + return stream.str(); +} + +TEST(StatusRegister, Construct) { + auto reg = make_unique(StatusRegister()); + auto bit_pattern = get_bit_pattern(reg.get()); + + EXPECT_EQ("P", reg->get_name()); + + EXPECT_EQ("00000000", bit_pattern); + + EXPECT_EQ(false, reg->is_carry_set()); + EXPECT_EQ(false, reg->is_zero_set()); + EXPECT_EQ(false, reg->is_interupt_disable_set()); + EXPECT_EQ(false, reg->is_decimal_set()); + EXPECT_EQ(false, reg->is_break_set()); + EXPECT_EQ(false, reg->is_overflow_set()); + EXPECT_EQ(false, reg->is_negative_set()); +} + +TEST(StatusRegister, CarryFlag) { + auto reg = make_unique(StatusRegister()); + EXPECT_EQ(false, reg->is_carry_set()); + + reg->set_carry(true); + EXPECT_EQ(true, reg->is_carry_set()); + + auto bit_pattern = get_bit_pattern(reg.get()); + EXPECT_EQ("00000001", bit_pattern); + + reg->set_carry(false); + EXPECT_EQ(false, reg->is_carry_set()); + bit_pattern = get_bit_pattern(reg.get()); + EXPECT_EQ("00000000", bit_pattern); +} + +TEST(StatusRegister, ZeroFlag) { + auto reg = make_unique(StatusRegister()); + EXPECT_EQ(false, reg->is_zero_set()); + + reg->set_zero(true); + EXPECT_EQ(true, reg->is_zero_set()); + + auto bit_pattern = get_bit_pattern(reg.get()); + EXPECT_EQ("00000010", bit_pattern); + + reg->set_zero(false); + EXPECT_EQ(false, reg->is_zero_set()); + bit_pattern = get_bit_pattern(reg.get()); + EXPECT_EQ("00000000", bit_pattern); +} + +TEST(StatusRegister, InteruptDisableFlag) { + auto reg = make_unique(StatusRegister()); + EXPECT_EQ(false, reg->is_interupt_disable_set()); + + reg->set_interupt_disable(true); + EXPECT_EQ(true, reg->is_interupt_disable_set()); + + auto bit_pattern = get_bit_pattern(reg.get()); + EXPECT_EQ("00000100", bit_pattern); + + reg->set_interupt_disable(false); + EXPECT_EQ(false, reg->is_interupt_disable_set()); + bit_pattern = get_bit_pattern(reg.get()); + EXPECT_EQ("00000000", bit_pattern); +} + +TEST(StatusRegister, DecimalFlag) { + auto reg = make_unique(StatusRegister()); + EXPECT_EQ(false, reg->is_decimal_set()); + + reg->set_decimal(true); + EXPECT_EQ(true, reg->is_decimal_set()); + + auto bit_pattern = get_bit_pattern(reg.get()); + EXPECT_EQ("00001000", bit_pattern); + + reg->set_decimal(false); + EXPECT_EQ(false, reg->is_decimal_set()); + bit_pattern = get_bit_pattern(reg.get()); + EXPECT_EQ("00000000", bit_pattern); +} + +TEST(StatusRegister, BreakFlag) { + auto reg = make_unique(StatusRegister()); + EXPECT_EQ(false, reg->is_break_set()); + + reg->set_break(true); + EXPECT_EQ(true, reg->is_break_set()); + + auto bit_pattern = get_bit_pattern(reg.get()); + EXPECT_EQ("00010000", bit_pattern); + + reg->set_break(false); + EXPECT_EQ(false, reg->is_break_set()); + bit_pattern = get_bit_pattern(reg.get()); + EXPECT_EQ("00000000", bit_pattern); +} + +TEST(StatusRegister, OverflowFlag) { + auto reg = make_unique(StatusRegister()); + EXPECT_EQ(false, reg->is_overflow_set()); + + reg->set_overflow(true); + EXPECT_EQ(true, reg->is_overflow_set()); + + auto bit_pattern = get_bit_pattern(reg.get()); + EXPECT_EQ("01000000", bit_pattern); + + reg->set_overflow(false); + EXPECT_EQ(false, reg->is_overflow_set()); + bit_pattern = get_bit_pattern(reg.get()); + EXPECT_EQ("00000000", bit_pattern); +} + +TEST(StatusRegister, NegativeFlag) { + auto reg = make_unique(StatusRegister()); + EXPECT_EQ(false, reg->is_negative_set()); + + reg->set_negative(true); + EXPECT_EQ(true, reg->is_negative_set()); + + auto bit_pattern = get_bit_pattern(reg.get()); + EXPECT_EQ("10000000", bit_pattern); + + reg->set_negative(false); + EXPECT_EQ(false, reg->is_negative_set()); + bit_pattern = get_bit_pattern(reg.get()); + EXPECT_EQ("00000000", bit_pattern); +} + +TEST(StatusRegister, AllFlags) { + auto reg = make_unique(StatusRegister()); + EXPECT_EQ(false, reg->is_negative_set()); + + reg->set_carry(true); + reg->set_zero(true); + reg->set_interupt_disable(true); + reg->set_decimal(true); + reg->set_break(true); + reg->set_overflow(true); + reg->set_negative(true); + + auto bit_pattern = get_bit_pattern(reg.get()); + EXPECT_EQ("11011111", bit_pattern); + + reg->set_carry(false); + reg->set_zero(false); + reg->set_interupt_disable(false); + reg->set_decimal(false); + reg->set_break(false); + reg->set_overflow(false); + reg->set_negative(false); + + bit_pattern = get_bit_pattern(reg.get()); + EXPECT_EQ("00000000", bit_pattern); +}