From cab2cec9ffe658c311829c516400a30d71e12da6 Mon Sep 17 00:00:00 2001 From: Tony Di Nucci Date: Thu, 18 Apr 2019 01:09:44 +0100 Subject: [PATCH] Add "adc" instructions --- src/machine/machine.cpp | 2 +- src/machine/machine.h | 2 +- src/machine/register.h | 4 +- src/machine/status-register.h | 2 +- .../maths-opcode-handler-container.cpp | 124 ++++++++++++ .../handler/maths-opcode-handler-container.h | 81 ++++++++ src/opcode/opcode-handler-directory.cpp | 2 + src/utils.cpp | 12 ++ src/utils.h | 2 + test/maths-opcode-handler-test.cpp | 177 ++++++++++++++++++ 10 files changed, 403 insertions(+), 5 deletions(-) create mode 100644 src/opcode/handler/maths-opcode-handler-container.cpp create mode 100644 src/opcode/handler/maths-opcode-handler-container.h create mode 100644 test/maths-opcode-handler-test.cpp diff --git a/src/machine/machine.cpp b/src/machine/machine.cpp index f6d3bd7..4dbcd24 100644 --- a/src/machine/machine.cpp +++ b/src/machine/machine.cpp @@ -92,7 +92,7 @@ namespace emu_6502 { pimpl->load(program, load_at); } - bool Machine::is_eop() { + bool Machine::is_eop() const { return pimpl->is_eop(); } diff --git a/src/machine/machine.h b/src/machine/machine.h index 5fa2a9a..b63980c 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -26,7 +26,7 @@ namespace emu_6502 { Memory& get_memory(); Stack& get_stack(); - bool is_eop(); + bool is_eop() const; uint8_t read_program_byte(); void load(const vector& program, uint16_t load_at); void execute(); diff --git a/src/machine/register.h b/src/machine/register.h index 9c0123c..5887ef6 100644 --- a/src/machine/register.h +++ b/src/machine/register.h @@ -16,9 +16,9 @@ namespace emu_6502 { T value; public: - Register(string name); + explicit Register(string name); Register(const Register&) = delete; - Register& operator=(const Register&) = delete; + virtual Register& operator=(const Register&) = delete; const string& get_name() const; diff --git a/src/machine/status-register.h b/src/machine/status-register.h index 9514a67..b44a567 100644 --- a/src/machine/status-register.h +++ b/src/machine/status-register.h @@ -24,7 +24,7 @@ namespace emu_6502 { StatusRegister() : Register>("PS") {} StatusRegister(const StatusRegister&) = delete; - StatusRegister& operator=(const StatusRegister&) = delete; + virtual StatusRegister& operator=(const StatusRegister&) = delete; bool is_carry_set() const; bool is_zero_set() const; diff --git a/src/opcode/handler/maths-opcode-handler-container.cpp b/src/opcode/handler/maths-opcode-handler-container.cpp new file mode 100644 index 0000000..e2cec86 --- /dev/null +++ b/src/opcode/handler/maths-opcode-handler-container.cpp @@ -0,0 +1,124 @@ +#include "maths-opcode-handler-container.h" +#include "../../utils.h" + +namespace emu_6502 { + MathsOpcodeHandlerContainer::MathsOpcodeHandlerContainer() : OpcodeHandlerContainer() { + handlers.insert({Op::ADC_IMM, [this](Machine& machine) { adc_imm(machine); }}); + handlers.insert({Op::ADC_ZPG, [this](Machine& machine) { adc_zpg(machine); }}); + handlers.insert({Op::ADC_ZPG_X, [this](Machine& machine) { adc_zpg_x(machine); }}); + handlers.insert({Op::ADC_ABS, [this](Machine& machine) { adc_abs(machine); }}); + handlers.insert({Op::ADC_ABS_X, [this](Machine& machine) { adc_abs_x(machine); }}); + handlers.insert({Op::ADC_ABS_Y, [this](Machine& machine) { adc_abs_y(machine); }}); + handlers.insert({Op::ADC_IND_X, [this](Machine& machine) { adc_ind_x(machine); }}); + handlers.insert({Op::ADC_IND_Y, [this](Machine& machine) { adc_ind_y(machine); }}); + } + + void MathsOpcodeHandlerContainer::adc(Machine& machine, uint8_t value){ + auto& cpu = machine.get_cpu(); + auto init_a = cpu.get_a().get_value(); + + uint16_t result = value + init_a; + if (cpu.get_ps().is_carry_set()) + result += 1; + + cpu.get_a().set_value(result); // will chop off bit 8 if set + auto a = cpu.get_a().get_value(); + + // 'a' may be 0 if the result wasn't 0, i.e. the cary bit is set + cpu.get_ps().set_zero(a == 0); + cpu.get_ps().set_negative((a & 0x80) == 0x80); + cpu.get_ps().set_carry(result > 0xFF); + cpu.get_ps().set_overflow( + (value < 0x7F && init_a < 0x7F && a > 0x7F) || + (value > 0x7F && init_a > 0x7F && a < 0x7F)); + } + + void MathsOpcodeHandlerContainer::adc_imm(Machine& machine) { + adc(machine, machine.read_program_byte()); + } + + void MathsOpcodeHandlerContainer::adc_zpg(Machine& machine) { + auto addr = get_zpg_address(machine.read_program_byte()); + adc(machine, machine.get_memory().get_at(addr)); + } + + void MathsOpcodeHandlerContainer::adc_zpg_x(Machine& machine) { + auto addr = get_zpg_x_address(machine.read_program_byte(), machine.get_cpu()); + adc(machine, machine.get_memory().get_at(addr)); + } + + void MathsOpcodeHandlerContainer::adc_abs(Machine& machine) { + auto low = machine.read_program_byte(); + auto high = machine.read_program_byte(); + auto addr = get_abs_address(low, high); + adc(machine, machine.get_memory().get_at(addr)); + } + + void MathsOpcodeHandlerContainer::adc_abs_x(Machine& machine) { + auto addr = get_abs_x_address(machine, machine.get_cpu()); + adc(machine, machine.get_memory().get_at(addr)); + } + + void MathsOpcodeHandlerContainer::adc_abs_y(Machine& machine) { + auto addr = get_abs_y_address(machine, machine.get_cpu()); + adc(machine, machine.get_memory().get_at(addr)); + } + + void MathsOpcodeHandlerContainer::adc_ind_x(Machine& machine) { + auto addr = get_ind_x_address(machine.read_program_byte(), machine); + adc(machine, machine.get_memory().get_at(addr)); + } + + void MathsOpcodeHandlerContainer::adc_ind_y(Machine& machine) { + auto addr = get_ind_y_address(machine.read_program_byte(), machine); + adc(machine, machine.get_memory().get_at(addr)); + } + + void MathsOpcodeHandlerContainer::dec_zpg(Machine& machine) { + } + + void MathsOpcodeHandlerContainer::dec_zpg_x(Machine& machine) { + } + + void MathsOpcodeHandlerContainer::dec_abs(Machine& machine) { + } + + void MathsOpcodeHandlerContainer::dec_abs_x(Machine& machine) { + } + + void MathsOpcodeHandlerContainer::dex(Machine& machine) { + } + + void MathsOpcodeHandlerContainer::dey(Machine& machine) { + } + + void MathsOpcodeHandlerContainer::inx(Machine& machine) { + } + + void MathsOpcodeHandlerContainer::iny(Machine& machine) { + } + + void MathsOpcodeHandlerContainer::sbc_imm(Machine& machine) { + } + + void MathsOpcodeHandlerContainer::sbc_zpg(Machine& machine) { + } + + void MathsOpcodeHandlerContainer::sbc_zpg_x(Machine& machine) { + } + + void MathsOpcodeHandlerContainer::sbc_abs(Machine& machine) { + } + + void MathsOpcodeHandlerContainer::sbc_abs_x(Machine& machine) { + } + + void MathsOpcodeHandlerContainer::sbc_abs_y(Machine& machine) { + } + + void MathsOpcodeHandlerContainer::sbc_ind_x(Machine& machine) { + } + + void MathsOpcodeHandlerContainer::sbc_ind_y(Machine& machine) { + } +} diff --git a/src/opcode/handler/maths-opcode-handler-container.h b/src/opcode/handler/maths-opcode-handler-container.h new file mode 100644 index 0000000..6dd124e --- /dev/null +++ b/src/opcode/handler/maths-opcode-handler-container.h @@ -0,0 +1,81 @@ +#ifndef INC_6502_EMULATOR_MATHS_OPCODE_HANDLER_CONTAINER_H +#define INC_6502_EMULATOR_MATHS_OPCODE_HANDLER_CONTAINER_H + +#include "opcode-handler-container.h" + +namespace emu_6502 { + class MathsOpcodeHandlerContainer : public OpcodeHandlerContainer { + private: + enum Op { + ADC_IMM = 0x69, + ADC_ZPG = 0x65, + ADC_ZPG_X = 0x75, + ADC_ABS = 0x6D, + ADC_ABS_X = 0x7D, + ADC_ABS_Y = 0x79, + ADC_IND_X = 0x61, + ADC_IND_Y = 0x71, + + DEC_ZPG = 0xC6, + DEC_ZPG_X = 0xD6, + DEC_ABS = 0xCE, + DEC_ABS_X = 0xDE, + + INC_ZPG = 0xE6, + INC_ZPG_X = 0xF6, + INC_ABS = 0xEE, + INC_ABS_X = 0xFE, + + DEX = 0xCA, + DEY = 0x88, + INX = 0xE8, + INY = 0xC8, + + SBC_IMM = 0x0, + SBC_ZPG = 0x0, + SBC_ZPG_X = 0x0, + SBC_ABS = 0x0, + SBC_ABS_X = 0x0, + SBC_ABS_Y = 0x0, + SBC_IND_X = 0x0, + SBC_IND_Y = 0x0 + }; + + void adc(Machine& machine, uint8_t value); + void adc_imm(Machine& machine); + void adc_zpg(Machine& machine); + void adc_zpg_x(Machine& machine); + void adc_abs(Machine& machine); + void adc_abs_x(Machine& machine); + void adc_abs_y(Machine& machine); + void adc_ind_x(Machine& machine); + void adc_ind_y(Machine& machine); + + void dec_zpg(Machine& machine); + void dec_zpg_x(Machine& machine); + void dec_abs(Machine& machine); + void dec_abs_x(Machine& machine); + + void dex(Machine& machine); + void dey(Machine& machine); + void inx(Machine& machine); + void iny(Machine& machine); + + void sbc_imm(Machine& machine); + void sbc_zpg(Machine& machine); + void sbc_zpg_x(Machine& machine); + void sbc_abs(Machine& machine); + void sbc_abs_x(Machine& machine); + void sbc_abs_y(Machine& machine); + void sbc_ind_x(Machine& machine); + void sbc_ind_y(Machine& machine); + + public: + MathsOpcodeHandlerContainer(); + MathsOpcodeHandlerContainer(const MathsOpcodeHandlerContainer&) = delete; + MathsOpcodeHandlerContainer& operator=(const MathsOpcodeHandlerContainer&) = delete; + }; +} + + +#endif //INC_6502_EMULATOR_MATHS_OPCODE_HANDLER_CONTAINER_H diff --git a/src/opcode/opcode-handler-directory.cpp b/src/opcode/opcode-handler-directory.cpp index f7c200b..6d0487f 100644 --- a/src/opcode/opcode-handler-directory.cpp +++ b/src/opcode/opcode-handler-directory.cpp @@ -2,6 +2,7 @@ #include "handler/load-opcode-handler-container.h" #include "handler/store-opcode-handler-container.h" #include "handler/transfer-opcode-handler-container.h" +#include "handler/maths-opcode-handler-container.h" #include "../utils.h" namespace emu_6502 { @@ -10,6 +11,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/src/utils.cpp b/src/utils.cpp index 405e8ec..29f56c0 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -37,6 +37,18 @@ namespace emu_6502 { return address; } + uint16_t get_abs_x_address(Machine& machine, Cpu& cpu) { + auto low = machine.read_program_byte(); + auto high = machine.read_program_byte(); + return get_abs_x_address(low, high, machine.get_cpu()); + } + + uint16_t get_abs_y_address(Machine& machine, Cpu& cpu) { + auto low = machine.read_program_byte(); + auto high = machine.read_program_byte(); + return get_abs_y_address(low, high, machine.get_cpu()); + } + uint16_t get_ind_x_address(uint8_t offset, Machine& machine) { uint8_t paddress = machine.get_cpu().get_x().get_value() + offset; auto low = machine.get_memory().get_at(paddress); diff --git a/src/utils.h b/src/utils.h index 44ee4a0..f5e1d28 100644 --- a/src/utils.h +++ b/src/utils.h @@ -15,6 +15,8 @@ namespace emu_6502 { uint16_t get_abs_address(uint8_t low_byte, uint8_t high_byte); uint16_t get_abs_x_address(uint8_t low_byte, uint8_t high_byte, Cpu& cpu); uint16_t get_abs_y_address(uint8_t low_byte, uint8_t high_byte, Cpu& cpu); + uint16_t get_abs_x_address(Machine& machine, Cpu& cpu); + uint16_t get_abs_y_address(Machine& machine, Cpu& cpu); uint16_t get_ind_x_address(uint8_t offset, Machine& machine); uint16_t get_ind_y_address(uint8_t offset, Machine& machine); } diff --git a/test/maths-opcode-handler-test.cpp b/test/maths-opcode-handler-test.cpp new file mode 100644 index 0000000..1bdcc3a --- /dev/null +++ b/test/maths-opcode-handler-test.cpp @@ -0,0 +1,177 @@ +#include "gtest/gtest.h" +#include "test-utils.h" + +using namespace std; +using namespace emu_6502; + +const uint8_t ADC_IMM = 0x69; +const uint8_t ADC_ZPG = 0x65; +const uint8_t ADC_ZPG_X = 0x75; +const uint8_t ADC_ABS = 0x6D; +const uint8_t ADC_ABS_X = 0x7D; +const uint8_t ADC_ABS_Y = 0x79; +const uint8_t ADC_IND_X = 0x61; +const uint8_t ADC_IND_Y = 0x71; + +TEST(MathsOpcodeHandlerContainer, ADC_IMM) { + auto machine = create_machine({ADC_IMM, 36}); + machine->get_cpu().get_a().set_value(36); + machine->execute(); + + ASSERT_EQ(72, machine->get_cpu().get_a().get_value()); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(MathsOpcodeHandlerContainer, ADC_IMM_ZeroFlag) { + auto machine = create_machine({ADC_IMM, 0}); + machine->get_cpu().get_a().set_value(0); + machine->execute(); + + ASSERT_EQ(0, machine->get_cpu().get_a().get_value()); + + RegisterFlagSet flags{}; + flags.zero = true; + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags)); +} + +TEST(MathsOpcodeHandlerContainer, ADC_IMM_CarryFlag) { + auto machine = create_machine({ADC_IMM, 200}); + machine->get_cpu().get_a().set_value(72); + machine->execute(); + + ASSERT_EQ(16, machine->get_cpu().get_a().get_value()); + + RegisterFlagSet flags{}; + flags.carry = true; + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags)); +} + +TEST(MathsOpcodeHandlerContainer, ADC_IMM_ZeroAndCarryFlags) { + auto machine = create_machine({ADC_IMM, 0xFF}); + machine->get_cpu().get_a().set_value(1); + machine->execute(); + + ASSERT_EQ(0, machine->get_cpu().get_a().get_value()); + + RegisterFlagSet flags{}; + flags.zero = true; + flags.carry = true; + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags)); +} + +TEST(MathsOpcodeHandlerContainer, ADC_IMM_NegativeFlag) { + auto machine = create_machine({ADC_IMM, 200}); + machine->get_cpu().get_a().set_value(32); + machine->execute(); + + ASSERT_EQ(232, machine->get_cpu().get_a().get_value()); + + RegisterFlagSet flags{}; + flags.negative = true; + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags)); +} + +TEST(MathsOpcodeHandlerContainer, ADC_IMM_OverflowFlag_Postive) { + auto machine = create_machine({ADC_IMM, 120}); + machine->get_cpu().get_a().set_value(32); + machine->execute(); + + ASSERT_EQ(152, machine->get_cpu().get_a().get_value()); + + RegisterFlagSet flags{}; + flags.negative = true; + flags.overflow = true; + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags)); +} + +TEST(MathsOpcodeHandlerContainer, ADC_IMM_OverflowFlag_Negative) { + auto machine = create_machine({ADC_IMM, 208}); + machine->get_cpu().get_a().set_value(144); + machine->execute(); + + ASSERT_EQ(96, machine->get_cpu().get_a().get_value()); + + RegisterFlagSet flags{}; + flags.carry = true; + flags.overflow = true; + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags)); +} + +TEST(MathsOpcodeHandlerContainer, ADC_ZPG) { + auto machine = create_machine({ADC_ZPG, 0xf1}); + machine->get_cpu().get_a().set_value(36); + machine->get_memory().set_at(0xf1, 20); + machine->execute(); + + ASSERT_EQ(56, machine->get_cpu().get_a().get_value()); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(MathsOpcodeHandlerContainer, ADC_ZPG_X) { + auto machine = create_machine({ADC_ZPG_X, 0xf1}); + machine->get_cpu().get_a().set_value(36); + machine->get_cpu().get_x().set_value(5); + machine->get_memory().set_at(0xf6, 20); + machine->execute(); + + ASSERT_EQ(56, machine->get_cpu().get_a().get_value()); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(MathsOpcodeHandlerContainer, ADC_ABS) { + auto machine = create_machine({ADC_ABS, 0xf1, 0x36}); + machine->get_cpu().get_a().set_value(36); + machine->get_memory().set_at(0x36f1, 20); + machine->execute(); + + ASSERT_EQ(56, machine->get_cpu().get_a().get_value()); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(MathsOpcodeHandlerContainer, ADC_ABS_X) { + auto machine = create_machine({ADC_ABS_X, 0xf1, 0x36}); + machine->get_cpu().get_a().set_value(36); + machine->get_cpu().get_x().set_value(8); + machine->get_memory().set_at(0x36f9, 20); + machine->execute(); + + ASSERT_EQ(56, machine->get_cpu().get_a().get_value()); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(MathsOpcodeHandlerContainer, ADC_ABS_Y) { + auto machine = create_machine({ADC_ABS_Y, 0xf1, 0x36}); + machine->get_cpu().get_a().set_value(36); + machine->get_cpu().get_y().set_value(8); + machine->get_memory().set_at(0x36f9, 20); + machine->execute(); + + ASSERT_EQ(56, machine->get_cpu().get_a().get_value()); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(MathsOpcodeHandlerContainer, ADC_IND_X) { + auto machine = create_machine({ADC_IND_X, 0x41}); + machine->get_cpu().get_x().set_value(0x22); + machine->get_cpu().get_a().set_value(36); + machine->get_memory().set_at(0x63, 0x34); + machine->get_memory().set_at(0x64, 0x12); + machine->get_memory().set_at(0x1234, 20); + machine->execute(); + + ASSERT_EQ(56, machine->get_cpu().get_a().get_value()); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +} + +TEST(MathsOpcodeHandlerContainer, ADC_IND_Y) { + auto machine = create_machine({ADC_IND_Y, 0x41}); + machine->get_cpu().get_y().set_value(0x22); + machine->get_cpu().get_a().set_value(36); + machine->get_memory().set_at(0x41, 0x34); + machine->get_memory().set_at(0x42, 0x12); + machine->get_memory().set_at(0x1256, 20); + machine->execute(); + + ASSERT_EQ(56, machine->get_cpu().get_a().get_value()); + ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{})); +}