From b2baba888fcba824c725a5a31df43e5fb7e353e9 Mon Sep 17 00:00:00 2001 From: Tony Di Nucci Date: Wed, 27 Mar 2019 20:04:01 +0000 Subject: [PATCH] More opcodes --- CMakeLists.txt | 4 +- src/machine.cpp | 2 +- src/memory.cpp | 19 +++- src/memory.h | 8 +- src/opcode/handler/flag-opcode-handler.h | 6 +- src/opcode/handler/lda-opcode-handler.cpp | 5 + src/opcode/handler/lda-opcode-handler.h | 14 +++ src/opcode/handler/ldx-opcode-handler.cpp | 42 ++++++- src/opcode/handler/ldx-opcode-handler.h | 23 ++-- src/opcode/handler/register-opcode-handler.h | 5 +- src/opcode/handler/stack-opcode-handler.cpp | 43 +++++++ src/opcode/handler/stack-opcode-handler.h | 27 +++++ src/opcode/opcode-handler-directory.cpp | 10 +- src/opcode/opcode-handler-directory.h | 3 +- src/opcode/opcode-handler.cpp | 5 +- src/opcode/opcode-handler.h | 4 +- src/stack.cpp | 26 +++++ src/stack.h | 22 ++++ test/instruction-executor-test.cpp | 3 +- test/ldx-opcode-handler-test.cpp | 106 ++++++++++++++++++ test/stack-opcode-handler-test.cpp | 112 +++++++++++++++++++ 21 files changed, 445 insertions(+), 44 deletions(-) create mode 100644 src/opcode/handler/lda-opcode-handler.cpp create mode 100644 src/opcode/handler/lda-opcode-handler.h create mode 100644 src/opcode/handler/stack-opcode-handler.cpp create mode 100644 src/opcode/handler/stack-opcode-handler.h create mode 100644 src/stack.cpp create mode 100644 src/stack.h create mode 100644 test/ldx-opcode-handler-test.cpp create mode 100644 test/stack-opcode-handler-test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5dd10f5..0a98d69 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}) +add_executable(6502_emulator_test ${test_src} test/stack-opcode-handler-test.cpp test/ldx-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}) +add_executable(6502_emulator ${emulator_src} src/opcode/handler/stack-opcode-handler.cpp src/opcode/handler/stack-opcode-handler.h src/stack.cpp src/stack.h src/opcode/handler/lda-opcode-handler.cpp src/opcode/handler/lda-opcode-handler.h) diff --git a/src/machine.cpp b/src/machine.cpp index 4e63c9a..3af29fb 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -6,7 +6,7 @@ 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)); + opcode_handler_dir = make_shared(OpcodeHandlerDirectory(program, reg_man, memory)); instruction_executor = make_unique(InstructionExecutor(program, reg_man, opcode_handler_dir)); } diff --git a/src/memory.cpp b/src/memory.cpp index 24b6476..4415896 100644 --- a/src/memory.cpp +++ b/src/memory.cpp @@ -2,13 +2,22 @@ #include Memory::Memory() { - bytes = make_shared>(vector((1024 * 64) - 1)); + bytes = make_unique>(vector(0xFFFF)); + stack = make_unique(Stack()); +} + +void Memory::set_byte_at(uint16_t index, uint8_t value) { + bytes->at(index) = value; } 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); +} + +void Memory::stack_push(uint8_t data) { + stack->push(data); +} + +uint8_t Memory::stack_pop() { + return stack->pop(); } \ No newline at end of file diff --git a/src/memory.h b/src/memory.h index d824a42..bf8444d 100644 --- a/src/memory.h +++ b/src/memory.h @@ -1,7 +1,7 @@ #ifndef INC_6502_EMULATOR_MEMORY_H #define INC_6502_EMULATOR_MEMORY_H - +#include "stack.h" #include #include @@ -11,10 +11,14 @@ class Memory { public: explicit Memory(); + void set_byte_at(uint16_t index, uint8_t value); uint8_t get_byte_at(uint16_t index); + void stack_push(uint8_t data); + uint8_t stack_pop(); private: - shared_ptr> bytes; + unique_ptr> bytes; + unique_ptr stack; }; diff --git a/src/opcode/handler/flag-opcode-handler.h b/src/opcode/handler/flag-opcode-handler.h index 5241358..87b704b 100644 --- a/src/opcode/handler/flag-opcode-handler.h +++ b/src/opcode/handler/flag-opcode-handler.h @@ -1,7 +1,6 @@ #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 { @@ -14,8 +13,9 @@ public: 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) { + explicit FlagOpcodeHandler(shared_ptr program, shared_ptr reg_man, + shared_ptr memory) : + OpcodeHandler(program, reg_man, memory) { handled_opcodes->push_back(CLC); handled_opcodes->push_back(SEC); handled_opcodes->push_back(CLI); diff --git a/src/opcode/handler/lda-opcode-handler.cpp b/src/opcode/handler/lda-opcode-handler.cpp new file mode 100644 index 0000000..f950b64 --- /dev/null +++ b/src/opcode/handler/lda-opcode-handler.cpp @@ -0,0 +1,5 @@ +// +// Created by tony on 27/03/19. +// + +#include "lda-opcode-handler.h" diff --git a/src/opcode/handler/lda-opcode-handler.h b/src/opcode/handler/lda-opcode-handler.h new file mode 100644 index 0000000..020f51d --- /dev/null +++ b/src/opcode/handler/lda-opcode-handler.h @@ -0,0 +1,14 @@ +// +// Created by tony on 27/03/19. +// + +#ifndef INC_6502_EMULATOR_LDA_OPCODE_HANDLER_H +#define INC_6502_EMULATOR_LDA_OPCODE_HANDLER_H + + +class LdaOpcodeHandler { + +}; + + +#endif //INC_6502_EMULATOR_LDA_OPCODE_HANDLER_H diff --git a/src/opcode/handler/ldx-opcode-handler.cpp b/src/opcode/handler/ldx-opcode-handler.cpp index 109da32..c973bb4 100644 --- a/src/opcode/handler/ldx-opcode-handler.cpp +++ b/src/opcode/handler/ldx-opcode-handler.cpp @@ -3,25 +3,55 @@ const uint8_t LdxOpcodeHandler::IMMEDIATE; const uint8_t LdxOpcodeHandler::ZERO_PAGE; -const uint8_t LdxOpcodeHandler::ZERO_PAGE_X; +const uint8_t LdxOpcodeHandler::ZERO_PAGE_Y; 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(); + auto x_reg = reg_man->get_x_index(); switch (opcode) { case IMMEDIATE: - accumulator->set_value(byte2); + x_reg->set_value(byte2); move_program_counter(2); break; + case ZERO_PAGE: + x_reg->set_value(memory->get_byte_at(byte2)); + move_program_counter(2); + break; + + case ZERO_PAGE_Y: { + uint16_t address = byte2 + reg_man->get_y_index()->get_value(); + + x_reg->set_value(memory->get_byte_at(address)); + move_program_counter(2); + break; + } + + case ABSOLUTE: { + auto low = byte2; + auto high = program->get_byte_at(instructionIndex + 2); + uint16_t address = (high << 8) + low; + + x_reg->set_value(memory->get_byte_at(address)); + move_program_counter(3); + break; + } + + case ABSOLUTE_Y: { + auto low = byte2; + auto high = program->get_byte_at(instructionIndex + 2); + uint16_t address = (high << 8) + low + reg_man->get_y_index()->get_value(); + + x_reg->set_value(memory->get_byte_at(address)); + move_program_counter(3); + break; + } + default: stringstream stream; stream << "Unexpected opcode 0x" << hex << opcode; diff --git a/src/opcode/handler/ldx-opcode-handler.h b/src/opcode/handler/ldx-opcode-handler.h index dff22e1..ddaf6da 100644 --- a/src/opcode/handler/ldx-opcode-handler.h +++ b/src/opcode/handler/ldx-opcode-handler.h @@ -6,25 +6,20 @@ 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; + static const uint8_t IMMEDIATE = 0xA2; + static const uint8_t ZERO_PAGE = 0xA6; + static const uint8_t ZERO_PAGE_Y = 0xB6; + static const uint8_t ABSOLUTE = 0xAE; + static const uint8_t ABSOLUTE_Y = 0xBE; - explicit LdxOpcodeHandler(shared_ptr program, shared_ptr reg_man) : - OpcodeHandler(program, reg_man) { + explicit LdxOpcodeHandler(shared_ptr program, shared_ptr reg_man, + shared_ptr memory) : + OpcodeHandler(program, reg_man, memory) { handled_opcodes->push_back(IMMEDIATE); handled_opcodes->push_back(ZERO_PAGE); - handled_opcodes->push_back(ZERO_PAGE_X); + handled_opcodes->push_back(ZERO_PAGE_Y); 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; diff --git a/src/opcode/handler/register-opcode-handler.h b/src/opcode/handler/register-opcode-handler.h index 2d1892a..0d30075 100644 --- a/src/opcode/handler/register-opcode-handler.h +++ b/src/opcode/handler/register-opcode-handler.h @@ -15,8 +15,9 @@ public: 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) { + explicit RegisterOpcodeHandler(shared_ptr program, shared_ptr reg_man, + shared_ptr memory) : + OpcodeHandler(program, reg_man, memory) { handled_opcodes->push_back(TAX); handled_opcodes->push_back(TXA); handled_opcodes->push_back(DEX); diff --git a/src/opcode/handler/stack-opcode-handler.cpp b/src/opcode/handler/stack-opcode-handler.cpp new file mode 100644 index 0000000..e90e50f --- /dev/null +++ b/src/opcode/handler/stack-opcode-handler.cpp @@ -0,0 +1,43 @@ +#include "stack-opcode-handler.h" +#include +#include + +const uint8_t StackOpcodeHandler::PHA; +const uint8_t StackOpcodeHandler::PLA; +const uint8_t StackOpcodeHandler::PHP; +const uint8_t StackOpcodeHandler::PLP; + +void StackOpcodeHandler::execute() { + auto instructionIndex = reg_man->get_program_counter()->get_value(); + auto opcode = program->get_byte_at(instructionIndex); + + switch (opcode) { + case PHA: + memory->stack_push(reg_man->get_accumulator()->get_value()); + break; + + case PLA: + reg_man->get_accumulator()->set_value(memory->stack_pop()); + break; + + case PHP: { + ulong long_data = reg_man->get_status_register()->get_value().to_ulong(); + auto data = static_cast(long_data); + memory->stack_push(data); + break; + } + + case PLP: { + auto data = memory->stack_pop(); + reg_man->get_status_register()->set_value(bitset<8>(data)); + 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/stack-opcode-handler.h b/src/opcode/handler/stack-opcode-handler.h new file mode 100644 index 0000000..cb8a7f1 --- /dev/null +++ b/src/opcode/handler/stack-opcode-handler.h @@ -0,0 +1,27 @@ +#ifndef INC_6502_EMULATOR_STACK_OPCODE_HANDLER_H +#define INC_6502_EMULATOR_STACK_OPCODE_HANDLER_H + +#include "../../program.h" +#include "../opcode-handler.h" + +class StackOpcodeHandler : public OpcodeHandler { +public: + static const uint8_t PHA = 0x48; + static const uint8_t PLA = 0x68; + static const uint8_t PHP = 0x08; + static const uint8_t PLP = 0x28; + + explicit StackOpcodeHandler(shared_ptr program, shared_ptr reg_man, + shared_ptr memory) : + OpcodeHandler(program, reg_man, memory) { + handled_opcodes->push_back(PHA); + handled_opcodes->push_back(PLA); + handled_opcodes->push_back(PHP); + handled_opcodes->push_back(PLP); + } + + virtual void execute() override; +}; + + +#endif //INC_6502_EMULATOR_STACK_OPCODE_HANDLER_H diff --git a/src/opcode/opcode-handler-directory.cpp b/src/opcode/opcode-handler-directory.cpp index 4d41717..f531720 100644 --- a/src/opcode/opcode-handler-directory.cpp +++ b/src/opcode/opcode-handler-directory.cpp @@ -2,16 +2,18 @@ #include "handler/flag-opcode-handler.h" #include "handler/register-opcode-handler.h" #include "handler/ldx-opcode-handler.h" +#include "handler/stack-opcode-handler.h" #include #include -OpcodeHandlerDirectory::OpcodeHandlerDirectory(shared_ptr program, shared_ptr reg_man) { +OpcodeHandlerDirectory::OpcodeHandlerDirectory(shared_ptr program, shared_ptr reg_man, shared_ptr memory) { handlers = make_unique>>(); - register_handler(make_shared(program, reg_man)); - register_handler(make_shared(program, reg_man)); - register_handler(make_shared(program, reg_man)); + register_handler(make_shared(FlagOpcodeHandler(program, reg_man, memory))); + register_handler(make_shared(RegisterOpcodeHandler(program, reg_man, memory))); + register_handler(make_shared(LdxOpcodeHandler(program, reg_man, memory))); + register_handler(make_shared(StackOpcodeHandler(program, reg_man, memory))); } void OpcodeHandlerDirectory::register_handler(shared_ptr handler) { diff --git a/src/opcode/opcode-handler-directory.h b/src/opcode/opcode-handler-directory.h index df2cf36..3695e54 100644 --- a/src/opcode/opcode-handler-directory.h +++ b/src/opcode/opcode-handler-directory.h @@ -2,6 +2,7 @@ #define INC_6502_EMULATOR_OPCODE_HANDLER_DIRECTORY_H #include "../program.h" +#include "../memory.h" #include "opcode-handler.h" #include #include @@ -11,7 +12,7 @@ using namespace std; class OpcodeHandlerDirectory { public: - explicit OpcodeHandlerDirectory(shared_ptr program, shared_ptr reg_man); + explicit OpcodeHandlerDirectory(shared_ptr program, shared_ptr reg_man, shared_ptr memory); shared_ptr get_handler(uint8_t opcode); private: diff --git a/src/opcode/opcode-handler.cpp b/src/opcode/opcode-handler.cpp index 3d80203..f686f47 100644 --- a/src/opcode/opcode-handler.cpp +++ b/src/opcode/opcode-handler.cpp @@ -1,9 +1,10 @@ -#include "../program.h" #include "opcode-handler.h" -OpcodeHandler::OpcodeHandler(shared_ptr program, shared_ptr reg_man) { +OpcodeHandler::OpcodeHandler(shared_ptr program, shared_ptr reg_man, + shared_ptr memory) { this->program = program; this->reg_man = reg_man; + this->memory = memory; } void OpcodeHandler::move_program_counter(uint8_t forward_by) { diff --git a/src/opcode/opcode-handler.h b/src/opcode/opcode-handler.h index b7f7e88..9d3afcf 100644 --- a/src/opcode/opcode-handler.h +++ b/src/opcode/opcode-handler.h @@ -2,6 +2,7 @@ #define INC_6502_EMULATOR_OPCODE_HANDLER_H #include "../program.h" +#include "../memory.h" #include "../register/register-manager.h" #include #include @@ -10,7 +11,7 @@ using namespace std; class OpcodeHandler { public: - OpcodeHandler(shared_ptr program, shared_ptr reg_man); + OpcodeHandler(shared_ptr program, shared_ptr reg_man, shared_ptr memory); shared_ptr> get_handled_opcodes() { return handled_opcodes; } @@ -19,6 +20,7 @@ public: protected: shared_ptr> handled_opcodes = make_shared>(); shared_ptr program; + shared_ptr memory; shared_ptr reg_man; void move_program_counter(uint8_t forward_by); diff --git a/src/stack.cpp b/src/stack.cpp new file mode 100644 index 0000000..21dde6d --- /dev/null +++ b/src/stack.cpp @@ -0,0 +1,26 @@ +#include "stack.h" + +#include + +const uint8_t Stack::MAX_SIZE; + +Stack::Stack() { + data = make_unique>(stack()); +} + +void Stack::push(uint8_t byte) { + if (data->size() >= MAX_SIZE) + throw runtime_error("Stack overflow"); + + data->push(byte); +} + +uint8_t Stack::pop() { + if (data->empty()) + throw runtime_error("Stack is empty"); + + auto result = data->top(); + data->pop(); + + return result; +} \ No newline at end of file diff --git a/src/stack.h b/src/stack.h new file mode 100644 index 0000000..4ac5204 --- /dev/null +++ b/src/stack.h @@ -0,0 +1,22 @@ +#ifndef INC_6502_EMULATOR_STACK_H +#define INC_6502_EMULATOR_STACK_H + +#include +#include + +using namespace std; + +class Stack { +public: + explicit Stack(); + + void push(uint8_t byte); + uint8_t pop(); + +private: + static const uint8_t MAX_SIZE = 0xFF; + unique_ptr> data; +}; + + +#endif //INC_6502_EMULATOR_STACK_H diff --git a/test/instruction-executor-test.cpp b/test/instruction-executor-test.cpp index 6d7e22a..9a17411 100644 --- a/test/instruction-executor-test.cpp +++ b/test/instruction-executor-test.cpp @@ -22,7 +22,8 @@ TEST(InstructionExecutor, Execute) { CLC }); auto program = make_shared(Program(code)); - auto opcode_handler_dir = make_shared(OpcodeHandlerDirectory(program, reg_man)); + auto memory = make_shared(Memory()); + auto opcode_handler_dir = make_shared(OpcodeHandlerDirectory(program, reg_man, memory)); auto executor = make_unique(InstructionExecutor(program, reg_man, opcode_handler_dir)); executor->execute(); diff --git a/test/ldx-opcode-handler-test.cpp b/test/ldx-opcode-handler-test.cpp new file mode 100644 index 0000000..322a446 --- /dev/null +++ b/test/ldx-opcode-handler-test.cpp @@ -0,0 +1,106 @@ +#include "gtest/gtest.h" +#include "../src/machine.h" +#include "../src/program.h" +#include "../src/opcode/handler/stack-opcode-handler.h" + +#include +#include + +using namespace std; + +const uint8_t IMMEDIATE = 0xA2; +const uint8_t ZERO_PAGE = 0xA6; +const uint8_t ZERO_PAGE_Y = 0xB6; +const uint8_t ABSOLUTE = 0xAE; +const uint8_t ABSOLUTE_Y = 0xBE; + +unique_ptr ldxt_get_machine(shared_ptr> code) { + auto program = make_shared(Program(code)); + auto machine = make_unique(Machine(program)); + return machine; +} + +TEST(LdxOpcodeHandler, Immediate) { + auto code = make_shared>(vector{IMMEDIATE, 36}); + auto machine = ldxt_get_machine(code); + auto reg_man = machine->get_reg_man(); + auto x_reg = reg_man->get_x_index(); + + ASSERT_EQ(0, x_reg->get_value()); + + machine->execute(); + + ASSERT_EQ(36, x_reg->get_value()); +} + +TEST(LdxOpcodeHandler, ZeroPage) { + auto code = make_shared>(vector{ZERO_PAGE, 123}); + auto machine = ldxt_get_machine(code); + auto reg_man = machine->get_reg_man(); + auto x_reg = reg_man->get_x_index(); + + ASSERT_EQ(0, x_reg->get_value()); + + machine->get_memory()->set_byte_at(123, 78); + machine->execute(); + + ASSERT_EQ(78, x_reg->get_value()); +} + +TEST(LdxOpcodeHandler, ZeroPageY) { + auto code = make_shared>(vector{ZERO_PAGE_Y, 250}); + auto machine = ldxt_get_machine(code); + auto reg_man = machine->get_reg_man(); + auto x_reg = reg_man->get_x_index(); + auto y_reg = reg_man->get_y_index(); + + ASSERT_EQ(0, x_reg->get_value()); + ASSERT_EQ(0, y_reg->get_value()); + machine->get_memory()->set_byte_at(250, 255); + machine->execute(); + ASSERT_EQ(255, x_reg->get_value()); + + reg_man->get_y_index()->set_value(100); + reg_man->get_program_counter()->set_value(0); + machine->execute(); + ASSERT_EQ(0, x_reg->get_value()); + + machine->get_memory()->set_byte_at(350, 233); + reg_man->get_program_counter()->set_value(0); + machine->execute(); + ASSERT_EQ(233, x_reg->get_value()); +} + +TEST(LdxOpcodeHandler, Absolute) { + auto code = make_shared>(vector{ABSOLUTE, 0xe5, 0xff}); + auto machine = ldxt_get_machine(code); + auto reg_man = machine->get_reg_man(); + auto x_reg = reg_man->get_x_index(); + + ASSERT_EQ(0, x_reg->get_value()); + + machine->get_memory()->set_byte_at(0xffe5, 0xde); + machine->execute(); + + ASSERT_EQ(0xde, x_reg->get_value()); +} + +TEST(LdxOpcodeHandler, AbsoluteY) { + auto code = make_shared>(vector{ABSOLUTE_Y, 0x33, 0xc2}); + auto machine = ldxt_get_machine(code); + auto reg_man = machine->get_reg_man(); + auto x_reg = reg_man->get_x_index(); + auto y_reg = reg_man->get_y_index(); + + ASSERT_EQ(0, x_reg->get_value()); + ASSERT_EQ(0, y_reg->get_value()); + machine->get_memory()->set_byte_at(0xc233, 0xc8); + machine->execute(); + ASSERT_EQ(0xc8, x_reg->get_value()); + + y_reg->set_value(0x55); + machine->get_memory()->set_byte_at(0xc233 + 0x55, 0xab); + reg_man->get_program_counter()->set_value(0); + machine->execute(); + ASSERT_EQ(0xab, x_reg->get_value()); +} \ No newline at end of file diff --git a/test/stack-opcode-handler-test.cpp b/test/stack-opcode-handler-test.cpp new file mode 100644 index 0000000..1d6a589 --- /dev/null +++ b/test/stack-opcode-handler-test.cpp @@ -0,0 +1,112 @@ +#include "gtest/gtest.h" +#include "../src/machine.h" +#include "../src/program.h" +#include "../src/opcode/handler/stack-opcode-handler.h" + +#include +#include + +using namespace std; + +const uint8_t PHA = 0x48; +const uint8_t PLA = 0x68; +const uint8_t PHP = 0x08; +const uint8_t PLP = 0x28; + +unique_ptr st_get_machine(shared_ptr> code) { + auto program = make_shared(Program(code)); + auto machine = make_unique(Machine(program)); + return machine; +} + +TEST(StackOpcodeHandler, PushAccumulator) { + auto code = make_shared>(vector{PHA}); + auto machine = st_get_machine(code); + auto reg_man = machine->get_reg_man(); + + reg_man->get_accumulator()->set_value(0x8b); + ASSERT_EQ(0x8b, reg_man->get_accumulator()->get_value()); + + // stack is empty + ASSERT_THROW(machine->get_memory()->stack_pop(), runtime_error); + + machine->execute(); + + ASSERT_EQ(0x8b, reg_man->get_accumulator()->get_value()); + auto stack_value = machine->get_memory()->stack_pop(); + + ASSERT_EQ(0x8b, stack_value); + + // stack is empty again + ASSERT_THROW(machine->get_memory()->stack_pop(), runtime_error); +} + +TEST(StackOpcodeHandler, PullAccumulator) { + auto code = make_shared>(vector{PLA}); + auto machine = st_get_machine(code); + auto reg_man = machine->get_reg_man(); + + machine->get_memory()->stack_push(1); + machine->get_memory()->stack_push(2); + machine->get_memory()->stack_push(3); + + machine->execute(); + ASSERT_EQ(3, reg_man->get_accumulator()->get_value()); + + reg_man->get_program_counter()->set_value(0); + machine->execute(); + ASSERT_EQ(2, reg_man->get_accumulator()->get_value()); + + reg_man->get_program_counter()->set_value(0); + machine->execute(); + ASSERT_EQ(1, reg_man->get_accumulator()->get_value()); + + // stack is empty + ASSERT_THROW(machine->get_memory()->stack_pop(), runtime_error); +} + +TEST(StackOpcodeHandler, PushStatus) { + auto code = make_shared>(vector{PHP}); + auto machine = st_get_machine(code); + auto reg_man = machine->get_reg_man(); + + reg_man->get_status_register()->set_value(255); + ASSERT_EQ(255, reg_man->get_status_register()->get_value().to_ulong()); + + // stack is empty + ASSERT_THROW(machine->get_memory()->stack_pop(), runtime_error); + + machine->execute(); + + ASSERT_EQ(255, reg_man->get_status_register()->get_value().to_ulong()); + auto stack_value = machine->get_memory()->stack_pop(); + + ASSERT_EQ(255, stack_value); + + // stack is empty again + ASSERT_THROW(machine->get_memory()->stack_pop(), runtime_error); +} + +TEST(StackOpcodeHandler, PullStatus) { + auto code = make_shared>(vector{PLP}); + auto machine = st_get_machine(code); + auto reg_man = machine->get_reg_man(); + + machine->get_memory()->stack_push(8); + machine->get_memory()->stack_push(16); + machine->get_memory()->stack_push(32); + + machine->execute(); + ASSERT_EQ(32, reg_man->get_status_register()->get_value().to_ulong()); + + reg_man->get_program_counter()->set_value(0); + machine->execute(); + ASSERT_EQ(16, reg_man->get_status_register()->get_value().to_ulong()); + + reg_man->get_program_counter()->set_value(0); + machine->execute(); + ASSERT_EQ(8, reg_man->get_status_register()->get_value().to_ulong()); + + // stack is empty + ASSERT_THROW(machine->get_memory()->stack_pop(), runtime_error); +} \ No newline at end of file