diff --git a/CMakeLists.txt b/CMakeLists.txt index 947d1d9..10a1462 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,9 +25,9 @@ add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src 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/test-utils.h) +add_executable(6502_emulator_test ${test_src}) 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} src/utils.cpp) \ No newline at end of file +add_executable(6502_emulator ${emulator_src}) \ No newline at end of file diff --git a/src/opcode/handler/store-opcode-handler-container.cpp b/src/opcode/handler/store-opcode-handler-container.cpp new file mode 100644 index 0000000..7ccb832 --- /dev/null +++ b/src/opcode/handler/store-opcode-handler-container.cpp @@ -0,0 +1,72 @@ +#include "store-opcode-handler-container.h" +#include "../../utils.h" + +namespace emu_6502 { + StoreOpcodeHandlerContainer::StoreOpcodeHandlerContainer() : OpcodeHandlerContainer() { + handlers.insert({Op::STA_ZPG, [this](Machine& machine) { st_zpg(machine, machine.get_cpu().get_a()); }}); + handlers.insert({Op::STA_ZPG_X, [this](Machine& machine) { st_zpg_x(machine, machine.get_cpu().get_a()); }}); + handlers.insert({Op::STA_ABS, [this](Machine& machine) { st_abs(machine, machine.get_cpu().get_a()); }}); + handlers.insert({Op::STA_ABS_X, [this](Machine& machine) { st_abs_x(machine, machine.get_cpu().get_a()); }}); + handlers.insert({Op::STA_ABS_Y, [this](Machine& machine) { st_abs_y(machine, machine.get_cpu().get_a()); }}); + handlers.insert({Op::STA_IND_X, [this](Machine& machine) { st_ind_x(machine, machine.get_cpu().get_a()); }}); + handlers.insert({Op::STA_IND_Y, [this](Machine& machine) { st_ind_y(machine, machine.get_cpu().get_a()); }}); + + handlers.insert({Op::STX_ZPG, [this](Machine& machine) { st_zpg(machine, machine.get_cpu().get_x()); }}); + handlers.insert({Op::STX_ZPG_Y, [this](Machine& machine) { st_zpg_y(machine, machine.get_cpu().get_x()); }}); + handlers.insert({Op::STX_ABS, [this](Machine& machine) { st_abs(machine, machine.get_cpu().get_x()); }}); + + handlers.insert({Op::STY_ZPG, [this](Machine& machine) { st_zpg(machine, machine.get_cpu().get_y()); }}); + handlers.insert({Op::STY_ZPG_X, [this](Machine& machine) { st_zpg_x(machine, machine.get_cpu().get_y()); }}); + handlers.insert({Op::STY_ABS, [this](Machine& machine) { st_abs(machine, machine.get_cpu().get_y()); }}); + } + + void StoreOpcodeHandlerContainer::store_to(Machine& machine, Register& reg, uint16_t address) { + machine.get_memory().set_at(address, reg.get_value()); + } + + void StoreOpcodeHandlerContainer::st_zpg(Machine& machine, Register& reg) { + auto addr = get_zpg_address(machine.read_program_byte()); + store_to(machine, reg, addr); + } + + void StoreOpcodeHandlerContainer::st_zpg_x(Machine& machine, Register& reg) { + auto addr = get_zpg_x_address(machine.read_program_byte(), machine.get_cpu()); + store_to(machine, reg, addr); + } + + void StoreOpcodeHandlerContainer::st_zpg_y(Machine& machine, Register& reg) { + auto addr = get_zpg_y_address(machine.read_program_byte(), machine.get_cpu()); + store_to(machine, reg, addr); + } + + void StoreOpcodeHandlerContainer::st_abs(Machine& machine, Register& reg) { + auto low = machine.read_program_byte(); + auto high = machine.read_program_byte(); + auto addr = get_abs_address(low, high); + store_to(machine, reg, addr); + } + + void StoreOpcodeHandlerContainer::st_abs_x(Machine& machine, Register& reg) { + auto low = machine.read_program_byte(); + auto high = machine.read_program_byte(); + auto addr = get_abs_x_address(low, high, machine.get_cpu()); + store_to(machine, reg, addr); + } + + void StoreOpcodeHandlerContainer::st_abs_y(Machine& machine, Register& reg) { + auto low = machine.read_program_byte(); + auto high = machine.read_program_byte(); + auto addr = get_abs_y_address(low, high, machine.get_cpu()); + store_to(machine, reg, addr); + } + + void StoreOpcodeHandlerContainer::st_ind_x(Machine& machine, Register& reg) { + auto addr = get_ind_x_address(machine.read_program_byte(), machine); + store_to(machine, reg, addr); + } + + void StoreOpcodeHandlerContainer::st_ind_y(Machine& machine, Register& reg) { + auto addr = get_ind_y_address(machine.read_program_byte(), machine); + store_to(machine, reg, addr); + } +} \ No newline at end of file diff --git a/src/opcode/handler/store-opcode-handler-container.h b/src/opcode/handler/store-opcode-handler-container.h new file mode 100644 index 0000000..0ef1567 --- /dev/null +++ b/src/opcode/handler/store-opcode-handler-container.h @@ -0,0 +1,52 @@ +#ifndef INC_6502_EMULATOR_STORE_OPCODE_HANDLER_CONTAINER_H +#define INC_6502_EMULATOR_STORE_OPCODE_HANDLER_CONTAINER_H + +#include "opcode-handler-container.h" +#include "../../machine/machine.h" + +#include +#include +#include + +using namespace std; + +namespace emu_6502 { + class StoreOpcodeHandlerContainer : public OpcodeHandlerContainer { + private: + enum Op { + STA_ZPG = 0x85, + STA_ZPG_X = 0x95, + STA_ABS = 0x8D, + STA_ABS_X = 0x9D, + STA_ABS_Y = 0x99, + STA_IND_X = 0x81, + STA_IND_Y = 0x91, + + STX_ZPG = 0x86, + STX_ZPG_Y = 0x96, + STX_ABS = 0x8E, + + STY_ZPG = 0x84, + STY_ZPG_X = 0x94, + STY_ABS = 0x8C, + }; + + void store_to(Machine& machine, Register& reg, uint16_t address); + + void st_zpg(Machine& machine, Register& reg); + void st_zpg_x(Machine& machine, Register& reg); + void st_zpg_y(Machine& machine, Register& reg); + void st_abs(Machine& machine, Register& reg); + void st_abs_x(Machine& machine, Register& reg); + void st_abs_y(Machine& machine, Register& reg); + void st_ind_x(Machine& machine, Register& reg); + void st_ind_y(Machine& machine, Register& reg); + + public: + StoreOpcodeHandlerContainer(); + StoreOpcodeHandlerContainer(const StoreOpcodeHandlerContainer& other) = delete; + }; +} + + +#endif //INC_6502_EMULATOR_STORE_OPCODE_HANDLER_CONTAINER_H diff --git a/src/opcode/opcode-handler-directory.cpp b/src/opcode/opcode-handler-directory.cpp index 8e7dd5b..3edeb67 100644 --- a/src/opcode/opcode-handler-directory.cpp +++ b/src/opcode/opcode-handler-directory.cpp @@ -1,11 +1,13 @@ #include "opcode-handler-directory.h" #include "handler/load-opcode-handler-container.h" +#include "handler/store-opcode-handler-container.h" #include "../utils.h" namespace emu_6502 { OpcodeHandlerDirectory::OpcodeHandlerDirectory() : handler_containers{}, handlers{} { handler_containers.push_back(make_unique()); + handler_containers.push_back(make_unique()); init_handlers(); } diff --git a/test/store-opcode-handler-test.cpp b/test/store-opcode-handler-test.cpp new file mode 100644 index 0000000..27b376a --- /dev/null +++ b/test/store-opcode-handler-test.cpp @@ -0,0 +1,231 @@ +#include "gtest/gtest.h" +#include "test-utils.h" + +#include +#include + +using namespace std; +using namespace emu_6502; + +const uint8_t STA_ZPG = 0x85; +const uint8_t STA_ZPG_X = 0x95; +const uint8_t STA_ABS = 0x8D; +const uint8_t STA_ABS_X = 0x9D; +const uint8_t STA_ABS_Y = 0x99; +const uint8_t STA_IND_X = 0x81; +const uint8_t STA_IND_Y = 0x91; + +const uint8_t STX_ZPG = 0x86; +const uint8_t STX_ZPG_Y = 0x96; +const uint8_t STX_ABS = 0x8E; + +const uint8_t STY_ZPG = 0x84; +const uint8_t STY_ZPG_X = 0x94; +const uint8_t STY_ABS = 0x8C; + +TEST(StoreOpcodeHandlerContainer, STA_ZPG) { + auto machine = create_machine({STA_ZPG, 0xe5}); + machine->get_cpu().get_a().set_value(0x9d); + machine->execute(); + + ASSERT_EQ(0x9d, machine->get_memory().get_at(0xe5)); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(StoreOpcodeHandlerContainer, STA_ZPG_FlagsUnaffected) { + auto machine = create_machine({STA_ZPG, 0xe5}); + machine->get_cpu().get_a().set_value(0x9d); + + auto& ps = machine->get_cpu().get_ps(); + ps.set_overflow(true); + ps.set_zero(true); + ps.set_negative(true); + ps.set_carry(true); + ps.set_break(true); + ps.set_decimal(true); + ps.set_interupt_disable(true); + + machine->execute(); + + ASSERT_EQ(0x9d, machine->get_memory().get_at(0xe5)); + + RegisterFlagSet flags{}; + flags.overflow = true; + flags.zero = true; + flags.negative = true; + flags.carry = true; + flags.brk = true; + flags.decimal = true; + flags.interupt_disable = true; + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags)); +} + +TEST(StoreOpcodeHandlerContainer, STA_ZPG_X) { + auto machine = create_machine({STA_ZPG_X, 0x10}); + machine->get_cpu().get_x().set_value(0x15); + machine->get_cpu().get_a().set_value(0x4f); + machine->execute(); + + ASSERT_EQ(0x4f, machine->get_memory().get_at(0x25)); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(StoreOpcodeHandlerContainer, STA_ZPG_X_Wrap) { + auto machine = create_machine({STA_ZPG_X, 0xFF}); + machine->get_cpu().get_x().set_value(0x80); + machine->get_cpu().get_a().set_value(0x4f); + machine->execute(); + + ASSERT_EQ(0x4f, machine->get_memory().get_at(0x7F)); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(StoreOpcodeHandlerContainer, STA_ABS) { + auto machine = create_machine({STA_ABS, 0x55, 0x66}); + machine->get_cpu().get_a().set_value(0x10); + machine->execute(); + + ASSERT_EQ(0x10, machine->get_memory().get_at(0x6655)); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(StoreOpcodeHandlerContainer, STA_ABS_X) { + auto machine = create_machine({STA_ABS_X, 0x55, 0x66}); + machine->get_cpu().get_x().set_value(0x4); + machine->get_cpu().get_a().set_value(0x10); + machine->execute(); + + ASSERT_EQ(0x10, machine->get_memory().get_at(0x6659)); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(StoreOpcodeHandlerContainer, STA_ABS_Y) { + auto machine = create_machine({STA_ABS_Y, 0x55, 0x66}); + machine->get_cpu().get_y().set_value(0x4); + machine->get_cpu().get_a().set_value(0x10); + machine->execute(); + + ASSERT_EQ(0x10, machine->get_memory().get_at(0x6659)); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(StoreOpcodeHandlerContainer, STA_IND_X) { + auto machine = create_machine({STA_IND_X, 0x55}); + machine->get_cpu().get_x().set_value(0x4); + machine->get_cpu().get_a().set_value(0x33); + + // pointer + machine->get_memory().set_at(0x59, 0x3c); + machine->get_memory().set_at(0x5A, 0x4d); + + machine->execute(); + + ASSERT_EQ(0x33, machine->get_memory().get_at(0x4d3c)); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(StoreOpcodeHandlerContainer, STA_IND_X_Wrap) { + auto machine = create_machine({STA_IND_X, 0xFF}); + machine->get_cpu().get_x().set_value(0x6b); + machine->get_cpu().get_a().set_value(0x33); + + // pointer + machine->get_memory().set_at(0x6a, 0x3c); + machine->get_memory().set_at(0x6b, 0x4d); + + machine->execute(); + + ASSERT_EQ(0x33, machine->get_memory().get_at(0x4d3c)); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(StoreOpcodeHandlerContainer, STA_IND_Y) { + auto machine = create_machine({STA_IND_Y, 0x55}); + machine->get_memory().set_at(0x55, 0x12); + machine->get_memory().set_at(0x56, 0x34); + + machine->get_cpu().get_y().set_value(0x1a); + machine->get_cpu().get_a().set_value(0x33); + + machine->execute(); + + ASSERT_EQ(0x33, machine->get_memory().get_at(0x342c)); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(StoreOpcodeHandlerContainer, STX_ZPG) { + auto machine = create_machine({STX_ZPG, 123}); + machine->get_cpu().get_x().set_value(78); + machine->execute(); + + ASSERT_EQ(78, machine->get_memory().get_at(123)); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(StoreOpcodeHandlerContainer, STX_ZPG_Y) { + auto machine = create_machine({STX_ZPG_Y, 0x10}); + machine->get_cpu().get_y().set_value(0x15); + machine->get_cpu().get_x().set_value(78); + machine->execute(); + + ASSERT_EQ(78, machine->get_memory().get_at(0x25)); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(StoreOpcodeHandlerContainer, STX_ZPG_Y_Wrap) { + auto machine = create_machine({STX_ZPG_Y, 0xFF}); + machine->get_cpu().get_y().set_value(0x80); + machine->get_cpu().get_x().set_value(78); + machine->execute(); + + ASSERT_EQ(78, machine->get_memory().get_at(0x7F)); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(StoreOpcodeHandlerContainer, STX_ABS) { + auto machine = create_machine({STX_ABS, 0x55, 0x66}); + machine->get_cpu().get_x().set_value(0x10); + machine->execute(); + + ASSERT_EQ(0x10, machine->get_memory().get_at(0x6655)); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(StoreOpcodeHandlerContainer, STY_ZPG) { + auto machine = create_machine({STY_ZPG, 123}); + machine->get_cpu().get_y().set_value(78); + machine->execute(); + + ASSERT_EQ(78, machine->get_memory().get_at(123)); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(StoreOpcodeHandlerContainer, STY_ZPG_X) { + auto machine = create_machine({STY_ZPG_X, 0x10}); + machine->get_cpu().get_x().set_value(0x15); + machine->get_cpu().get_y().set_value(78); + machine->execute(); + + ASSERT_EQ(78, machine->get_memory().get_at(0x25)); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(StoreOpcodeHandlerContainer, STY_ZPG_X_Wrap) { + auto machine = create_machine({STY_ZPG_X, 0xFF}); + machine->get_cpu().get_x().set_value(0x80); + machine->get_cpu().get_y().set_value(78); + machine->execute(); + + ASSERT_EQ(78, machine->get_memory().get_at(0x7F)); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(StoreOpcodeHandlerContainer, STY_ABS) { + auto machine = create_machine({STY_ABS, 0x55, 0x66}); + machine->get_cpu().get_y().set_value(0x10); + machine->execute(); + + ASSERT_EQ(0x10, machine->get_memory().get_at(0x6655)); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + diff --git a/test/test-utils.cpp b/test/test-utils.cpp new file mode 100644 index 0000000..13ae7d8 --- /dev/null +++ b/test/test-utils.cpp @@ -0,0 +1,19 @@ +#include "test-utils.h" + +unique_ptr create_machine(vector code) { + auto machine = make_unique(); + machine->load(code, 0x600); + + return machine; +} + +bool are_flags_set(const StatusRegister& reg, const RegisterFlagSet& flags) { + return + flags.carry == reg.is_carry_set() && + flags.zero == reg.is_zero_set() && + flags.interupt_disable == reg.is_interupt_disable_set() && + flags.decimal == reg.is_interupt_disable_set() && + flags.brk == reg.is_break_set() && + flags.overflow == reg.is_overflow_set() && + flags.negative == reg.is_negative_set(); +} diff --git a/test/test-utils.h b/test/test-utils.h index 1cffa61..df9d22e 100644 --- a/test/test-utils.h +++ b/test/test-utils.h @@ -1,3 +1,6 @@ +#ifndef INC_6502_EMULATOR_TEST_UTILS_H +#define INC_6502_EMULATOR_TEST_UTILS_H + #include "../src/opcode/opcode-handler-directory.h" #include "../src/machine/status-register.h" @@ -17,20 +20,7 @@ struct RegisterFlagSet { bool negative; }; -unique_ptr create_machine(vector code) { - auto machine = make_unique(); - machine->load(code, 0x600); +unique_ptr create_machine(vector code); +bool are_flags_set(const StatusRegister& reg, const RegisterFlagSet& flags); - return machine; -} - -bool are_flags_set(const StatusRegister& reg, const RegisterFlagSet& flags) { - return - flags.carry == reg.is_carry_set() && - flags.zero == reg.is_zero_set() && - flags.interupt_disable == reg.is_interupt_disable_set() && - flags.decimal == reg.is_interupt_disable_set() && - flags.brk == reg.is_break_set() && - flags.overflow == reg.is_overflow_set() && - flags.negative == reg.is_negative_set(); -} \ No newline at end of file +#endif \ No newline at end of file