This commit is contained in:
Tony Di Nucci 2019-03-27 00:14:44 +00:00
commit e6df451eca
34 changed files with 1404 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.idea/
cmake-build-debug/

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/machine-test.cpp test/flags-opcode-handler-test.cpp)
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})

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 ""
)

View File

@ -0,0 +1,38 @@
#include "instruction-executor.h"
#include <stdexcept>
#include <iostream>
void dump_cpu_state(shared_ptr<RegisterManager> reg_man) {
cout << "--------------" << endl;
cout << "A=0x" << hex << (int) reg_man->get_accumulator()->get_value() <<
" X=0x" << hex << (int) reg_man->get_x_index()->get_value() <<
" Y=0x" << hex << (int) reg_man->get_y_index()->get_value() << endl;
cout << "SP=0x" << hex << (int) reg_man->get_stack_pointer()->get_value() <<
" PC=0x" << hex << (int) reg_man->get_program_counter()->get_value() << endl;
cout << "NV-BDIZC" << endl <<
reg_man->get_status_register()->get_value() << endl;
}
InstructionExecutor::InstructionExecutor(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man,
shared_ptr<OpcodeHandlerDirectory> opcode_handler_dir) {
this->program = program;
this->reg_man = reg_man;
this->opcode_handler_dir = opcode_handler_dir;
}
void InstructionExecutor::execute() {
auto pc = reg_man->get_program_counter();
do {
auto opcode = program->get_byte_at(pc->get_value());
auto handler = opcode_handler_dir->get_handler(opcode);
handler->execute();
dump_cpu_state(reg_man);
} while (pc->get_value() < program->get_size());
}

View File

@ -0,0 +1,27 @@
#ifndef INC_6502_EMULATOR_INSTRUCTION_EXECUTOR_H
#define INC_6502_EMULATOR_INSTRUCTION_EXECUTOR_H
#include "../program.h"
#include "../register/register-manager.h"
#include "../opcode/opcode-handler-directory.h"
#include <memory>
#include <vector>
using namespace std;
class InstructionExecutor {
public:
explicit InstructionExecutor(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man,
shared_ptr<OpcodeHandlerDirectory> opcode_handler_dir);
void execute();
private:
shared_ptr<Program> program;
shared_ptr<RegisterManager> reg_man;
shared_ptr<OpcodeHandlerDirectory> opcode_handler_dir;
};
#endif //INC_6502_EMULATOR_INSTRUCTION_EXECUTOR_H

24
src/machine.cpp Normal file
View File

@ -0,0 +1,24 @@
#include "machine.h"
#include <iostream>
Machine::Machine(shared_ptr<Program> program) {
this->program = program;
memory = make_shared<Memory>(Memory());
reg_man = make_shared<RegisterManager>(RegisterManager());
opcode_handler_dir = make_shared<OpcodeHandlerDirectory>(OpcodeHandlerDirectory(program, reg_man));
instruction_executor =
make_unique<InstructionExecutor>(InstructionExecutor(program, reg_man, opcode_handler_dir));
}
shared_ptr<Memory> Machine::get_memory() {
return memory;
}
shared_ptr<RegisterManager> Machine::get_reg_man() {
return reg_man;
}
void Machine::execute() {
instruction_executor->execute();
}

31
src/machine.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef INC_6502_EMULATOR_MACHINE_H
#define INC_6502_EMULATOR_MACHINE_H
#include "program.h"
#include "memory.h"
#include "register/register-manager.h"
#include "opcode/opcode-handler-directory.h"
#include "instruction/instruction-executor.h"
#include <vector>
using namespace std;
class Machine {
public:
explicit Machine(shared_ptr<Program> program);
shared_ptr<RegisterManager> get_reg_man();
shared_ptr<Memory> get_memory();
void execute();
private:
shared_ptr<Program> program;
shared_ptr<Memory> memory;
shared_ptr<RegisterManager> reg_man;
shared_ptr<OpcodeHandlerDirectory> opcode_handler_dir;
shared_ptr<InstructionExecutor> instruction_executor;
};
#endif //INC_6502_EMULATOR_MACHINE_H

42
src/main.cpp Normal file
View File

@ -0,0 +1,42 @@
#include "register/register-manager.h"
#include <iostream>
#include <memory>
using namespace std;
void stack_test(string astring) {
astring.push_back('x');
}
void heap_test(shared_ptr<string> astring){
astring->push_back('y');
}
int main() {
auto reg = unique_ptr<Register<uint8_t>>(new Register<uint8_t>("PC"));
cout << "Register: " << reg->get_name() << " " << to_string(56) << endl;
string astr = "hello";
shared_ptr<string> bstr = make_shared<string>("there");
cout << astr << " - " << bstr->c_str() << endl;
stack_test(astr);
heap_test(bstr);
cout << astr << " - " << bstr->c_str() << endl;
auto reg_man = new RegisterManager();
auto accum = reg_man->get_accumulator();
cout << "****** " << accum->get_name() << " ******" << endl;
cout << "****** " << reg_man->get_x_index()->get_name() << " ******" << endl;
cout << "****** " << reg_man->get_y_index()->get_name() << " ******" << endl;
cout << "****** " << reg_man->get_stack_pointer()->get_name() << " ******" << endl;
cout << "****** " << reg_man->get_status_register()->get_name() << " ******" << endl;
cout << "****** " << reg_man->get_program_counter()->get_name() << " ******" << endl;
return 0;
}

14
src/memory.cpp Normal file
View File

@ -0,0 +1,14 @@
#include "memory.h"
#include <stdexcept>
Memory::Memory() {
bytes = make_shared<vector<uint8_t>>(vector<uint8_t>((1024 * 64) - 1));
}
uint8_t Memory::get_byte_at(uint16_t index) {
auto size = bytes->size();
if (size == 0 || bytes->size() - 1 < index)
throw runtime_error("Attempted to read past end of memory");
return bytes->at(index);
}

21
src/memory.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef INC_6502_EMULATOR_MEMORY_H
#define INC_6502_EMULATOR_MEMORY_H
#include <memory>
#include <vector>
using namespace std;
class Memory {
public:
explicit Memory();
uint8_t get_byte_at(uint16_t index);
private:
shared_ptr<vector<uint8_t>> bytes;
};
#endif //INC_6502_EMULATOR_MEMORY_H

View File

@ -0,0 +1,48 @@
#include "flag-opcode-handler.h"
#include <stdexcept>
#include <sstream>
const uint8_t FlagOpcodeHandler::CLC;
const uint8_t FlagOpcodeHandler::SEC;
const uint8_t FlagOpcodeHandler::CLI;
const uint8_t FlagOpcodeHandler::SEI;
const uint8_t FlagOpcodeHandler::CLV;
const uint8_t FlagOpcodeHandler::CLD;
const uint8_t FlagOpcodeHandler::SED;
void FlagOpcodeHandler::execute() {
auto instructionIndex = reg_man->get_program_counter()->get_value();
auto opcode = program->get_byte_at(instructionIndex);
auto statusReg = reg_man->get_status_register();
switch (opcode) {
case CLC:
statusReg->set_carry(false);
break;
case SEC:
statusReg->set_carry(true);
break;
case CLI:
statusReg->set_interupt_disable(false);
break;
case SEI:
statusReg->set_interupt_disable(true);
break;
case CLV:
statusReg->set_overflow(false);
break;
case CLD:
statusReg->set_decimal(false);
break;
case SED:
statusReg->set_decimal(true);
break;
default:
stringstream stream;
stream << "Unexpected opcode 0x" << hex << opcode;
throw runtime_error(stream.str());
}
move_program_counter(1);
}

View File

@ -0,0 +1,31 @@
#ifndef INC_6502_EMULATOR_FLAG_OPCODE_HANDLER_H
#define INC_6502_EMULATOR_FLAG_OPCODE_HANDLER_H
#include "../../program.h"
#include "../opcode-handler.h"
class FlagOpcodeHandler : public OpcodeHandler {
public:
static const uint8_t CLC = 0x18;
static const uint8_t SEC = 0x38;
static const uint8_t CLI = 0x58;
static const uint8_t SEI = 0x78;
static const uint8_t CLV = 0xB8;
static const uint8_t CLD = 0xD8;
static const uint8_t SED = 0xF8;
explicit FlagOpcodeHandler(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man) :
OpcodeHandler(program, reg_man) {
handled_opcodes->push_back(CLC);
handled_opcodes->push_back(SEC);
handled_opcodes->push_back(CLI);
handled_opcodes->push_back(SEI);
handled_opcodes->push_back(CLV);
handled_opcodes->push_back(CLD);
handled_opcodes->push_back(SED);
}
virtual void execute() override;
};
#endif //INC_6502_EMULATOR_FLAG_OPCODE_HANDLER_H

View File

@ -0,0 +1,30 @@
#include "ldx-opcode-handler.h"
#include <sstream>
const uint8_t LdxOpcodeHandler::IMMEDIATE;
const uint8_t LdxOpcodeHandler::ZERO_PAGE;
const uint8_t LdxOpcodeHandler::ZERO_PAGE_X;
const uint8_t LdxOpcodeHandler::ABSOLUTE;
const uint8_t LdxOpcodeHandler::ABSOLUTE_X;
const uint8_t LdxOpcodeHandler::ABSOLUTE_Y;
const uint8_t LdxOpcodeHandler::INDIRECT_X;
const uint8_t LdxOpcodeHandler::INDIRECT_Y;
void LdxOpcodeHandler::execute() {
auto instructionIndex = reg_man->get_program_counter()->get_value();
auto opcode = program->get_byte_at(instructionIndex);
auto byte2 = program->get_byte_at(instructionIndex + 1);
auto accumulator = reg_man->get_accumulator();
switch (opcode) {
case IMMEDIATE:
accumulator->set_value(byte2);
move_program_counter(2);
break;
default:
stringstream stream;
stream << "Unexpected opcode 0x" << hex << opcode;
throw runtime_error(stream.str());
}
}

View File

@ -0,0 +1,33 @@
#ifndef INC_6502_EMULATOR_LDX_OPCODE_HANDLER_H
#define INC_6502_EMULATOR_LDX_OPCODE_HANDLER_H
#include "../../program.h"
#include "../opcode-handler.h"
class LdxOpcodeHandler : public OpcodeHandler {
public:
static const uint8_t IMMEDIATE = 0xA9;
static const uint8_t ZERO_PAGE = 0xA5;
static const uint8_t ZERO_PAGE_X = 0xB5;
static const uint8_t ABSOLUTE = 0xAD;
static const uint8_t ABSOLUTE_X = 0xBD;
static const uint8_t ABSOLUTE_Y = 0xB9;
static const uint8_t INDIRECT_X = 0xA1;
static const uint8_t INDIRECT_Y = 0xB1;
explicit LdxOpcodeHandler(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man) :
OpcodeHandler(program, reg_man) {
handled_opcodes->push_back(IMMEDIATE);
handled_opcodes->push_back(ZERO_PAGE);
handled_opcodes->push_back(ZERO_PAGE_X);
handled_opcodes->push_back(ABSOLUTE);
handled_opcodes->push_back(ABSOLUTE_X);
handled_opcodes->push_back(ABSOLUTE_Y);
handled_opcodes->push_back(INDIRECT_X);
handled_opcodes->push_back(INDIRECT_Y);
}
virtual void execute() override;
};
#endif //INC_6502_EMULATOR_LDX_OPCODE_HANDLER_H

View File

@ -0,0 +1,54 @@
#include "register-opcode-handler.h"
#include <stdexcept>
#include <sstream>
const uint8_t RegisterOpcodeHandler::TAX;
const uint8_t RegisterOpcodeHandler::TXA;
const uint8_t RegisterOpcodeHandler::DEX;
const uint8_t RegisterOpcodeHandler::INX;
const uint8_t RegisterOpcodeHandler::TAY;
const uint8_t RegisterOpcodeHandler::TYA;
const uint8_t RegisterOpcodeHandler::DEY;
const uint8_t RegisterOpcodeHandler::INY;
void RegisterOpcodeHandler::execute() {
auto instructionIndex = reg_man->get_program_counter()->get_value();
auto opcode = program->get_byte_at(instructionIndex);
auto accumulator = reg_man->get_accumulator();
auto x_index = reg_man->get_x_index();
auto y_index = reg_man->get_y_index();
switch (opcode) {
case TAX:
x_index->set_value(accumulator->get_value());
break;
case TXA:
accumulator->set_value(x_index->get_value());
break;
case DEX:
x_index->set_value(x_index->get_value() - 1);
break;
case INX:
x_index->set_value(x_index->get_value() + 1);
break;
case TAY:
y_index->set_value(accumulator->get_value());
break;
case TYA:
accumulator->set_value(y_index->get_value());
break;
case DEY:
y_index->set_value(y_index->get_value() - 1);
break;
case INY:
y_index->set_value(y_index->get_value() + 1);
break;
default:
stringstream stream;
stream << "Unexpected opcode 0x" << hex << opcode;
throw runtime_error(stream.str());
}
move_program_counter(1);
}

View File

@ -0,0 +1,34 @@
#ifndef INC_6502_EMULATOR_REGISTER_OPCODE_HANDLER_H
#define INC_6502_EMULATOR_REGISTER_OPCODE_HANDLER_H
#include "../../program.h"
#include "../opcode-handler.h"
class RegisterOpcodeHandler : public OpcodeHandler {
public:
static const uint8_t TAX = 0xAA;
static const uint8_t TXA = 0x8A;
static const uint8_t DEX = 0xCA;
static const uint8_t INX = 0xE8;
static const uint8_t TAY = 0xA8;
static const uint8_t TYA = 0x98;
static const uint8_t DEY = 0x88;
static const uint8_t INY = 0xC8;
explicit RegisterOpcodeHandler(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man) :
OpcodeHandler(program, reg_man) {
handled_opcodes->push_back(TAX);
handled_opcodes->push_back(TXA);
handled_opcodes->push_back(DEX);
handled_opcodes->push_back(INX);
handled_opcodes->push_back(TAY);
handled_opcodes->push_back(TYA);
handled_opcodes->push_back(DEY);
handled_opcodes->push_back(INY);
}
virtual void execute() override;
};
#endif //INC_6502_EMULATOR_REGISTER_OPCODE_HANDLER_H

View File

@ -0,0 +1,31 @@
#include "opcode-handler-directory.h"
#include "handler/flag-opcode-handler.h"
#include "handler/register-opcode-handler.h"
#include "handler/ldx-opcode-handler.h"
#include <sstream>
#include <stdexcept>
OpcodeHandlerDirectory::OpcodeHandlerDirectory(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man) {
handlers = make_unique<unordered_map<uint8_t, shared_ptr<OpcodeHandler>>>();
register_handler(make_shared<FlagOpcodeHandler>(program, reg_man));
register_handler(make_shared<RegisterOpcodeHandler>(program, reg_man));
register_handler(make_shared<LdxOpcodeHandler>(program, reg_man));
}
void OpcodeHandlerDirectory::register_handler(shared_ptr<OpcodeHandler> handler) {
for (auto opcode: *handler->get_handled_opcodes()) {
handlers->insert({opcode, handler});
}
}
shared_ptr<OpcodeHandler> OpcodeHandlerDirectory::get_handler(uint8_t opcode) {
if (handlers->find(opcode) == handlers->end()) {
stringstream stream;
stream << "No handler registered for opcode 0x" << hex << opcode;
throw runtime_error(stream.str());
}
return handlers->at(opcode);
}

View File

@ -0,0 +1,24 @@
#ifndef INC_6502_EMULATOR_OPCODE_HANDLER_DIRECTORY_H
#define INC_6502_EMULATOR_OPCODE_HANDLER_DIRECTORY_H
#include "../program.h"
#include "opcode-handler.h"
#include <unordered_map>
#include <vector>
#include <memory>
using namespace std;
class OpcodeHandlerDirectory {
public:
explicit OpcodeHandlerDirectory(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man);
shared_ptr<OpcodeHandler> get_handler(uint8_t opcode);
private:
unique_ptr<unordered_map<uint8_t, shared_ptr<OpcodeHandler>>> handlers;
void register_handler(shared_ptr<OpcodeHandler> handler);
};
#endif //INC_6502_EMULATOR_OPCODE_HANDLER_DIRECTORY_H

View File

@ -0,0 +1,12 @@
#include "../program.h"
#include "opcode-handler.h"
OpcodeHandler::OpcodeHandler(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man) {
this->program = program;
this->reg_man = reg_man;
}
void OpcodeHandler::move_program_counter(uint8_t forward_by) {
auto pc = reg_man->get_program_counter();
pc->set_value(pc->get_value() + forward_by);
}

View File

@ -0,0 +1,28 @@
#ifndef INC_6502_EMULATOR_OPCODE_HANDLER_H
#define INC_6502_EMULATOR_OPCODE_HANDLER_H
#include "../program.h"
#include "../register/register-manager.h"
#include <vector>
#include <memory>
using namespace std;
class OpcodeHandler {
public:
OpcodeHandler(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man);
shared_ptr<vector<uint8_t>> get_handled_opcodes() { return handled_opcodes; }
virtual void execute() = 0;
protected:
shared_ptr<vector<uint8_t>> handled_opcodes = make_shared<vector<uint8_t>>();
shared_ptr<Program> program;
shared_ptr<RegisterManager> reg_man;
void move_program_counter(uint8_t forward_by);
};
#endif //INC_6502_EMULATOR_OPCODE_HANDLER_H

19
src/program.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "program.h"
#include <stdexcept>
Program::Program(shared_ptr<vector<uint8_t>> bytes) {
this->bytes = bytes;
}
uint16_t Program::get_size() {
return bytes->size();
}
uint8_t Program::get_byte_at(uint16_t index) {
auto size = bytes->size();
if (size == 0 || bytes->size() - 1 < index)
throw runtime_error("Attempted to read past end of program");
return bytes->at(index);
}

21
src/program.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef INC_6502_EMULATOR_PROGRAM_H
#define INC_6502_EMULATOR_PROGRAM_H
#include <memory>
#include <vector>
using namespace std;
class Program {
public:
explicit Program(shared_ptr<vector<uint8_t>> bytes);
uint8_t get_byte_at(uint16_t index);
uint16_t get_size();
private:
shared_ptr<vector<uint8_t>> bytes;
};
#endif //INC_6502_EMULATOR_PROGRAM_H

View File

@ -0,0 +1,39 @@
#include "register-manager.h"
#include "register.h"
#include <memory>
#include <cstring>
using namespace std;
RegisterManager::RegisterManager() {
accumulator = std::make_shared<Register<uint8_t>>(Register<uint8_t>("A"));
x_index = make_shared<Register<uint8_t>>(Register<uint8_t>("X"));
y_index = make_shared<Register<uint8_t>>(Register<uint8_t>("Y"));
stack_pointer = make_shared<Register<uint8_t>>(Register<uint8_t>("SP"));
status_register = make_shared<StatusRegister>(StatusRegister());
program_counter = make_shared<Register<uint16_t>>(Register<uint16_t>("PC"));
}
shared_ptr<Register<uint8_t>> RegisterManager::get_accumulator() {
return accumulator;
}
shared_ptr<Register<uint8_t>> RegisterManager::get_x_index() {
return x_index;
}
shared_ptr<Register<uint8_t>> RegisterManager::get_y_index() {
return y_index;
}
shared_ptr<Register<uint8_t>> RegisterManager::get_stack_pointer() {
return stack_pointer;
}
shared_ptr<StatusRegister> RegisterManager::get_status_register() {
return status_register;
}
shared_ptr<Register<uint16_t>> RegisterManager::get_program_counter() {
return program_counter;
}

View File

@ -0,0 +1,36 @@
#ifndef INC_6502_EMULATOR_REGISTER_MANAGER_H
#define INC_6502_EMULATOR_REGISTER_MANAGER_H
#include "register.h"
#include "status-register.h"
#include <memory>
using namespace std;
class RegisterManager {
public:
explicit RegisterManager();
shared_ptr<Register<uint8_t>> get_accumulator();
shared_ptr<Register<uint8_t>> get_x_index();
shared_ptr<Register<uint8_t>> get_y_index();
shared_ptr<Register<uint8_t>> get_stack_pointer();
shared_ptr<StatusRegister> get_status_register();
shared_ptr<Register<uint16_t>> get_program_counter();
private:
shared_ptr<Register<uint8_t>> accumulator;
shared_ptr<Register<uint8_t>> x_index;
shared_ptr<Register<uint8_t>> y_index;
shared_ptr<Register<uint8_t>> stack_pointer;
shared_ptr<StatusRegister> status_register;
shared_ptr<Register<uint16_t>> program_counter;
};
#endif //INC_6502_EMULATOR_REGISTER_MANAGER_H

30
src/register/register.cpp Normal file
View File

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

28
src/register/register.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef INC_6502_EMULATOR_REGISTER_H
#define INC_6502_EMULATOR_REGISTER_H
#include <cstdint>
#include <string>
using namespace std;
template <class T>
class Register
{
public:
explicit Register<T>(string name);
string get_name();
T get_value();
void set_value(T value);
protected:
T value;
private:
string name;
};
#endif //INC_6502_EMULATOR_REGISTER_H

View File

@ -0,0 +1,65 @@
#include "status-register.h"
const uint8_t CARRY_FLAG = 0;
const uint8_t ZERO_FLAG = 1;
const uint8_t INTERUPT_DISABLE_FLAG = 2;
const uint8_t DECIMAL_FLAG = 3;
const uint8_t BREAK_FLAG = 4;
const uint8_t OVERFLOW_FLAG = 6;
const uint8_t NEGATIVE_FLAG = 7;
bool StatusRegister::is_carry_set() {
return this->value[CARRY_FLAG];
}
bool StatusRegister::is_zero_set() {
return this->value[ZERO_FLAG];
}
bool StatusRegister::is_interupt_disable_set() {
return this->value[INTERUPT_DISABLE_FLAG];
}
bool StatusRegister::is_decimal_set() {
return this->value[DECIMAL_FLAG];
}
bool StatusRegister::is_break_set() {
return this->value[BREAK_FLAG];
}
bool StatusRegister::is_overflow_set() {
return this->value[OVERFLOW_FLAG];
}
bool StatusRegister::is_negative_set() {
return this->value[NEGATIVE_FLAG];
}
void StatusRegister::set_carry(bool state) {
this->value[CARRY_FLAG] = state;
}
void StatusRegister::set_zero(bool state) {
this->value[ZERO_FLAG] = state;
}
void StatusRegister::set_interupt_disable(bool state) {
this->value[INTERUPT_DISABLE_FLAG] = state;
}
void StatusRegister::set_decimal(bool state) {
this->value[DECIMAL_FLAG] = state;
}
void StatusRegister::set_break(bool state) {
this->value[BREAK_FLAG] = state;
}
void StatusRegister::set_overflow(bool state) {
this->value[OVERFLOW_FLAG] = state;
}
void StatusRegister::set_negative(bool state) {
this->value[NEGATIVE_FLAG] = state;
}

View File

@ -0,0 +1,29 @@
#ifndef INC_6502_EMULATOR_STATUS_REGISTER_H
#define INC_6502_EMULATOR_STATUS_REGISTER_H
#include "register.h"
#include <bitset>
class StatusRegister : public Register<bitset<8>> {
public:
explicit StatusRegister() : Register<bitset<8>>("P") {}
bool is_carry_set();
bool is_zero_set();
bool is_interupt_disable_set();
bool is_decimal_set();
bool is_break_set();
bool is_overflow_set();
bool is_negative_set();
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

View File

@ -0,0 +1,112 @@
#include "gtest/gtest.h"
#include "../src/machine.h"
#include "../src/program.h"
#include <memory>
#include <vector>
#include <sstream>
#include <iostream>
using namespace std;
const uint8_t CLC = 0x18;
const uint8_t SEC = 0x38;
const uint8_t CLI = 0x58;
const uint8_t SEI = 0x78;
const uint8_t CLV = 0xB8;
const uint8_t CLD = 0xD8;
const uint8_t SED = 0xF8;
unique_ptr<Machine> ft_get_machine(shared_ptr<vector<uint8_t>> code){
auto program = make_shared<Program>(Program(code));
auto machine = make_unique<Machine>(Machine(program));
return machine;
}
TEST(FlagOpcodeHandlerTest, ClearCarry) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{CLC});
auto machine = ft_get_machine(code);
auto status = machine->get_reg_man()->get_status_register();
ASSERT_EQ(0, status->is_carry_set());
status->set_carry(true);
ASSERT_EQ(1, status->is_carry_set());
machine->execute();
ASSERT_EQ(0, status->is_carry_set());
}
TEST(FlagOpcodeHandlerTest, SetCarry) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{SEC});
auto machine = ft_get_machine(code);
auto status = machine->get_reg_man()->get_status_register();
ASSERT_EQ(0, status->is_carry_set());
machine->execute();
ASSERT_EQ(1, status->is_carry_set());
}
TEST(FlagOpcodeHandlerTest, ClearInteruptDisable) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{CLI});
auto machine = ft_get_machine(code);
auto status = machine->get_reg_man()->get_status_register();
ASSERT_EQ(0, status->is_interupt_disable_set());
status->set_interupt_disable(true);
ASSERT_EQ(1, status->is_interupt_disable_set());
machine->execute();
ASSERT_EQ(0, status->is_interupt_disable_set());
}
TEST(FlagOpcodeHandlerTest, SetInteruptDisable) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{SEI});
auto machine = ft_get_machine(code);
auto status = machine->get_reg_man()->get_status_register();
ASSERT_EQ(0, status->is_interupt_disable_set());
machine->execute();
ASSERT_EQ(1, status->is_interupt_disable_set());
}
TEST(FlagOpcodeHandlerTest, ClearOverflow) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{CLV});
auto machine = ft_get_machine(code);
auto status = machine->get_reg_man()->get_status_register();
ASSERT_EQ(0, status->is_overflow_set());
status->set_overflow(true);
ASSERT_EQ(1, status->is_overflow_set());
machine->execute();
ASSERT_EQ(0, status->is_overflow_set());
}
TEST(FlagOpcodeHandlerTest, ClearDecimal) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{CLD});
auto machine = ft_get_machine(code);
auto status = machine->get_reg_man()->get_status_register();
ASSERT_EQ(0, status->is_decimal_set());
status->set_decimal(true);
ASSERT_EQ(1, status->is_decimal_set());
machine->execute();
ASSERT_EQ(0, status->is_decimal_set());
}
TEST(FlagOpcodeHandlerTest, SetDecimal) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{SED});
auto machine = ft_get_machine(code);
auto status = machine->get_reg_man()->get_status_register();
ASSERT_EQ(0, status->is_decimal_set());
machine->execute();
ASSERT_EQ(1, status->is_decimal_set());
}

View File

@ -0,0 +1,29 @@
#include "gtest/gtest.h"
#include "../src/program.h"
#include "../src/instruction/instruction-executor.h"
#include "../src/register/register-manager.h"
#include "../src/opcode/opcode-handler-directory.h"
#include <memory>
#include <vector>
#include <sstream>
#include <iostream>
using namespace std;
const uint8_t CLC = 0x18;
const uint8_t SEC = 0x38;
const uint8_t LDX_IMMEDIATE = 0xA9;
TEST(InstructionExecutor, Execute) {
auto reg_man = make_shared<RegisterManager>(RegisterManager());
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>
{SEC,
CLC
});
auto program = make_shared<Program>(Program(code));
auto opcode_handler_dir = make_shared<OpcodeHandlerDirectory>(OpcodeHandlerDirectory(program, reg_man));
auto executor = make_unique<InstructionExecutor>(InstructionExecutor(program, reg_man, opcode_handler_dir));
executor->execute();
}

27
test/machine-test.cpp Normal file
View File

@ -0,0 +1,27 @@
#include "gtest/gtest.h"
#include "../src/machine.h"
#include "../src/program.h"
#include <memory>
#include <vector>
#include <sstream>
#include <iostream>
using namespace std;
const uint8_t CLC = 0x18;
const uint8_t SEC = 0x38;
const uint8_t CLI = 0x58;
const uint8_t SEI = 0x78;
const uint8_t CLV = 0xB8;
const uint8_t CLD = 0xD8;
const uint8_t SED = 0xF8;
const uint8_t LDX_IMMEDIATE = 0xA9;
TEST(Machine, Execute) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{SEC, SEI, SED, CLI, CLV, CLD, CLC, LDX_IMMEDIATE, 0x6B});
auto program = make_shared<Program>(Program(code));
auto machine = make_unique<Machine>(Machine(program));
machine->execute();
}

View File

@ -0,0 +1,219 @@
#include "gtest/gtest.h"
#include "../src/machine.h"
#include "../src/program.h"
#include <memory>
#include <vector>
using namespace std;
const uint8_t TAX = 0xAA;
const uint8_t TXA = 0x8A;
const uint8_t DEX = 0xCA;
const uint8_t INX = 0xE8;
const uint8_t TAY = 0xA8;
const uint8_t TYA = 0x98;
const uint8_t DEY = 0x88;
const uint8_t INY = 0xC8;
unique_ptr<Machine> rt_get_machine(shared_ptr<vector<uint8_t>> code) {
auto program = make_shared<Program>(Program(code));
auto machine = make_unique<Machine>(Machine(program));
return machine;
}
TEST(RegisterOpcodeHandler, TransferAtoX) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{TAX});
auto machine = rt_get_machine(code);
auto reg_man = machine->get_reg_man();
auto aval = reg_man->get_accumulator()->get_value();
auto xval = reg_man->get_x_index()->get_value();
ASSERT_EQ(0, aval);
ASSERT_EQ(0, xval);
machine->execute();
aval = reg_man->get_accumulator()->get_value();
xval = reg_man->get_x_index()->get_value();
ASSERT_EQ(0, aval);
ASSERT_EQ(0, xval);
reg_man->get_program_counter()->set_value(0);
reg_man->get_accumulator()->set_value(0x3e);
machine->execute();
aval = reg_man->get_accumulator()->get_value();
xval = reg_man->get_x_index()->get_value();
ASSERT_EQ(0x3e, aval);
ASSERT_EQ(0x3e, xval);
}
TEST(RegisterOpcodeHandler, TransferXtoA) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{TXA});
auto machine = rt_get_machine(code);
auto reg_man = machine->get_reg_man();
auto aval = reg_man->get_accumulator()->get_value();
auto xval = reg_man->get_x_index()->get_value();
ASSERT_EQ(0, aval);
ASSERT_EQ(0, xval);
machine->execute();
aval = reg_man->get_accumulator()->get_value();
xval = reg_man->get_x_index()->get_value();
ASSERT_EQ(0, aval);
ASSERT_EQ(0, xval);
reg_man->get_program_counter()->set_value(0);
reg_man->get_x_index()->set_value(0x4f);
machine->execute();
aval = reg_man->get_accumulator()->get_value();
xval = reg_man->get_x_index()->get_value();
ASSERT_EQ(0x4f, aval);
ASSERT_EQ(0x4f, xval);
}
TEST(RegisterOpcodeHandler, DecrementX) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{DEX});
auto machine = rt_get_machine(code);
auto reg_man = machine->get_reg_man();
auto xval = reg_man->get_x_index()->get_value();
ASSERT_EQ(0, xval);
machine->execute();
xval = reg_man->get_x_index()->get_value();
ASSERT_EQ(0xff, xval);
reg_man->get_program_counter()->set_value(0);
machine->execute();
xval = reg_man->get_x_index()->get_value();
ASSERT_EQ(0xfe, xval);
reg_man->get_program_counter()->set_value(0);
reg_man->get_x_index()->set_value(0x45);
machine->execute();
xval = reg_man->get_x_index()->get_value();
ASSERT_EQ(0x44, xval);
}
TEST(RegisterOpcodeHandler, IncrementX) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{INX});
auto machine = rt_get_machine(code);
auto reg_man = machine->get_reg_man();
auto xval = reg_man->get_x_index()->get_value();
ASSERT_EQ(0, xval);
machine->execute();
xval = reg_man->get_x_index()->get_value();
ASSERT_EQ(0x01, xval);
reg_man->get_program_counter()->set_value(0);
machine->execute();
xval = reg_man->get_x_index()->get_value();
ASSERT_EQ(0x02, xval);
reg_man->get_program_counter()->set_value(0);
reg_man->get_x_index()->set_value(0x45);
machine->execute();
xval = reg_man->get_x_index()->get_value();
ASSERT_EQ(0x46, xval);
}
TEST(RegisterOpcodeHandler, TransferAtoY) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{TAY});
auto machine = rt_get_machine(code);
auto reg_man = machine->get_reg_man();
auto aval = reg_man->get_accumulator()->get_value();
auto yval = reg_man->get_y_index()->get_value();
ASSERT_EQ(0, aval);
ASSERT_EQ(0, yval);
machine->execute();
aval = reg_man->get_accumulator()->get_value();
yval = reg_man->get_y_index()->get_value();
ASSERT_EQ(0, aval);
ASSERT_EQ(0, yval);
reg_man->get_program_counter()->set_value(0);
reg_man->get_accumulator()->set_value(0xaf);
machine->execute();
aval = reg_man->get_accumulator()->get_value();
yval = reg_man->get_y_index()->get_value();
ASSERT_EQ(0xaf, aval);
ASSERT_EQ(0xaf, yval);
}
TEST(RegisterOpcodeHandler, TransferYtoA) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{TYA});
auto machine = rt_get_machine(code);
auto reg_man = machine->get_reg_man();
auto aval = reg_man->get_accumulator()->get_value();
auto yval = reg_man->get_y_index()->get_value();
ASSERT_EQ(0, aval);
ASSERT_EQ(0, yval);
machine->execute();
aval = reg_man->get_accumulator()->get_value();
yval = reg_man->get_y_index()->get_value();
ASSERT_EQ(0, aval);
ASSERT_EQ(0, yval);
reg_man->get_program_counter()->set_value(0);
reg_man->get_y_index()->set_value(0xff);
machine->execute();
aval = reg_man->get_accumulator()->get_value();
yval = reg_man->get_y_index()->get_value();
ASSERT_EQ(0xff, aval);
ASSERT_EQ(0xff, yval);
}
TEST(RegisterOpcodeHandler, DecrementY) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{DEY});
auto machine = rt_get_machine(code);
auto reg_man = machine->get_reg_man();
auto yval = reg_man->get_y_index()->get_value();
ASSERT_EQ(0, yval);
machine->execute();
yval = reg_man->get_y_index()->get_value();
ASSERT_EQ(0xff, yval);
reg_man->get_program_counter()->set_value(0);
machine->execute();
yval = reg_man->get_y_index()->get_value();
ASSERT_EQ(0xfe, yval);
reg_man->get_program_counter()->set_value(0);
reg_man->get_y_index()->set_value(0x3b);
machine->execute();
yval = reg_man->get_y_index()->get_value();
ASSERT_EQ(0x3a, yval);
}
TEST(RegisterOpcodeHandler, IncrementY) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{INY});
auto machine = rt_get_machine(code);
auto reg_man = machine->get_reg_man();
auto yval = reg_man->get_y_index()->get_value();
ASSERT_EQ(0, yval);
machine->execute();
yval = reg_man->get_y_index()->get_value();
ASSERT_EQ(0x01, yval);
reg_man->get_program_counter()->set_value(0);
machine->execute();
yval = reg_man->get_y_index()->get_value();
ASSERT_EQ(0x02, yval);
reg_man->get_program_counter()->set_value(0);
reg_man->get_y_index()->set_value(0x3b);
machine->execute();
yval = reg_man->get_y_index()->get_value();
ASSERT_EQ(0x3c, yval);
}

8
test/register-test.cpp Normal file
View File

@ -0,0 +1,8 @@
#include "gtest/gtest.h"
#include "../src/register/register.h"
TEST(Construct, Equals) {
auto reg = new Register<uint16_t>("PC");
EXPECT_EQ("PC", reg->get_name());
}

View File

@ -0,0 +1,170 @@
#include "gtest/gtest.h"
#include "../src/register/status-register.h"
#include <memory>
#include <sstream>
#include <iostream>
using namespace std;
string get_bit_pattern(StatusRegister* reg) {
stringstream stream;
stream << reg->get_value();
return stream.str();
}
TEST(StatusRegister, Construct) {
auto reg = make_unique<StatusRegister>(StatusRegister());
auto bit_pattern = get_bit_pattern(reg.get());
EXPECT_EQ("P", reg->get_name());
EXPECT_EQ("00000000", bit_pattern);
EXPECT_EQ(false, reg->is_carry_set());
EXPECT_EQ(false, reg->is_zero_set());
EXPECT_EQ(false, reg->is_interupt_disable_set());
EXPECT_EQ(false, reg->is_decimal_set());
EXPECT_EQ(false, reg->is_break_set());
EXPECT_EQ(false, reg->is_overflow_set());
EXPECT_EQ(false, reg->is_negative_set());
}
TEST(StatusRegister, CarryFlag) {
auto reg = make_unique<StatusRegister>(StatusRegister());
EXPECT_EQ(false, reg->is_carry_set());
reg->set_carry(true);
EXPECT_EQ(true, reg->is_carry_set());
auto bit_pattern = get_bit_pattern(reg.get());
EXPECT_EQ("00000001", bit_pattern);
reg->set_carry(false);
EXPECT_EQ(false, reg->is_carry_set());
bit_pattern = get_bit_pattern(reg.get());
EXPECT_EQ("00000000", bit_pattern);
}
TEST(StatusRegister, ZeroFlag) {
auto reg = make_unique<StatusRegister>(StatusRegister());
EXPECT_EQ(false, reg->is_zero_set());
reg->set_zero(true);
EXPECT_EQ(true, reg->is_zero_set());
auto bit_pattern = get_bit_pattern(reg.get());
EXPECT_EQ("00000010", bit_pattern);
reg->set_zero(false);
EXPECT_EQ(false, reg->is_zero_set());
bit_pattern = get_bit_pattern(reg.get());
EXPECT_EQ("00000000", bit_pattern);
}
TEST(StatusRegister, InteruptDisableFlag) {
auto reg = make_unique<StatusRegister>(StatusRegister());
EXPECT_EQ(false, reg->is_interupt_disable_set());
reg->set_interupt_disable(true);
EXPECT_EQ(true, reg->is_interupt_disable_set());
auto bit_pattern = get_bit_pattern(reg.get());
EXPECT_EQ("00000100", bit_pattern);
reg->set_interupt_disable(false);
EXPECT_EQ(false, reg->is_interupt_disable_set());
bit_pattern = get_bit_pattern(reg.get());
EXPECT_EQ("00000000", bit_pattern);
}
TEST(StatusRegister, DecimalFlag) {
auto reg = make_unique<StatusRegister>(StatusRegister());
EXPECT_EQ(false, reg->is_decimal_set());
reg->set_decimal(true);
EXPECT_EQ(true, reg->is_decimal_set());
auto bit_pattern = get_bit_pattern(reg.get());
EXPECT_EQ("00001000", bit_pattern);
reg->set_decimal(false);
EXPECT_EQ(false, reg->is_decimal_set());
bit_pattern = get_bit_pattern(reg.get());
EXPECT_EQ("00000000", bit_pattern);
}
TEST(StatusRegister, BreakFlag) {
auto reg = make_unique<StatusRegister>(StatusRegister());
EXPECT_EQ(false, reg->is_break_set());
reg->set_break(true);
EXPECT_EQ(true, reg->is_break_set());
auto bit_pattern = get_bit_pattern(reg.get());
EXPECT_EQ("00010000", bit_pattern);
reg->set_break(false);
EXPECT_EQ(false, reg->is_break_set());
bit_pattern = get_bit_pattern(reg.get());
EXPECT_EQ("00000000", bit_pattern);
}
TEST(StatusRegister, OverflowFlag) {
auto reg = make_unique<StatusRegister>(StatusRegister());
EXPECT_EQ(false, reg->is_overflow_set());
reg->set_overflow(true);
EXPECT_EQ(true, reg->is_overflow_set());
auto bit_pattern = get_bit_pattern(reg.get());
EXPECT_EQ("01000000", bit_pattern);
reg->set_overflow(false);
EXPECT_EQ(false, reg->is_overflow_set());
bit_pattern = get_bit_pattern(reg.get());
EXPECT_EQ("00000000", bit_pattern);
}
TEST(StatusRegister, NegativeFlag) {
auto reg = make_unique<StatusRegister>(StatusRegister());
EXPECT_EQ(false, reg->is_negative_set());
reg->set_negative(true);
EXPECT_EQ(true, reg->is_negative_set());
auto bit_pattern = get_bit_pattern(reg.get());
EXPECT_EQ("10000000", bit_pattern);
reg->set_negative(false);
EXPECT_EQ(false, reg->is_negative_set());
bit_pattern = get_bit_pattern(reg.get());
EXPECT_EQ("00000000", bit_pattern);
}
TEST(StatusRegister, AllFlags) {
auto reg = make_unique<StatusRegister>(StatusRegister());
EXPECT_EQ(false, reg->is_negative_set());
reg->set_carry(true);
reg->set_zero(true);
reg->set_interupt_disable(true);
reg->set_decimal(true);
reg->set_break(true);
reg->set_overflow(true);
reg->set_negative(true);
auto bit_pattern = get_bit_pattern(reg.get());
EXPECT_EQ("11011111", bit_pattern);
reg->set_carry(false);
reg->set_zero(false);
reg->set_interupt_disable(false);
reg->set_decimal(false);
reg->set_break(false);
reg->set_overflow(false);
reg->set_negative(false);
bit_pattern = get_bit_pattern(reg.get());
EXPECT_EQ("00000000", bit_pattern);
}