Scaled back to just LD* ops but fundamental improvements

This commit is contained in:
Tony Di Nucci 2019-04-15 23:51:04 +01:00
parent 99c0834f0d
commit 3cb4bcb0e1
29 changed files with 1796 additions and 0 deletions

33
CMakeLists.txt Normal file
View File

@ -0,0 +1,33 @@
cmake_minimum_required(VERSION 3.13)
project(6502_emulator)
set(CMAKE_CXX_STANDARD 14)
configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download )
if(result)
message(FATAL_ERROR "CMake step for googletest failed: ${result}")
endif()
execute_process(COMMAND ${CMAKE_COMMAND} --build .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download )
if(result)
message(FATAL_ERROR "Build step for googletest failed: ${result}")
endif()
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src
${CMAKE_CURRENT_BINARY_DIR}/googletest-build
EXCLUDE_FROM_ALL)
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)
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)

15
CMakeLists.txt.in Normal file
View File

@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.13)
project(googletest-download NONE)
include(ExternalProject)
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG master
SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src"
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)

49
src/machine/cpu.cpp Normal file
View File

@ -0,0 +1,49 @@
#include "cpu.h"
#include <iostream>
namespace emu_6502 {
Cpu::Cpu() {
a = make_unique<Register<uint8_t>>("A");
x = make_unique<Register<uint8_t>>("X");
y = make_unique<Register<uint8_t>>("Y");
sp = make_unique<Register<uint8_t>>("SP");
pc = make_unique<ProgramCounter>();
ps = make_unique<StatusRegister>();
}
Register<uint8_t>& Cpu::get_a() {
return *a;
}
Register<uint8_t>& Cpu::get_x() {
return *x;
}
Register<uint8_t>& Cpu::get_y() {
return *y;
}
Register<uint8_t>& Cpu::get_sp() {
return *sp;
}
ProgramCounter& Cpu::get_pc() {
return *pc;
}
StatusRegister& Cpu::get_ps() {
return *ps;
}
void Cpu::dump() {
cout << "--------------" << endl;
cout << "A=0x" << hex << (int) a->get_value() <<
" X=0x" << hex << (int) x->get_value() <<
" Y=0x" << hex << (int) y->get_value() << endl;
cout << "SP=0x" << hex << (int) sp->get_value() <<
" PC=0x" << hex << (int) pc->get_value() << endl;
cout << "NV-BDIZC" << endl << ps->get_value() << endl;
}
}

38
src/machine/cpu.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef INC_6502_EMULATOR_CPU_H
#define INC_6502_EMULATOR_CPU_H
#include "register.h"
#include "status-register.h"
#include "program-counter.h"
#include <memory>
using namespace std;
namespace emu_6502 {
class Cpu {
private:
unique_ptr<Register<uint8_t>> a;
unique_ptr<Register<uint8_t>> x;
unique_ptr<Register<uint8_t>> y;
unique_ptr<Register<uint8_t>> sp;
unique_ptr<ProgramCounter> pc;
unique_ptr<StatusRegister> ps;
public:
Cpu();
Cpu(const Cpu&) = delete;
Cpu& operator=(const Cpu&) = delete;
Register<uint8_t>& get_a();
Register<uint8_t>& get_x();
Register<uint8_t>& get_y();
Register<uint8_t>& get_sp();
ProgramCounter& get_pc();
StatusRegister& get_ps();
void dump();
};
}
#endif //INC_6502_EMULATOR_CPU_H

106
src/machine/machine.cpp Normal file
View File

@ -0,0 +1,106 @@
#include "machine.h"
#include "../opcode/opcode-handler-directory.h"
#include <stdexcept>
namespace emu_6502 {
class MachineImpl {
private:
uint16_t code_loaded_at;
uint16_t code_size;
unique_ptr<Cpu> cpu;
unique_ptr<Memory> memory;
unique_ptr<Stack> stack;
unique_ptr<OpcodeHandlerDirectory> opcode_handler_dir;
public:
MachineImpl() {
cpu = make_unique<Cpu>();
memory = make_unique<Memory>();
stack = make_unique<Stack>(*memory, cpu->get_sp());
opcode_handler_dir = make_unique<OpcodeHandlerDirectory>();
}
MachineImpl(const MachineImpl&) = delete;
MachineImpl& operator=(const MachineImpl&) = delete;
Cpu& get_cpu() {
return *cpu;
}
Memory& get_memory() {
return *memory;
}
Stack& get_stack() {
return *stack;
}
void load(const vector<uint8_t>& program, uint16_t load_at) {
code_loaded_at = load_at;
code_size = program.size();
uint32_t code_end = load_at + code_size;
if (code_end > 0xFE00) // need space for graphics, interupts, etc.
throw runtime_error("Program does not fit into memory");
for (auto i = 0; i < program.size(); i++)
memory->set_at(load_at + i, program.at(i));
cpu->get_pc().set_value(load_at);
}
bool is_eop() {
return cpu->get_pc().get_value() >= code_loaded_at + code_size;
}
uint8_t read_program_byte() {
if (is_eop())
throw runtime_error("Passed end of program");
auto byte = memory->get_at(cpu->get_pc().get_value());
cpu->get_pc().inc();
return byte;
}
void execute(Machine& machine) {
while (!is_eop()) {
auto byte = read_program_byte();
opcode_handler_dir->execute(byte, machine);
}
}
};
Machine::Machine() : pimpl(make_unique<MachineImpl>()) {
}
Machine::~Machine() = default;
Cpu& Machine::get_cpu() {
return pimpl->get_cpu();
}
Memory& Machine::get_memory() {
return pimpl->get_memory();
}
Stack& Machine::get_stack() {
return pimpl->get_stack();
}
void Machine::load(const vector<uint8_t>& program, uint16_t load_at) {
pimpl->load(program, load_at);
}
bool Machine::is_eop() {
return pimpl->is_eop();
}
uint8_t Machine::read_program_byte() {
return pimpl->read_program_byte();
}
void Machine::execute() {
pimpl->execute(*this);
}
}

36
src/machine/machine.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef INC_6502_EMULATOR_MACHINE_H
#define INC_6502_EMULATOR_MACHINE_H
#include "cpu.h"
#include "memory.h"
#include "stack.h"
#include <memory>
#include <vector>
using namespace std;
namespace emu_6502 {
class MachineImpl;
class Machine {
private:
unique_ptr<MachineImpl> pimpl;
public:
Machine();
~Machine();
Machine(const Machine&) = delete;
Machine& operator=(const Machine&) = delete;
Cpu& get_cpu();
Memory& get_memory();
Stack& get_stack();
bool is_eop();
uint8_t read_program_byte();
void load(const vector<uint8_t>& program, uint16_t load_at);
void execute();
};
}
#endif //INC_6502_EMULATOR_MACHINE_H

27
src/machine/memory.cpp Normal file
View File

@ -0,0 +1,27 @@
#include "memory.h"
namespace emu_6502 {
inline uint16_t Memory::get_page_offset(uint8_t page) {
return page * 0xFF;
}
Memory::Memory() {
memory = vector<uint8_t>(0xFFFF);
}
uint8_t Memory::get_at(uint16_t address) {
return memory.at(address);
}
void Memory::set_at(uint16_t address, uint8_t value) {
memory.at(address) = value;
}
uint8_t Memory::get_from_page(uint8_t page, uint8_t offset) {
return get_at(get_page_offset(page) + offset);
}
void Memory::set_on_page(uint8_t page, uint8_t offset, uint8_t value) {
set_at(get_page_offset(page) + offset, value);
}
}

29
src/machine/memory.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef INC_6502_EMULATOR_MEMORY_H
#define INC_6502_EMULATOR_MEMORY_H
#include <vector>
#include <cstdint>
using namespace std;
namespace emu_6502 {
class Memory {
private:
vector<uint8_t> memory;
uint16_t get_page_offset(uint8_t page);
public:
Memory();
Memory(const Memory&) = delete;
Memory& operator=(const Memory&) = delete;
uint8_t get_at(uint16_t address);
void set_at(uint16_t address, uint8_t value);
uint8_t get_from_page(uint8_t page, uint8_t offset);
void set_on_page(uint8_t page, uint8_t offset, uint8_t value);
};
}
#endif //INC_6502_EMULATOR_MEMORY_H

View File

@ -0,0 +1,7 @@
#include "program-counter.h"
namespace emu_6502 {
void ProgramCounter::inc() {
value += 1;
}
}

View File

@ -0,0 +1,18 @@
#ifndef INC_6502_EMULATOR_PROGRAM_COUNTER_H
#define INC_6502_EMULATOR_PROGRAM_COUNTER_H
#include "register.h"
namespace emu_6502 {
class ProgramCounter : public Register<uint16_t> {
public:
ProgramCounter() : Register<uint16_t>("PC") {}
ProgramCounter(const ProgramCounter&) = delete;
ProgramCounter& operator=(const ProgramCounter&) = delete;
void inc();
};
}
#endif //INC_6502_EMULATOR_PROGRAM_COUNTER_H

33
src/machine/register.cpp Normal file
View File

@ -0,0 +1,33 @@
#include "register.h"
namespace emu_6502 {
template<typename T>
Register<T>::Register(string name) {
this->name = name;
this->value = 0;
}
template<class T>
void Register<T>::set_value(T value) {
this->value = value;
}
template<typename T>
const T Register<T>::get_value() {
return value;
}
template<typename T>
const string& Register<T>::get_name() {
return name;
}
}
template
class emu_6502::Register<uint8_t>;
template
class emu_6502::Register<uint16_t>;
template
class emu_6502::Register<bitset<8>>;

30
src/machine/register.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef INC_6502_EMULATOR_REGISTER_H
#define INC_6502_EMULATOR_REGISTER_H
#include <string>
#include <bitset>
using namespace std;
namespace emu_6502 {
template<typename T>
class Register {
private:
string name;
protected:
T value;
public:
Register<T>(string name);
Register<T>(const Register<T>&) = delete;
Register<T>& operator=(const Register<T>&) = delete;
const string& get_name();
const T get_value();
void set_value(T value);
};
}
#endif //INC_6502_EMULATOR_REGISTER_H

22
src/machine/stack.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "stack.h"
namespace emu_6502 {
Stack::Stack(Memory& memory, Register<uint8_t>& sp) : memory(memory), sp(sp) {
sp.set_value(0xFF);
}
void Stack::push(uint8_t value) {
auto sp_value = sp.get_value();
memory.set_on_page(Stack::PAGE, sp_value, value);
sp.set_value(sp_value + 1);
}
uint8_t Stack::pop() {
// a "feature" of the 6502 is that there is no check when popping and stack will wrap around
auto sp_value = sp.get_value() - 1;
sp.set_value(sp_value);
return memory.get_from_page(Stack::PAGE, sp_value);
}
}

30
src/machine/stack.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef INC_6502_EMULATOR_STACK_H
#define INC_6502_EMULATOR_STACK_H
#include <memory>
#include "memory.h"
#include "register.h"
using namespace std;
namespace emu_6502 {
class Stack {
private:
Memory& memory;
Register<uint8_t>& sp;
public:
const uint8_t PAGE = 1;
Stack(Memory& memory, Register<uint8_t>& sp);
Stack(const Stack&) = delete;
Stack& operator=(const Stack&) = delete;
void push(uint8_t value);
uint8_t pop();
};
}
#endif //INC_6502_EMULATOR_STACK_H

View File

@ -0,0 +1,59 @@
#include "status-register.h"
namespace emu_6502 {
bool StatusRegister::is_carry_set() const {
return this->value[Flag::CARRY];
}
bool StatusRegister::is_zero_set() const {
return this->value[Flag::ZERO];
}
bool StatusRegister::is_interupt_disable_set() const {
return this->value[Flag::INTERUPT_DISABLE];
}
bool StatusRegister::is_decimal_set() const {
return this->value[Flag::DECIMAL];
}
bool StatusRegister::is_break_set() const {
return this->value[Flag::BREAK];
}
bool StatusRegister::is_overflow_set() const {
return this->value[Flag::OVERFLOW];
}
bool StatusRegister::is_negative_set() const {
return this->value[Flag::NEGATIVE];
}
void StatusRegister::set_carry(bool state) {
this->value[Flag::CARRY] = state;
}
void StatusRegister::set_zero(bool state) {
this->value[Flag::ZERO] = state;
}
void StatusRegister::set_interupt_disable(bool state) {
this->value[Flag::INTERUPT_DISABLE] = state;
}
void StatusRegister::set_decimal(bool state) {
this->value[Flag::DECIMAL] = state;
}
void StatusRegister::set_break(bool state) {
this->value[Flag::BREAK] = state;
}
void StatusRegister::set_overflow(bool state) {
this->value[Flag::OVERFLOW] = state;
}
void StatusRegister::set_negative(bool state) {
this->value[Flag::NEGATIVE] = state;
}
}

View File

@ -0,0 +1,47 @@
#ifndef INC_6502_EMULATOR_STATUS_REGISTER_H
#define INC_6502_EMULATOR_STATUS_REGISTER_H
#include <bitset>
#include <string>
#include "register.h"
using namespace std;
namespace emu_6502 {
class StatusRegister : public Register<bitset<8>> {
private:
enum Flag {
CARRY = 0,
ZERO = 1,
INTERUPT_DISABLE = 2,
DECIMAL = 3,
BREAK = 4,
OVERFLOW = 6,
NEGATIVE = 7
};
public:
StatusRegister() : Register<bitset<8>>("PS") {}
StatusRegister(const StatusRegister&) = delete;
StatusRegister& operator=(const StatusRegister&) = delete;
bool is_carry_set() const;
bool is_zero_set() const;
bool is_interupt_disable_set() const;
bool is_decimal_set() const;
bool is_break_set() const;
bool is_overflow_set() const;
bool is_negative_set() const;
void set_carry(bool state);
void set_zero(bool state);
void set_interupt_disable(bool state);
void set_decimal(bool state);
void set_break(bool state);
void set_overflow(bool state);
void set_negative(bool state);
};
}
#endif //INC_6502_EMULATOR_STATUS_REGISTER_H

37
src/machine/terminal.cpp Normal file
View File

@ -0,0 +1,37 @@
#include <unistd.h>
#include <iostream>
#include "terminal.h"
namespace emu_6502 {
Terminal::Terminal(Memory& memory) : memory(memory) {
// for (auto i = 0; i < 100; i++)
// memory->set_at(Terminal::LOW_ADDR + i, 0x20 + i);
//
// if ((mainwin = initscr()) == NULL) {
// fprintf(stderr, "Error initialising ncurses.\n");
// exit(EXIT_FAILURE);
// }
//
// start_color();
}
Terminal::~Terminal() {
// delwin(mainwin);
// endwin();
cout << "shutting down" << endl;
}
void Terminal::refresh() {
// for (auto n = 0; n <= 100; n++) {
// auto line = (n / TERM_COLS);
// auto col = n % TERM_COLS;
//
// mvaddch(line, col, memory->get_at(Terminal::LOW_ADDR + n));
// n++;
// }
//
// wrefresh(mainwin);
}
}

33
src/machine/terminal.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef INC_6502_EMULATOR_TERMINAL_H
#define INC_6502_EMULATOR_TERMINAL_H
#include <ncurses.h>
#include <memory>
#include "memory.h"
using namespace std;
namespace emu_6502 {
class Terminal {
private:
const uint16_t LOW_ADDR = 0xFB00;
const uint16_t HIGH_ADDR = 0xFF00;
const uint8_t TERM_LINES = 25;
const uint8_t TERM_COLS = 40;
//WINDOW * mainwin;
Memory& memory;
public:
Terminal(Memory& memory);
Terminal(const Terminal&) = delete;
Terminal& operator=(const Terminal&) = delete;
~Terminal();
void refresh();
};
}
#endif //INC_6502_EMULATOR_TERMINAL_H

30
src/main.cpp Normal file
View File

@ -0,0 +1,30 @@
#include <iostream>
#include <memory>
#include <vector>
#include "machine/machine.h"
#include "machine/terminal.h"
#include "machine/memory.h"
#include "opcode/opcode-handler-directory.h"
#include "opcode/handler/load-opcode-handler-container.h"
using namespace std;
using namespace emu_6502;
int main() {
vector<uint8_t> code{0xa9, 53, 0xA5, 16};
auto machine = make_unique<Machine>();
machine->load(code, 0x600);
machine->execute();
// auto memory = make_shared<Memory>();
// auto term = make_unique<Terminal>(memory);
//
// term->refresh();
return 0;
}

View File

@ -0,0 +1,92 @@
#include "load-opcode-handler-container.h"
#include "../../utils.h"
#include <iostream>
namespace emu_6502 {
LoadOpcodeHandlerContainer::LoadOpcodeHandlerContainer() : OpcodeHandlerContainer() {
handlers.insert({Op::LDA_IMM, [this](Machine& machine) { ld_imm(machine, machine.get_cpu().get_a()); }});
handlers.insert({Op::LDA_ZPG, [this](Machine& machine) { ld_zpg(machine, machine.get_cpu().get_a()); }});
handlers.insert({Op::LDA_ZPG_X, [this](Machine& machine) { ld_zpg_x(machine, machine.get_cpu().get_a()); }});
handlers.insert({Op::LDA_ABS, [this](Machine& machine) { ld_abs(machine, machine.get_cpu().get_a()); }});
handlers.insert({Op::LDA_ABS_X, [this](Machine& machine) { ld_abs_x(machine, machine.get_cpu().get_a()); }});
handlers.insert({Op::LDA_ABS_Y, [this](Machine& machine) { ld_abs_y(machine, machine.get_cpu().get_a()); }});
handlers.insert({Op::LDA_IND_X, [this](Machine& machine) { ld_ind_x(machine, machine.get_cpu().get_a()); }});
handlers.insert({Op::LDA_IND_Y, [this](Machine& machine) { ld_ind_y(machine, machine.get_cpu().get_a()); }});
handlers.insert({Op::LDX_IMM, [this](Machine& machine) { ld_imm(machine, machine.get_cpu().get_x()); }});
handlers.insert({Op::LDX_ZPG, [this](Machine& machine) { ld_zpg(machine, machine.get_cpu().get_x()); }});
handlers.insert({Op::LDX_ZPG_Y, [this](Machine& machine) { ld_zpg_y(machine, machine.get_cpu().get_x()); }});
handlers.insert({Op::LDX_ABS, [this](Machine& machine) { ld_abs(machine, machine.get_cpu().get_x()); }});
handlers.insert({Op::LDX_ABS_Y, [this](Machine& machine) { ld_abs_y(machine, machine.get_cpu().get_x()); }});
handlers.insert({Op::LDY_IMM, [this](Machine& machine) { ld_imm(machine, machine.get_cpu().get_y()); }});
handlers.insert({Op::LDY_ZPG, [this](Machine& machine) { ld_zpg(machine, machine.get_cpu().get_y()); }});
handlers.insert({Op::LDY_ZPG_X, [this](Machine& machine) { ld_zpg_x(machine, machine.get_cpu().get_y()); }});
handlers.insert({Op::LDY_ABS, [this](Machine& machine) { ld_abs(machine, machine.get_cpu().get_y()); }});
handlers.insert({Op::LDY_ABS_X, [this](Machine& machine) { ld_abs_x(machine, machine.get_cpu().get_y()); }});
}
void LoadOpcodeHandlerContainer::set_flags(Cpu& cpu, Register<uint8_t>& reg) {
auto value = reg.get_value();
cpu.get_ps().set_negative((value & 0x80) == 0x80);
cpu.get_ps().set_zero(value == 0);
}
void LoadOpcodeHandlerContainer::set_from(Machine& machine, Register<uint8_t>& reg, uint16_t address) {
auto value = machine.get_memory().get_at(address);
reg.set_value(value);
set_flags(machine.get_cpu(), reg);
}
void LoadOpcodeHandlerContainer::ld_imm(Machine& machine, Register<uint8_t>& reg) {
reg.set_value(machine.read_program_byte());
set_flags(machine.get_cpu(), reg);
}
void LoadOpcodeHandlerContainer::ld_zpg(Machine& machine, Register<uint8_t>& reg) {
auto addr = get_zpg_address(machine.read_program_byte());
set_from(machine, reg, addr);
}
void LoadOpcodeHandlerContainer::ld_zpg_x(Machine& machine, Register<uint8_t>& reg) {
auto addr = get_zpg_x_address(machine.read_program_byte(), machine.get_cpu());
set_from(machine, reg, addr);
}
void LoadOpcodeHandlerContainer::ld_zpg_y(Machine& machine, Register<uint8_t>& reg) {
auto addr = get_zpg_y_address(machine.read_program_byte(), machine.get_cpu());
set_from(machine, reg, addr);
}
void LoadOpcodeHandlerContainer::ld_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);
set_from(machine, reg, addr);
}
void LoadOpcodeHandlerContainer::ld_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());
set_from(machine, reg, addr);
}
void LoadOpcodeHandlerContainer::ld_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());
set_from(machine, reg, addr);
}
void LoadOpcodeHandlerContainer::ld_ind_x(Machine& machine, Register<uint8_t>& reg) {
auto addr = get_ind_x_address(machine.read_program_byte(), machine);
set_from(machine, reg, addr);
}
void LoadOpcodeHandlerContainer::ld_ind_y(Machine& machine, Register<uint8_t>& reg) {
auto addr = get_ind_y_address(machine.read_program_byte(), machine);
set_from(machine, reg, addr);
}
}

View File

@ -0,0 +1,58 @@
#ifndef INC_6502_EMULATOR_LOAD_OPCODE_HANDLER_CONTAINER_H
#define INC_6502_EMULATOR_LOAD_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 LoadOpcodeHandlerContainer : public OpcodeHandlerContainer {
private:
enum Op {
LDA_IMM = 0xA9,
LDA_ZPG = 0xA5,
LDA_ZPG_X = 0xB5,
LDA_ABS = 0xAD,
LDA_ABS_X = 0xBD,
LDA_ABS_Y = 0xB9,
LDA_IND_X = 0xA1,
LDA_IND_Y = 0xB1,
LDX_IMM = 0xA2,
LDX_ZPG = 0xA6,
LDX_ZPG_Y = 0xB6,
LDX_ABS = 0xAE,
LDX_ABS_Y = 0xBE,
LDY_IMM = 0xA0,
LDY_ZPG = 0xA4,
LDY_ZPG_X = 0xB4,
LDY_ABS = 0xAC,
LDY_ABS_X = 0xBC
};
void set_flags(Cpu& machine, Register<uint8_t>& reg);
void set_from(Machine& machine, Register<uint8_t>& reg, uint16_t address);
void ld_imm(Machine& machine, Register<uint8_t>& reg);
void ld_zpg(Machine& machine, Register<uint8_t>& reg);
void ld_zpg_x(Machine& machine, Register<uint8_t>& reg);
void ld_zpg_y(Machine& machine, Register<uint8_t>& reg);
void ld_abs(Machine& machine, Register<uint8_t>& reg);
void ld_abs_x(Machine& machine, Register<uint8_t>& reg);
void ld_abs_y(Machine& machine, Register<uint8_t>& reg);
void ld_ind_x(Machine& machine, Register<uint8_t>& reg);
void ld_ind_y(Machine& machine, Register<uint8_t>& reg);
public:
LoadOpcodeHandlerContainer();
LoadOpcodeHandlerContainer(const LoadOpcodeHandlerContainer& other) = delete;
};
}
#endif //INC_6502_EMULATOR_LOAD_OPCODE_HANDLER_CONTAINER_H

View File

@ -0,0 +1,9 @@
#include "opcode-handler-container.h"
namespace emu_6502 {
OpcodeHandlerContainer::OpcodeHandlerContainer() : handlers{} {}
const unordered_map<uint8_t, function<void(Machine&)>>& OpcodeHandlerContainer::get_handlers() {
return handlers;
}
}

View File

@ -0,0 +1,24 @@
#ifndef INC_6502_EMULATOR_OPCODE_HANDLER_CONTAINER_H
#define INC_6502_EMULATOR_OPCODE_HANDLER_CONTAINER_H
#include "../../machine/machine.h"
#include <cstdint>
#include <unordered_map>
#include <functional>
namespace emu_6502 {
class OpcodeHandlerContainer {
protected:
unordered_map<uint8_t, function<void(Machine&)>> handlers;
public:
OpcodeHandlerContainer();
OpcodeHandlerContainer(const OpcodeHandlerContainer& other) = delete;
virtual ~OpcodeHandlerContainer() {};
const unordered_map<uint8_t, function<void(Machine&)>>& get_handlers();
};
}
#endif //INC_6502_EMULATOR_OPCODE_HANDLER_CONTAINER_H

View File

@ -0,0 +1,28 @@
#include "opcode-handler-directory.h"
#include "handler/load-opcode-handler-container.h"
#include "../utils.h"
namespace emu_6502 {
OpcodeHandlerDirectory::OpcodeHandlerDirectory() : handler_containers{}, handlers{} {
handler_containers.push_back(make_unique<LoadOpcodeHandlerContainer>());
init_handlers();
}
void OpcodeHandlerDirectory::init_handlers() {
for (auto& hc : handler_containers) {
for (auto& h : hc->get_handlers()) {
handlers.insert({h.first, h.second});
}
}
}
void OpcodeHandlerDirectory::execute(uint8_t opcode, Machine& machine) {
auto func = handlers.find(opcode);
if (func == handlers.end())
throw runtime_error("No appropriate handler for opcode 0x" + uint_to_hex(opcode));
func->second(machine);
}
}

View File

@ -0,0 +1,31 @@
#ifndef INC_6502_EMULATOR_OPCODE_HANDLER_DIRECTORY_H
#define INC_6502_EMULATOR_OPCODE_HANDLER_DIRECTORY_H
#include "handler/opcode-handler-container.h"
#include "../machine/machine.h"
#include <memory>
#include <vector>
#include <unordered_map>
#include <functional>
using namespace std;
namespace emu_6502 {
class OpcodeHandlerDirectory {
private:
vector<unique_ptr<OpcodeHandlerContainer>> handler_containers;
unordered_map<uint8_t, function<void(Machine&)>> handlers;
void init_handlers();
public:
OpcodeHandlerDirectory();
OpcodeHandlerDirectory(const OpcodeHandlerDirectory&) = delete;
OpcodeHandlerDirectory& operator=(const OpcodeHandlerDirectory&) = delete;
void execute(uint8_t opcode, Machine& machine);
};
}
#endif //INC_6502_EMULATOR_OPCODE_HANDLER_DIRECTORY_H

57
src/utils.cpp Normal file
View File

@ -0,0 +1,57 @@
#include "utils.h"
namespace emu_6502 {
string uint_to_hex(uint64_t value) {
stringstream ss;
ss << hex << value;
return ss.str();
}
uint8_t get_zpg_address(uint8_t offset) {
return offset;
}
uint8_t get_zpg_x_address(uint8_t offset, Cpu& cpu) {
// expect wrap around
return offset + cpu.get_x().get_value();
}
uint8_t get_zpg_y_address(uint8_t offset, Cpu& cpu) {
// expect wrap around
return offset + cpu.get_y().get_value();
}
uint16_t get_abs_address(uint8_t low_byte, uint8_t high_byte) {
uint16_t address = (high_byte << 8) + low_byte;
return address;
}
uint16_t get_abs_x_address(uint8_t low_byte, uint8_t high_byte, Cpu& cpu) {
uint16_t address = (high_byte << 8) + low_byte + cpu.get_x().get_value();
return address;
}
uint16_t get_abs_y_address(uint8_t low_byte, uint8_t high_byte, Cpu& cpu) {
uint16_t address = (high_byte << 8) + low_byte + cpu.get_y().get_value();
return address;
}
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);
auto high = machine.get_memory().get_at(paddress + 1);
uint16_t address = (high << 8) + low;
return address;
}
uint16_t get_ind_y_address(uint8_t offset, Machine& machine) {
auto low = machine.get_memory().get_at(offset);
auto high = machine.get_memory().get_at(offset + 1);
auto y = machine.get_cpu().get_y().get_value();
uint16_t address = (high << 8) + low + y;
return address;
}
}

22
src/utils.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef INC_6502_EMULATOR_UTILS_H
#define INC_6502_EMULATOR_UTILS_H
#include "machine/machine.h"
#include <string>
#include <sstream>
using namespace std;
namespace emu_6502 {
string uint_to_hex(uint64_t value);
uint8_t get_zpg_address(uint8_t offset);
uint8_t get_zpg_x_address(uint8_t offset, Cpu& cpu);
uint8_t get_zpg_y_address(uint8_t offset, Cpu& cpu);
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_ind_x_address(uint8_t offset, Machine& machine);
uint16_t get_ind_y_address(uint8_t offset, Machine& machine);
}
#endif // INC_6502_EMULATOR_UTILS_H

View File

@ -0,0 +1,760 @@
#include "gtest/gtest.h"
#include "test-utils.h"
#include <memory>
#include <vector>
using namespace std;
using namespace emu_6502;
const uint8_t LDA_IMM = 0xA9;
const uint8_t LDA_ZPG = 0xA5;
const uint8_t LDA_ZPG_X = 0xB5;
const uint8_t LDA_ABS = 0xAD;
const uint8_t LDA_ABS_X = 0xBD;
const uint8_t LDA_ABS_Y = 0xB9;
const uint8_t LDA_IND_X = 0xA1;
const uint8_t LDA_IND_Y = 0xB1;
const uint8_t LDX_IMM = 0xA2;
const uint8_t LDX_ZPG = 0xA6;
const uint8_t LDX_ZPG_Y = 0xB6;
const uint8_t LDX_ABS = 0xAE;
const uint8_t LDX_ABS_Y = 0xBE;
const uint8_t LDY_IMM = 0xA0;
const uint8_t LDY_ZPG = 0xA4;
const uint8_t LDY_ZPG_X = 0xB4;
const uint8_t LDY_ABS = 0xAC;
const uint8_t LDY_ABS_X = 0xBC;
TEST(LoadOpcodeHandlerContainer, LDA_IMM) {
auto machine = create_machine({LDA_IMM, 36});
machine->execute();
ASSERT_EQ(36, machine->get_cpu().get_a().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDA_IMM_ZeroFlag) {
auto machine = create_machine({LDA_IMM, 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(LoadOpcodeHandlerContainer, LDA_IMM_NegativeFlag) {
// ensure bit 7 set
auto machine = create_machine({LDA_IMM, 128});
machine->execute();
ASSERT_EQ(128, machine->get_cpu().get_a().get_value());
RegisterFlagSet flags{};
flags.negative = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDA_ZPG) {
auto machine = create_machine({LDA_ZPG, 123});
machine->get_memory().set_at(123, 78);
machine->execute();
ASSERT_EQ(78, machine->get_cpu().get_a().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDA_ZPG_ZeroFlag) {
auto machine = create_machine({LDA_ZPG, 123});
machine->get_memory().set_at(123, 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(LoadOpcodeHandlerContainer, LDA_ZPG_NegativeFlag) {
auto machine = create_machine({LDA_ZPG, 123});
// ensure bit 7 set
machine->get_memory().set_at(123, 150);
machine->execute();
ASSERT_EQ(150, machine->get_cpu().get_a().get_value());
RegisterFlagSet flags{};
flags.negative = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDA_ZPG_X) {
auto machine = create_machine({LDA_ZPG_X, 0x10});
machine->get_cpu().get_x().set_value(0x15);
machine->get_memory().set_at(0x25, 78);
machine->execute();
ASSERT_EQ(78, machine->get_cpu().get_a().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDA_ZPG_X_Wrap) {
auto machine = create_machine({LDA_ZPG_X, 0xFF});
machine->get_cpu().get_x().set_value(0x80);
machine->get_memory().set_at(0x7F, 78);
machine->execute();
ASSERT_EQ(78, machine->get_cpu().get_a().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDA_ZPG_X_ZeroFlag) {
auto machine = create_machine({LDA_ZPG_X, 0x10});
machine->get_cpu().get_x().set_value(0x15);
machine->get_memory().set_at(0x25, 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(LoadOpcodeHandlerContainer, LDA_ZPG_X_NegativeFlag) {
auto machine = create_machine({LDA_ZPG_X, 0x10});
machine->get_cpu().get_x().set_value(0x15);
// ensure bit 7 set
machine->get_memory().set_at(0x25, 0xF5);
machine->execute();
ASSERT_EQ(0xF5, machine->get_cpu().get_a().get_value());
RegisterFlagSet flags{};
flags.negative = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDA_ABS) {
auto machine = create_machine({LDA_ABS, 0x55, 0x66});
machine->get_memory().set_at(0x6655, 0x10);
machine->execute();
ASSERT_EQ(0x10, machine->get_cpu().get_a().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDA_ABS_ZeroFlag) {
auto machine = create_machine({LDA_ABS, 0x55, 0x66});
machine->get_memory().set_at(0x6655, 0x0);
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(LoadOpcodeHandlerContainer, LDA_ABS_NegativeFlag) {
// ensure bit 7 set
auto machine = create_machine({LDA_ABS, 0x55, 0x66});
machine->get_memory().set_at(0x6655, 0xF6);
machine->execute();
ASSERT_EQ(0xF6, machine->get_cpu().get_a().get_value());
RegisterFlagSet flags{};
flags.negative = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDA_ABS_X) {
auto machine = create_machine({LDA_ABS_X, 0x55, 0x66});
machine->get_cpu().get_x().set_value(0x4);
machine->get_memory().set_at(0x6659, 0x10);
machine->execute();
ASSERT_EQ(0x10, machine->get_cpu().get_a().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDA_ABS_X_ZeroFlag) {
auto machine = create_machine({LDA_ABS_X, 0x55, 0x66});
machine->get_cpu().get_x().set_value(0x4);
machine->get_memory().set_at(0x6659, 0x0);
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(LoadOpcodeHandlerContainer, LDA_ABS_X_NegativeFlag) {
// ensure bit 7 set
auto machine = create_machine({LDA_ABS_X, 0x55, 0x66});
machine->get_cpu().get_x().set_value(0x4);
machine->get_memory().set_at(0x6659, 0xF6);
machine->execute();
ASSERT_EQ(0xF6, machine->get_cpu().get_a().get_value());
RegisterFlagSet flags{};
flags.negative = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDA_ABS_Y) {
auto machine = create_machine({LDA_ABS_Y, 0x55, 0x66});
machine->get_cpu().get_y().set_value(0x4);
machine->get_memory().set_at(0x6659, 0x10);
machine->execute();
ASSERT_EQ(0x10, machine->get_cpu().get_a().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDA_ABS_Y_ZeroFlag) {
auto machine = create_machine({LDA_ABS_Y, 0x55, 0x66});
machine->get_cpu().get_y().set_value(0x4);
machine->get_memory().set_at(0x6659, 0x0);
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(LoadOpcodeHandlerContainer, LDA_ABS_Y_NegativeFlag) {
// ensure bit 7 set
auto machine = create_machine({LDA_ABS_Y, 0x55, 0x66});
machine->get_cpu().get_y().set_value(0x4);
machine->get_memory().set_at(0x6659, 0xF6);
machine->execute();
ASSERT_EQ(0xF6, machine->get_cpu().get_a().get_value());
RegisterFlagSet flags{};
flags.negative = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDA_IND_X) {
auto machine = create_machine({LDA_IND_X, 0x55});
machine->get_cpu().get_x().set_value(0x4);
// pointer
machine->get_memory().set_at(0x59, 0x3c);
machine->get_memory().set_at(0x5A, 0x4d);
// value
machine->get_memory().set_at(0x4d3c, 0x33);
machine->execute();
ASSERT_EQ(0x33, machine->get_cpu().get_a().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDA_IND_X_Wrap) {
auto machine = create_machine({LDA_IND_X, 0xFF});
machine->get_cpu().get_x().set_value(0x6b);
// pointer
machine->get_memory().set_at(0x6a, 0x3c);
machine->get_memory().set_at(0x6b, 0x4d);
// value
machine->get_memory().set_at(0x4d3c, 0x33);
machine->execute();
ASSERT_EQ(0x33, machine->get_cpu().get_a().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDA_IND_X_ZeroFlag) {
auto machine = create_machine({LDA_IND_X, 0x55});
machine->get_cpu().get_x().set_value(0x4);
// pointer
machine->get_memory().set_at(0x59, 0x3c);
machine->get_memory().set_at(0x5A, 0x4d);
// value
machine->get_memory().set_at(0x4d3c, 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(LoadOpcodeHandlerContainer, LDA_IND_X_NegativeFlag) {
auto machine = create_machine({LDA_IND_X, 0x55});
machine->get_cpu().get_x().set_value(0x4);
// pointer
machine->get_memory().set_at(0x59, 0x3c);
machine->get_memory().set_at(0x5A, 0x4d);
// value - ensure bit 7 set
machine->get_memory().set_at(0x4d3c, 0xe3);
machine->execute();
ASSERT_EQ(0xe3, machine->get_cpu().get_a().get_value());
RegisterFlagSet flags{};
flags.negative = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDA_IND_Y) {
auto machine = create_machine({LDA_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_memory().set_at(0x342c, 0x33);
machine->execute();
ASSERT_EQ(0x33, machine->get_cpu().get_a().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDA_IND_Y_ZeroFlag) {
auto machine = create_machine({LDA_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_memory().set_at(0x342c, 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(LoadOpcodeHandlerContainer, LDA_IND_Y_NegativeFlag) {
auto machine = create_machine({LDA_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);
// ensure bit 7 set
machine->get_memory().set_at(0x342c, 0xf3);
machine->execute();
ASSERT_EQ(0xf3, machine->get_cpu().get_a().get_value());
RegisterFlagSet flags{};
flags.negative = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDX_IMM) {
auto machine = create_machine({LDX_IMM, 36});
machine->execute();
ASSERT_EQ(36, machine->get_cpu().get_x().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDX_IMM_ZeroFlag) {
auto machine = create_machine({LDX_IMM, 0});
machine->execute();
ASSERT_EQ(0, machine->get_cpu().get_x().get_value());
RegisterFlagSet flags{};
flags.zero = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDX_IMM_NegativeFlag) {
auto machine = create_machine({LDX_IMM, 0xff});
machine->execute();
ASSERT_EQ(0xff, machine->get_cpu().get_x().get_value());
RegisterFlagSet flags{};
flags.negative = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDX_ZPG) {
auto machine = create_machine({LDX_ZPG, 123});
machine->get_memory().set_at(123, 78);
machine->execute();
ASSERT_EQ(78, machine->get_cpu().get_x().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDX_ZPG_ZeroFlag) {
auto machine = create_machine({LDX_ZPG, 123});
machine->get_memory().set_at(123, 0);
machine->execute();
ASSERT_EQ(0, machine->get_cpu().get_x().get_value());
RegisterFlagSet flags{};
flags.zero = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDX_ZPG_NegativeFlag) {
auto machine = create_machine({LDX_ZPG, 123});
// ensure bit 7 set
machine->get_memory().set_at(123, 150);
machine->execute();
ASSERT_EQ(150, machine->get_cpu().get_x().get_value());
RegisterFlagSet flags{};
flags.negative = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDX_ZPG_Y) {
auto machine = create_machine({LDX_ZPG_Y, 0x10});
machine->get_cpu().get_y().set_value(0x15);
machine->get_memory().set_at(0x25, 78);
machine->execute();
ASSERT_EQ(78, machine->get_cpu().get_x().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDX_ZPG_Y_Wrap) {
auto machine = create_machine({LDX_ZPG_Y, 0xFF});
machine->get_cpu().get_y().set_value(0x80);
machine->get_memory().set_at(0x7F, 78);
machine->execute();
ASSERT_EQ(78, machine->get_cpu().get_x().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDX_ZPG_Y_ZeroFlag) {
auto machine = create_machine({LDX_ZPG_Y, 0x10});
machine->get_cpu().get_y().set_value(0x15);
machine->get_memory().set_at(0x25, 0);
machine->execute();
ASSERT_EQ(0, machine->get_cpu().get_x().get_value());
RegisterFlagSet flags{};
flags.zero = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDX_ZPG_Y_NegativeFlag) {
auto machine = create_machine({LDX_ZPG_Y, 0x10});
machine->get_cpu().get_y().set_value(0x15);
// ensure bit 7 set
machine->get_memory().set_at(0x25, 0xF5);
machine->execute();
ASSERT_EQ(0xF5, machine->get_cpu().get_x().get_value());
RegisterFlagSet flags{};
flags.negative = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDX_ABS) {
auto machine = create_machine({LDX_ABS, 0x55, 0x66});
machine->get_memory().set_at(0x6655, 0x10);
machine->execute();
ASSERT_EQ(0x10, machine->get_cpu().get_x().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDX_ABS_ZeroFlag) {
auto machine = create_machine({LDX_ABS, 0x55, 0x66});
machine->get_memory().set_at(0x6655, 0x0);
machine->execute();
ASSERT_EQ(0, machine->get_cpu().get_x().get_value());
RegisterFlagSet flags{};
flags.zero = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDX_ABS_NegativeFlag) {
// ensure bit 7 set
auto machine = create_machine({LDX_ABS, 0x55, 0x66});
machine->get_memory().set_at(0x6655, 0xF6);
machine->execute();
ASSERT_EQ(0xF6, machine->get_cpu().get_x().get_value());
RegisterFlagSet flags{};
flags.negative = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDX_ABS_Y) {
auto machine = create_machine({LDX_ABS_Y, 0x55, 0x66});
machine->get_cpu().get_y().set_value(0x4);
machine->get_memory().set_at(0x6659, 0x10);
machine->execute();
ASSERT_EQ(0x10, machine->get_cpu().get_x().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDX_ABS_Y_ZeroFlag) {
auto machine = create_machine({LDX_ABS_Y, 0x55, 0x66});
machine->get_cpu().get_y().set_value(0x4);
machine->get_memory().set_at(0x6659, 0x0);
machine->execute();
ASSERT_EQ(0, machine->get_cpu().get_x().get_value());
RegisterFlagSet flags{};
flags.zero = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDX_ABS_Y_NegativeFlag) {
// ensure bit 7 set
auto machine = create_machine({LDX_ABS_Y, 0x55, 0x66});
machine->get_cpu().get_y().set_value(0x4);
machine->get_memory().set_at(0x6659, 0xF6);
machine->execute();
ASSERT_EQ(0xF6, machine->get_cpu().get_x().get_value());
RegisterFlagSet flags{};
flags.negative = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDY_IMM) {
auto machine = create_machine({LDY_IMM, 36});
machine->execute();
ASSERT_EQ(36, machine->get_cpu().get_y().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDY_IMM_ZeroFlag) {
auto machine = create_machine({LDY_IMM, 0});
machine->execute();
ASSERT_EQ(0, machine->get_cpu().get_y().get_value());
RegisterFlagSet flags{};
flags.zero = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDY_IMM_NegativeFlag) {
auto machine = create_machine({LDY_IMM, 0xff});
machine->execute();
ASSERT_EQ(0xff, machine->get_cpu().get_y().get_value());
RegisterFlagSet flags{};
flags.negative = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDY_ZPG) {
auto machine = create_machine({LDY_ZPG, 123});
machine->get_memory().set_at(123, 78);
machine->execute();
ASSERT_EQ(78, machine->get_cpu().get_y().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDY_ZPG_ZeroFlag) {
auto machine = create_machine({LDY_ZPG, 123});
machine->get_memory().set_at(123, 0);
machine->execute();
ASSERT_EQ(0, machine->get_cpu().get_y().get_value());
RegisterFlagSet flags{};
flags.zero = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDY_ZPG_NegativeFlag) {
auto machine = create_machine({LDY_ZPG, 123});
// ensure bit 7 set
machine->get_memory().set_at(123, 150);
machine->execute();
ASSERT_EQ(150, machine->get_cpu().get_y().get_value());
RegisterFlagSet flags{};
flags.negative = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDY_ZPG_X) {
auto machine = create_machine({LDY_ZPG_X, 0x10});
machine->get_cpu().get_x().set_value(0x15);
machine->get_memory().set_at(0x25, 78);
machine->execute();
ASSERT_EQ(78, machine->get_cpu().get_y().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDY_ZPG_X_Wrap) {
auto machine = create_machine({LDY_ZPG_X, 0xFF});
machine->get_cpu().get_x().set_value(0x80);
machine->get_memory().set_at(0x7F, 78);
machine->execute();
ASSERT_EQ(78, machine->get_cpu().get_y().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDY_ZPG_X_ZeroFlag) {
auto machine = create_machine({LDY_ZPG_X, 0x10});
machine->get_cpu().get_x().set_value(0x15);
machine->get_memory().set_at(0x25, 0);
machine->execute();
ASSERT_EQ(0, machine->get_cpu().get_y().get_value());
RegisterFlagSet flags{};
flags.zero = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDY_ZPG_X_NegativeFlag) {
auto machine = create_machine({LDY_ZPG_X, 0x10});
machine->get_cpu().get_x().set_value(0x15);
// ensure bit 7 set
machine->get_memory().set_at(0x25, 0xF5);
machine->execute();
ASSERT_EQ(0xF5, machine->get_cpu().get_y().get_value());
RegisterFlagSet flags{};
flags.negative = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDY_ABS) {
auto machine = create_machine({LDY_ABS, 0x55, 0x66});
machine->get_memory().set_at(0x6655, 0x10);
machine->execute();
ASSERT_EQ(0x10, machine->get_cpu().get_y().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDY_ABS_ZeroFlag) {
auto machine = create_machine({LDY_ABS, 0x55, 0x66});
machine->get_memory().set_at(0x6655, 0x0);
machine->execute();
ASSERT_EQ(0, machine->get_cpu().get_y().get_value());
RegisterFlagSet flags{};
flags.zero = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDY_ABS_NegativeFlag) {
// ensure bit 7 set
auto machine = create_machine({LDY_ABS, 0x55, 0x66});
machine->get_memory().set_at(0x6655, 0xF6);
machine->execute();
ASSERT_EQ(0xF6, machine->get_cpu().get_y().get_value());
RegisterFlagSet flags{};
flags.negative = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDY_ABS_X) {
auto machine = create_machine({LDY_ABS_X, 0x55, 0x66});
machine->get_cpu().get_x().set_value(0x4);
machine->get_memory().set_at(0x6659, 0x10);
machine->execute();
ASSERT_EQ(0x10, machine->get_cpu().get_y().get_value());
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), RegisterFlagSet{}));
}
TEST(LoadOpcodeHandlerContainer, LDY_ABS_X_ZeroFlag) {
auto machine = create_machine({LDY_ABS_X, 0x55, 0x66});
machine->get_cpu().get_x().set_value(0x4);
machine->get_memory().set_at(0x6659, 0x0);
machine->execute();
ASSERT_EQ(0, machine->get_cpu().get_y().get_value());
RegisterFlagSet flags{};
flags.zero = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}
TEST(LoadOpcodeHandlerContainer, LDY_ABS_X_NegativeFlag) {
// ensure bit 7 set
auto machine = create_machine({LDY_ABS_X, 0x55, 0x66});
machine->get_cpu().get_x().set_value(0x4);
machine->get_memory().set_at(0x6659, 0xF6);
machine->execute();
ASSERT_EQ(0xF6, machine->get_cpu().get_y().get_value());
RegisterFlagSet flags{};
flags.negative = true;
ASSERT_TRUE(are_flags_set(machine->get_cpu().get_ps(), flags));
}

36
test/test-utils.h Normal file
View File

@ -0,0 +1,36 @@
#include "../src/opcode/opcode-handler-directory.h"
#include "../src/machine/status-register.h"
#include <memory>
#include <vector>
using namespace std;
using namespace emu_6502;
struct RegisterFlagSet {
bool carry;
bool zero;
bool interupt_disable;
bool decimal;
bool brk;
bool overflow;
bool negative;
};
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();
}