Add store instructions

This commit is contained in:
Tony Di Nucci 2019-04-17 17:31:35 +01:00
parent 3cb4bcb0e1
commit d987a4849e
7 changed files with 384 additions and 18 deletions

View File

@ -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)
add_executable(6502_emulator ${emulator_src})

View File

@ -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<uint8_t>& reg, uint16_t address) {
machine.get_memory().set_at(address, reg.get_value());
}
void StoreOpcodeHandlerContainer::st_zpg(Machine& machine, Register<uint8_t>& reg) {
auto addr = get_zpg_address(machine.read_program_byte());
store_to(machine, reg, addr);
}
void StoreOpcodeHandlerContainer::st_zpg_x(Machine& machine, Register<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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<uint8_t>& reg) {
auto addr = get_ind_y_address(machine.read_program_byte(), machine);
store_to(machine, reg, addr);
}
}

View File

@ -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 <unordered_map>
#include <memory>
#include <functional>
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<uint8_t>& reg, uint16_t address);
void st_zpg(Machine& machine, Register<uint8_t>& reg);
void st_zpg_x(Machine& machine, Register<uint8_t>& reg);
void st_zpg_y(Machine& machine, Register<uint8_t>& reg);
void st_abs(Machine& machine, Register<uint8_t>& reg);
void st_abs_x(Machine& machine, Register<uint8_t>& reg);
void st_abs_y(Machine& machine, Register<uint8_t>& reg);
void st_ind_x(Machine& machine, Register<uint8_t>& reg);
void st_ind_y(Machine& machine, Register<uint8_t>& reg);
public:
StoreOpcodeHandlerContainer();
StoreOpcodeHandlerContainer(const StoreOpcodeHandlerContainer& other) = delete;
};
}
#endif //INC_6502_EMULATOR_STORE_OPCODE_HANDLER_CONTAINER_H

View File

@ -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<LoadOpcodeHandlerContainer>());
handler_containers.push_back(make_unique<StoreOpcodeHandlerContainer>());
init_handlers();
}

View File

@ -0,0 +1,231 @@
#include "gtest/gtest.h"
#include "test-utils.h"
#include <memory>
#include <vector>
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{}));
}

19
test/test-utils.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "test-utils.h"
unique_ptr<Machine> create_machine(vector<uint8_t> code) {
auto machine = make_unique<Machine>();
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();
}

View File

@ -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<Machine> create_machine(vector<uint8_t> code) {
auto machine = make_unique<Machine>();
machine->load(code, 0x600);
unique_ptr<Machine> create_machine(vector<uint8_t> 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();
}
#endif