mirror of
https://github.com/tdinucci/6502-emulator.git
synced 2024-11-23 07:34:14 +00:00
Add store instructions
This commit is contained in:
parent
3cb4bcb0e1
commit
d987a4849e
@ -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})
|
72
src/opcode/handler/store-opcode-handler-container.cpp
Normal file
72
src/opcode/handler/store-opcode-handler-container.cpp
Normal 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);
|
||||
}
|
||||
}
|
52
src/opcode/handler/store-opcode-handler-container.h
Normal file
52
src/opcode/handler/store-opcode-handler-container.h
Normal 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
|
@ -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();
|
||||
}
|
||||
|
231
test/store-opcode-handler-test.cpp
Normal file
231
test/store-opcode-handler-test.cpp
Normal 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
19
test/test-utils.cpp
Normal 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();
|
||||
}
|
@ -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
|
Loading…
Reference in New Issue
Block a user