diff --git a/src/machine/program-counter.cpp b/src/machine/program-counter.cpp index 8082d95..7331d9b 100644 --- a/src/machine/program-counter.cpp +++ b/src/machine/program-counter.cpp @@ -4,4 +4,8 @@ namespace emu_6502 { void ProgramCounter::inc() { value += 1; } + + void ProgramCounter::add(int8_t offset) { + value += offset; + } } \ No newline at end of file diff --git a/src/machine/program-counter.h b/src/machine/program-counter.h index 595dcb5..8a971d4 100644 --- a/src/machine/program-counter.h +++ b/src/machine/program-counter.h @@ -12,6 +12,7 @@ namespace emu_6502 { ProgramCounter& operator=(const ProgramCounter&) = delete; void inc(); + void add(int8_t offset); }; } diff --git a/src/opcode/handler/branch-opcode-handler-container.cpp b/src/opcode/handler/branch-opcode-handler-container.cpp new file mode 100644 index 0000000..6154d9f --- /dev/null +++ b/src/opcode/handler/branch-opcode-handler-container.cpp @@ -0,0 +1,65 @@ +#include "branch-opcode-handler-container.h" + +namespace emu_6502 { + BranchOpcodeHandlerContainer::BranchOpcodeHandlerContainer() { + handlers.insert({Op::BCS, [this](Machine& machine) { bcs(machine); }}); + handlers.insert({Op::BCC, [this](Machine& machine) { bcc(machine); }}); + + handlers.insert({Op::BEQ, [this](Machine& machine) { beq(machine); }}); + handlers.insert({Op::BNE, [this](Machine& machine) { bne(machine); }}); + + handlers.insert({Op::BPL, [this](Machine& machine) { bpl(machine); }}); + handlers.insert({Op::BMI, [this](Machine& machine) { bmi(machine); }}); + + handlers.insert({Op::BVS, [this](Machine& machine) { bvs(machine); }}); + handlers.insert({Op::BVC, [this](Machine& machine) { bvc(machine); }}); + } + + void BranchOpcodeHandlerContainer::bcs(Machine& machine) { + auto jump = machine.read_program_byte(); + if (machine.get_cpu().get_ps().is_carry_set()) + machine.get_cpu().get_pc().add(jump); + } + + void BranchOpcodeHandlerContainer::bcc(Machine& machine) { + auto jump = machine.read_program_byte(); + if (!machine.get_cpu().get_ps().is_carry_set()) + machine.get_cpu().get_pc().add(jump); + } + + void BranchOpcodeHandlerContainer::beq(Machine& machine) { + auto jump = machine.read_program_byte(); + if (machine.get_cpu().get_ps().is_zero_set()) + machine.get_cpu().get_pc().add(jump); + } + + void BranchOpcodeHandlerContainer::bne(Machine& machine) { + auto jump = machine.read_program_byte(); + if (!machine.get_cpu().get_ps().is_zero_set()) + machine.get_cpu().get_pc().add(jump); + } + + void BranchOpcodeHandlerContainer::bpl(Machine& machine) { + auto jump = machine.read_program_byte(); + if (!machine.get_cpu().get_ps().is_negative_set()) + machine.get_cpu().get_pc().add(jump); + } + + void BranchOpcodeHandlerContainer::bmi(Machine& machine) { + auto jump = machine.read_program_byte(); + if (machine.get_cpu().get_ps().is_negative_set()) + machine.get_cpu().get_pc().add(jump); + } + + void BranchOpcodeHandlerContainer::bvs(Machine& machine) { + auto jump = machine.read_program_byte(); + if (machine.get_cpu().get_ps().is_overflow_set()) + machine.get_cpu().get_pc().add(jump); + } + + void BranchOpcodeHandlerContainer::bvc(Machine& machine) { + auto jump = machine.read_program_byte(); + if (!machine.get_cpu().get_ps().is_overflow_set()) + machine.get_cpu().get_pc().add(jump); + } +} diff --git a/src/opcode/handler/branch-opcode-handler-container.h b/src/opcode/handler/branch-opcode-handler-container.h new file mode 100644 index 0000000..51a8461 --- /dev/null +++ b/src/opcode/handler/branch-opcode-handler-container.h @@ -0,0 +1,42 @@ +#ifndef INC_6502_EMULATOR_BRANCH_OPCODE_HANDLER_CONTAINER_H +#define INC_6502_EMULATOR_BRANCH_OPCODE_HANDLER_CONTAINER_H + +#include "opcode-handler-container.h" + +namespace emu_6502 { + class BranchOpcodeHandlerContainer : public OpcodeHandlerContainer { + private: + enum Op { + BCS = 0xB0, + BCC = 0x90, + + BEQ = 0xF0, + BNE = 0xD0, + + BPL = 0x10, + BMI = 0x30, + + BVS = 0x70, + BVC = 0x50 + }; + + void bcs(Machine& machine); + void bcc(Machine& machine); + + void beq(Machine& machine); + void bne(Machine& machine); + + void bpl(Machine& machine); + void bmi(Machine& machine); + + void bvs(Machine& machine); + void bvc(Machine& machine); + + public: + BranchOpcodeHandlerContainer(); + BranchOpcodeHandlerContainer(const BranchOpcodeHandlerContainer&) = delete; + BranchOpcodeHandlerContainer& operator=(BranchOpcodeHandlerContainer&) = delete; + }; +} + +#endif //INC_6502_EMULATOR_BRANCH_OPCODE_HANDLER_CONTAINER_H diff --git a/src/opcode/opcode-handler-directory.cpp b/src/opcode/opcode-handler-directory.cpp index 9074e30..accd35c 100644 --- a/src/opcode/opcode-handler-directory.cpp +++ b/src/opcode/opcode-handler-directory.cpp @@ -7,6 +7,7 @@ #include "handler/status-opcode-handler-container.h" #include "handler/compare-opcode-handler-container.h" #include "handler/stack-opcode-handler-container.h" +#include "handler/branch-opcode-handler-container.h" #include "../utils.h" namespace emu_6502 { @@ -20,6 +21,7 @@ namespace emu_6502 { handler_containers.push_back(make_unique()); handler_containers.push_back(make_unique()); handler_containers.push_back(make_unique()); + handler_containers.push_back(make_unique()); init_handlers(); } diff --git a/test/branch-opcode-handler-test.cpp b/test/branch-opcode-handler-test.cpp new file mode 100644 index 0000000..8f9d286 --- /dev/null +++ b/test/branch-opcode-handler-test.cpp @@ -0,0 +1,273 @@ +#include "gtest/gtest.h" +#include "test-utils.h" + +using namespace std; +using namespace emu_6502; + +const uint8_t BCS = 0xB0; +const uint8_t BCC = 0x90; + +const uint8_t BEQ = 0xF0; +const uint8_t BNE = 0xD0; + +const uint8_t BPL = 0x10; +const uint8_t BMI = 0x30; + +const uint8_t BVS = 0x70; +const uint8_t BVC = 0x50; + +TEST(BranchOpcodeHandlerContainer, BCS_Take_Forward) { + auto machine = create_machine({BCS, 0x23}); + machine->get_cpu().get_ps().set_carry(true); + machine->execute(); + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2 + 0x23, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BCS_Take_Backward) { + auto machine = create_machine({BCS, 0xF0}); + machine->get_cpu().get_ps().set_carry(true); + + try { + machine->execute(); + } + catch (exception) { + } + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2 - 0xf, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BCS_NotTake) { + auto machine = create_machine({BCS, 0x23}); + machine->get_cpu().get_ps().set_carry(false); + machine->execute(); + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BCC_Take_Forward) { + auto machine = create_machine({BCC, 0x23}); + machine->get_cpu().get_ps().set_carry(false); + machine->execute(); + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2 + 0x23, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BCC_Take_Backward) { + auto machine = create_machine({BCC, 0xF0}); + machine->get_cpu().get_ps().set_carry(false); + + try { + machine->execute(); + } + catch (exception) { + } + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2 - 0xf, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BCC_NotTake) { + auto machine = create_machine({BCC, 0x23}); + machine->get_cpu().get_ps().set_carry(true); + machine->execute(); + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BEQ_Take_Forward) { + auto machine = create_machine({BEQ, 0x23}); + machine->get_cpu().get_ps().set_zero(true); + machine->execute(); + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2 + 0x23, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BEQ_Take_Backward) { + auto machine = create_machine({BEQ, 0xF0}); + machine->get_cpu().get_ps().set_zero(true); + + try { + machine->execute(); + } + catch (exception) { + } + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2 - 0xf, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BEQ_NotTake) { + auto machine = create_machine({BEQ, 0x23}); + machine->get_cpu().get_ps().set_zero(false); + machine->execute(); + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BNE_Take_Forward) { + auto machine = create_machine({BNE, 0x23}); + machine->get_cpu().get_ps().set_zero(false); + machine->execute(); + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2 + 0x23, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BNE_Take_Backward) { + auto machine = create_machine({BNE, 0xF0}); + machine->get_cpu().get_ps().set_zero(false); + + try { + machine->execute(); + } + catch (exception) { + } + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2 - 0xf, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BNE_NotTake) { + auto machine = create_machine({BNE, 0x23}); + machine->get_cpu().get_ps().set_zero(true); + machine->execute(); + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BPL_Take_Forward) { + auto machine = create_machine({BPL, 0x23}); + machine->get_cpu().get_ps().set_negative(false); + machine->execute(); + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2 + 0x23, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BPL_Take_Backward) { + auto machine = create_machine({BPL, 0xF0}); + machine->get_cpu().get_ps().set_negative(false); + + try { + machine->execute(); + } + catch (exception) { + } + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2 - 0xf, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BPL_NotTake) { + auto machine = create_machine({BPL, 0x23}); + machine->get_cpu().get_ps().set_negative(true); + machine->execute(); + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BMI_Take_Forward) { + auto machine = create_machine({BMI, 0x23}); + machine->get_cpu().get_ps().set_negative(true); + machine->execute(); + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2 + 0x23, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BMI_Take_Backward) { + auto machine = create_machine({BMI, 0xF0}); + machine->get_cpu().get_ps().set_negative(true); + + try { + machine->execute(); + } + catch (exception) { + } + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2 - 0xf, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BMI_NotTake) { + auto machine = create_machine({BMI, 0x23}); + machine->get_cpu().get_ps().set_negative(false); + machine->execute(); + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BVS_Take_Forward) { + auto machine = create_machine({BVS, 0x23}); + machine->get_cpu().get_ps().set_overflow(true); + machine->execute(); + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2 + 0x23, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BVS_Take_Backward) { + auto machine = create_machine({BVS, 0xF0}); + machine->get_cpu().get_ps().set_overflow(true); + + try { + machine->execute(); + } + catch (exception) { + } + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2 - 0xf, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BVS_NotTake) { + auto machine = create_machine({BVS, 0x23}); + machine->get_cpu().get_ps().set_overflow(false); + machine->execute(); + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BVC_Take_Forward) { + auto machine = create_machine({BVC, 0x23}); + machine->get_cpu().get_ps().set_overflow(false); + machine->execute(); + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2 + 0x23, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BVC_Take_Backward) { + auto machine = create_machine({BVC, 0xF0}); + machine->get_cpu().get_ps().set_overflow(false); + + try { + machine->execute(); + } + catch (exception) { + } + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2 - 0xf, machine->get_cpu().get_pc().get_value()); +} + +TEST(BranchOpcodeHandlerContainer, BVC_NotTake) { + auto machine = create_machine({BVC, 0x23}); + machine->get_cpu().get_ps().set_overflow(true); + machine->execute(); + + // + 2 to account for op bytes + ASSERT_EQ(CODE_LOAD_ADDR + 2, machine->get_cpu().get_pc().get_value()); +} diff --git a/test/test-utils.cpp b/test/test-utils.cpp index 13ae7d8..c5ca35f 100644 --- a/test/test-utils.cpp +++ b/test/test-utils.cpp @@ -2,7 +2,7 @@ unique_ptr create_machine(vector code) { auto machine = make_unique(); - machine->load(code, 0x600); + machine->load(code, CODE_LOAD_ADDR); return machine; } diff --git a/test/test-utils.h b/test/test-utils.h index df9d22e..01e476b 100644 --- a/test/test-utils.h +++ b/test/test-utils.h @@ -10,6 +10,8 @@ using namespace std; using namespace emu_6502; +const uint16_t CODE_LOAD_ADDR = 0x600; + struct RegisterFlagSet { bool carry; bool zero;