More opcodes

This commit is contained in:
Tony Di Nucci 2019-03-27 20:04:01 +00:00
parent 2bcafa836d
commit b2baba888f
21 changed files with 445 additions and 44 deletions

View File

@ -25,9 +25,9 @@ add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src
file(GLOB_RECURSE test_src "test/**.h" "test/**.cpp" "src/**.h" "src/**.cpp")
list(FILTER test_src EXCLUDE REGEX ".*/6502-emulator/src/main.cpp$")
add_executable(6502_emulator_test ${test_src})
add_executable(6502_emulator_test ${test_src} test/stack-opcode-handler-test.cpp test/ldx-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})
add_executable(6502_emulator ${emulator_src} src/opcode/handler/stack-opcode-handler.cpp src/opcode/handler/stack-opcode-handler.h src/stack.cpp src/stack.h src/opcode/handler/lda-opcode-handler.cpp src/opcode/handler/lda-opcode-handler.h)

View File

@ -6,7 +6,7 @@ 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));
opcode_handler_dir = make_shared<OpcodeHandlerDirectory>(OpcodeHandlerDirectory(program, reg_man, memory));
instruction_executor =
make_unique<InstructionExecutor>(InstructionExecutor(program, reg_man, opcode_handler_dir));
}

View File

@ -2,13 +2,22 @@
#include <stdexcept>
Memory::Memory() {
bytes = make_shared<vector<uint8_t>>(vector<uint8_t>((1024 * 64) - 1));
bytes = make_unique<vector<uint8_t>>(vector<uint8_t>(0xFFFF));
stack = make_unique<Stack>(Stack());
}
void Memory::set_byte_at(uint16_t index, uint8_t value) {
bytes->at(index) = value;
}
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);
}
void Memory::stack_push(uint8_t data) {
stack->push(data);
}
uint8_t Memory::stack_pop() {
return stack->pop();
}

View File

@ -1,7 +1,7 @@
#ifndef INC_6502_EMULATOR_MEMORY_H
#define INC_6502_EMULATOR_MEMORY_H
#include "stack.h"
#include <memory>
#include <vector>
@ -11,10 +11,14 @@ class Memory {
public:
explicit Memory();
void set_byte_at(uint16_t index, uint8_t value);
uint8_t get_byte_at(uint16_t index);
void stack_push(uint8_t data);
uint8_t stack_pop();
private:
shared_ptr<vector<uint8_t>> bytes;
unique_ptr<vector<uint8_t>> bytes;
unique_ptr<Stack> stack;
};

View File

@ -1,7 +1,6 @@
#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 {
@ -14,8 +13,9 @@ public:
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) {
explicit FlagOpcodeHandler(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man,
shared_ptr<Memory> memory) :
OpcodeHandler(program, reg_man, memory) {
handled_opcodes->push_back(CLC);
handled_opcodes->push_back(SEC);
handled_opcodes->push_back(CLI);

View File

@ -0,0 +1,5 @@
//
// Created by tony on 27/03/19.
//
#include "lda-opcode-handler.h"

View File

@ -0,0 +1,14 @@
//
// Created by tony on 27/03/19.
//
#ifndef INC_6502_EMULATOR_LDA_OPCODE_HANDLER_H
#define INC_6502_EMULATOR_LDA_OPCODE_HANDLER_H
class LdaOpcodeHandler {
};
#endif //INC_6502_EMULATOR_LDA_OPCODE_HANDLER_H

View File

@ -3,25 +3,55 @@
const uint8_t LdxOpcodeHandler::IMMEDIATE;
const uint8_t LdxOpcodeHandler::ZERO_PAGE;
const uint8_t LdxOpcodeHandler::ZERO_PAGE_X;
const uint8_t LdxOpcodeHandler::ZERO_PAGE_Y;
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();
auto x_reg = reg_man->get_x_index();
switch (opcode) {
case IMMEDIATE:
accumulator->set_value(byte2);
x_reg->set_value(byte2);
move_program_counter(2);
break;
case ZERO_PAGE:
x_reg->set_value(memory->get_byte_at(byte2));
move_program_counter(2);
break;
case ZERO_PAGE_Y: {
uint16_t address = byte2 + reg_man->get_y_index()->get_value();
x_reg->set_value(memory->get_byte_at(address));
move_program_counter(2);
break;
}
case ABSOLUTE: {
auto low = byte2;
auto high = program->get_byte_at(instructionIndex + 2);
uint16_t address = (high << 8) + low;
x_reg->set_value(memory->get_byte_at(address));
move_program_counter(3);
break;
}
case ABSOLUTE_Y: {
auto low = byte2;
auto high = program->get_byte_at(instructionIndex + 2);
uint16_t address = (high << 8) + low + reg_man->get_y_index()->get_value();
x_reg->set_value(memory->get_byte_at(address));
move_program_counter(3);
break;
}
default:
stringstream stream;
stream << "Unexpected opcode 0x" << hex << opcode;

View File

@ -6,25 +6,20 @@
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;
static const uint8_t IMMEDIATE = 0xA2;
static const uint8_t ZERO_PAGE = 0xA6;
static const uint8_t ZERO_PAGE_Y = 0xB6;
static const uint8_t ABSOLUTE = 0xAE;
static const uint8_t ABSOLUTE_Y = 0xBE;
explicit LdxOpcodeHandler(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man) :
OpcodeHandler(program, reg_man) {
explicit LdxOpcodeHandler(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man,
shared_ptr<Memory> memory) :
OpcodeHandler(program, reg_man, memory) {
handled_opcodes->push_back(IMMEDIATE);
handled_opcodes->push_back(ZERO_PAGE);
handled_opcodes->push_back(ZERO_PAGE_X);
handled_opcodes->push_back(ZERO_PAGE_Y);
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;

View File

@ -15,8 +15,9 @@ public:
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) {
explicit RegisterOpcodeHandler(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man,
shared_ptr<Memory> memory) :
OpcodeHandler(program, reg_man, memory) {
handled_opcodes->push_back(TAX);
handled_opcodes->push_back(TXA);
handled_opcodes->push_back(DEX);

View File

@ -0,0 +1,43 @@
#include "stack-opcode-handler.h"
#include <sstream>
#include <climits>
const uint8_t StackOpcodeHandler::PHA;
const uint8_t StackOpcodeHandler::PLA;
const uint8_t StackOpcodeHandler::PHP;
const uint8_t StackOpcodeHandler::PLP;
void StackOpcodeHandler::execute() {
auto instructionIndex = reg_man->get_program_counter()->get_value();
auto opcode = program->get_byte_at(instructionIndex);
switch (opcode) {
case PHA:
memory->stack_push(reg_man->get_accumulator()->get_value());
break;
case PLA:
reg_man->get_accumulator()->set_value(memory->stack_pop());
break;
case PHP: {
ulong long_data = reg_man->get_status_register()->get_value().to_ulong();
auto data = static_cast<uint8_t>(long_data);
memory->stack_push(data);
break;
}
case PLP: {
auto data = memory->stack_pop();
reg_man->get_status_register()->set_value(bitset<8>(data));
break;
}
default:
stringstream stream;
stream << "Unexpected opcode 0x" << hex << opcode;
throw runtime_error(stream.str());
}
move_program_counter(1);
}

View File

@ -0,0 +1,27 @@
#ifndef INC_6502_EMULATOR_STACK_OPCODE_HANDLER_H
#define INC_6502_EMULATOR_STACK_OPCODE_HANDLER_H
#include "../../program.h"
#include "../opcode-handler.h"
class StackOpcodeHandler : public OpcodeHandler {
public:
static const uint8_t PHA = 0x48;
static const uint8_t PLA = 0x68;
static const uint8_t PHP = 0x08;
static const uint8_t PLP = 0x28;
explicit StackOpcodeHandler(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man,
shared_ptr<Memory> memory) :
OpcodeHandler(program, reg_man, memory) {
handled_opcodes->push_back(PHA);
handled_opcodes->push_back(PLA);
handled_opcodes->push_back(PHP);
handled_opcodes->push_back(PLP);
}
virtual void execute() override;
};
#endif //INC_6502_EMULATOR_STACK_OPCODE_HANDLER_H

View File

@ -2,16 +2,18 @@
#include "handler/flag-opcode-handler.h"
#include "handler/register-opcode-handler.h"
#include "handler/ldx-opcode-handler.h"
#include "handler/stack-opcode-handler.h"
#include <sstream>
#include <stdexcept>
OpcodeHandlerDirectory::OpcodeHandlerDirectory(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man) {
OpcodeHandlerDirectory::OpcodeHandlerDirectory(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man, shared_ptr<Memory> memory) {
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));
register_handler(make_shared<FlagOpcodeHandler>(FlagOpcodeHandler(program, reg_man, memory)));
register_handler(make_shared<RegisterOpcodeHandler>(RegisterOpcodeHandler(program, reg_man, memory)));
register_handler(make_shared<LdxOpcodeHandler>(LdxOpcodeHandler(program, reg_man, memory)));
register_handler(make_shared<StackOpcodeHandler>(StackOpcodeHandler(program, reg_man, memory)));
}
void OpcodeHandlerDirectory::register_handler(shared_ptr<OpcodeHandler> handler) {

View File

@ -2,6 +2,7 @@
#define INC_6502_EMULATOR_OPCODE_HANDLER_DIRECTORY_H
#include "../program.h"
#include "../memory.h"
#include "opcode-handler.h"
#include <unordered_map>
#include <vector>
@ -11,7 +12,7 @@ using namespace std;
class OpcodeHandlerDirectory {
public:
explicit OpcodeHandlerDirectory(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man);
explicit OpcodeHandlerDirectory(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man, shared_ptr<Memory> memory);
shared_ptr<OpcodeHandler> get_handler(uint8_t opcode);
private:

View File

@ -1,9 +1,10 @@
#include "../program.h"
#include "opcode-handler.h"
OpcodeHandler::OpcodeHandler(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man) {
OpcodeHandler::OpcodeHandler(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man,
shared_ptr<Memory> memory) {
this->program = program;
this->reg_man = reg_man;
this->memory = memory;
}
void OpcodeHandler::move_program_counter(uint8_t forward_by) {

View File

@ -2,6 +2,7 @@
#define INC_6502_EMULATOR_OPCODE_HANDLER_H
#include "../program.h"
#include "../memory.h"
#include "../register/register-manager.h"
#include <vector>
#include <memory>
@ -10,7 +11,7 @@ using namespace std;
class OpcodeHandler {
public:
OpcodeHandler(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man);
OpcodeHandler(shared_ptr<Program> program, shared_ptr<RegisterManager> reg_man, shared_ptr<Memory> memory);
shared_ptr<vector<uint8_t>> get_handled_opcodes() { return handled_opcodes; }
@ -19,6 +20,7 @@ public:
protected:
shared_ptr<vector<uint8_t>> handled_opcodes = make_shared<vector<uint8_t>>();
shared_ptr<Program> program;
shared_ptr<Memory> memory;
shared_ptr<RegisterManager> reg_man;
void move_program_counter(uint8_t forward_by);

26
src/stack.cpp Normal file
View File

@ -0,0 +1,26 @@
#include "stack.h"
#include <stdexcept>
const uint8_t Stack::MAX_SIZE;
Stack::Stack() {
data = make_unique<stack<uint8_t>>(stack<uint8_t>());
}
void Stack::push(uint8_t byte) {
if (data->size() >= MAX_SIZE)
throw runtime_error("Stack overflow");
data->push(byte);
}
uint8_t Stack::pop() {
if (data->empty())
throw runtime_error("Stack is empty");
auto result = data->top();
data->pop();
return result;
}

22
src/stack.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef INC_6502_EMULATOR_STACK_H
#define INC_6502_EMULATOR_STACK_H
#include <memory>
#include <stack>
using namespace std;
class Stack {
public:
explicit Stack();
void push(uint8_t byte);
uint8_t pop();
private:
static const uint8_t MAX_SIZE = 0xFF;
unique_ptr<stack<uint8_t>> data;
};
#endif //INC_6502_EMULATOR_STACK_H

View File

@ -22,7 +22,8 @@ TEST(InstructionExecutor, Execute) {
CLC
});
auto program = make_shared<Program>(Program(code));
auto opcode_handler_dir = make_shared<OpcodeHandlerDirectory>(OpcodeHandlerDirectory(program, reg_man));
auto memory = make_shared<Memory>(Memory());
auto opcode_handler_dir = make_shared<OpcodeHandlerDirectory>(OpcodeHandlerDirectory(program, reg_man, memory));
auto executor = make_unique<InstructionExecutor>(InstructionExecutor(program, reg_man, opcode_handler_dir));
executor->execute();

View File

@ -0,0 +1,106 @@
#include "gtest/gtest.h"
#include "../src/machine.h"
#include "../src/program.h"
#include "../src/opcode/handler/stack-opcode-handler.h"
#include <memory>
#include <vector>
using namespace std;
const uint8_t IMMEDIATE = 0xA2;
const uint8_t ZERO_PAGE = 0xA6;
const uint8_t ZERO_PAGE_Y = 0xB6;
const uint8_t ABSOLUTE = 0xAE;
const uint8_t ABSOLUTE_Y = 0xBE;
unique_ptr<Machine> ldxt_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(LdxOpcodeHandler, Immediate) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{IMMEDIATE, 36});
auto machine = ldxt_get_machine(code);
auto reg_man = machine->get_reg_man();
auto x_reg = reg_man->get_x_index();
ASSERT_EQ(0, x_reg->get_value());
machine->execute();
ASSERT_EQ(36, x_reg->get_value());
}
TEST(LdxOpcodeHandler, ZeroPage) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{ZERO_PAGE, 123});
auto machine = ldxt_get_machine(code);
auto reg_man = machine->get_reg_man();
auto x_reg = reg_man->get_x_index();
ASSERT_EQ(0, x_reg->get_value());
machine->get_memory()->set_byte_at(123, 78);
machine->execute();
ASSERT_EQ(78, x_reg->get_value());
}
TEST(LdxOpcodeHandler, ZeroPageY) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{ZERO_PAGE_Y, 250});
auto machine = ldxt_get_machine(code);
auto reg_man = machine->get_reg_man();
auto x_reg = reg_man->get_x_index();
auto y_reg = reg_man->get_y_index();
ASSERT_EQ(0, x_reg->get_value());
ASSERT_EQ(0, y_reg->get_value());
machine->get_memory()->set_byte_at(250, 255);
machine->execute();
ASSERT_EQ(255, x_reg->get_value());
reg_man->get_y_index()->set_value(100);
reg_man->get_program_counter()->set_value(0);
machine->execute();
ASSERT_EQ(0, x_reg->get_value());
machine->get_memory()->set_byte_at(350, 233);
reg_man->get_program_counter()->set_value(0);
machine->execute();
ASSERT_EQ(233, x_reg->get_value());
}
TEST(LdxOpcodeHandler, Absolute) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{ABSOLUTE, 0xe5, 0xff});
auto machine = ldxt_get_machine(code);
auto reg_man = machine->get_reg_man();
auto x_reg = reg_man->get_x_index();
ASSERT_EQ(0, x_reg->get_value());
machine->get_memory()->set_byte_at(0xffe5, 0xde);
machine->execute();
ASSERT_EQ(0xde, x_reg->get_value());
}
TEST(LdxOpcodeHandler, AbsoluteY) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{ABSOLUTE_Y, 0x33, 0xc2});
auto machine = ldxt_get_machine(code);
auto reg_man = machine->get_reg_man();
auto x_reg = reg_man->get_x_index();
auto y_reg = reg_man->get_y_index();
ASSERT_EQ(0, x_reg->get_value());
ASSERT_EQ(0, y_reg->get_value());
machine->get_memory()->set_byte_at(0xc233, 0xc8);
machine->execute();
ASSERT_EQ(0xc8, x_reg->get_value());
y_reg->set_value(0x55);
machine->get_memory()->set_byte_at(0xc233 + 0x55, 0xab);
reg_man->get_program_counter()->set_value(0);
machine->execute();
ASSERT_EQ(0xab, x_reg->get_value());
}

View File

@ -0,0 +1,112 @@
#include "gtest/gtest.h"
#include "../src/machine.h"
#include "../src/program.h"
#include "../src/opcode/handler/stack-opcode-handler.h"
#include <memory>
#include <vector>
using namespace std;
const uint8_t PHA = 0x48;
const uint8_t PLA = 0x68;
const uint8_t PHP = 0x08;
const uint8_t PLP = 0x28;
unique_ptr<Machine> st_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(StackOpcodeHandler, PushAccumulator) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{PHA});
auto machine = st_get_machine(code);
auto reg_man = machine->get_reg_man();
reg_man->get_accumulator()->set_value(0x8b);
ASSERT_EQ(0x8b, reg_man->get_accumulator()->get_value());
// stack is empty
ASSERT_THROW(machine->get_memory()->stack_pop(), runtime_error);
machine->execute();
ASSERT_EQ(0x8b, reg_man->get_accumulator()->get_value());
auto stack_value = machine->get_memory()->stack_pop();
ASSERT_EQ(0x8b, stack_value);
// stack is empty again
ASSERT_THROW(machine->get_memory()->stack_pop(), runtime_error);
}
TEST(StackOpcodeHandler, PullAccumulator) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{PLA});
auto machine = st_get_machine(code);
auto reg_man = machine->get_reg_man();
machine->get_memory()->stack_push(1);
machine->get_memory()->stack_push(2);
machine->get_memory()->stack_push(3);
machine->execute();
ASSERT_EQ(3, reg_man->get_accumulator()->get_value());
reg_man->get_program_counter()->set_value(0);
machine->execute();
ASSERT_EQ(2, reg_man->get_accumulator()->get_value());
reg_man->get_program_counter()->set_value(0);
machine->execute();
ASSERT_EQ(1, reg_man->get_accumulator()->get_value());
// stack is empty
ASSERT_THROW(machine->get_memory()->stack_pop(), runtime_error);
}
TEST(StackOpcodeHandler, PushStatus) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{PHP});
auto machine = st_get_machine(code);
auto reg_man = machine->get_reg_man();
reg_man->get_status_register()->set_value(255);
ASSERT_EQ(255, reg_man->get_status_register()->get_value().to_ulong());
// stack is empty
ASSERT_THROW(machine->get_memory()->stack_pop(), runtime_error);
machine->execute();
ASSERT_EQ(255, reg_man->get_status_register()->get_value().to_ulong());
auto stack_value = machine->get_memory()->stack_pop();
ASSERT_EQ(255, stack_value);
// stack is empty again
ASSERT_THROW(machine->get_memory()->stack_pop(), runtime_error);
}
TEST(StackOpcodeHandler, PullStatus) {
auto code = make_shared<vector<uint8_t>>(vector<uint8_t>{PLP});
auto machine = st_get_machine(code);
auto reg_man = machine->get_reg_man();
machine->get_memory()->stack_push(8);
machine->get_memory()->stack_push(16);
machine->get_memory()->stack_push(32);
machine->execute();
ASSERT_EQ(32, reg_man->get_status_register()->get_value().to_ulong());
reg_man->get_program_counter()->set_value(0);
machine->execute();
ASSERT_EQ(16, reg_man->get_status_register()->get_value().to_ulong());
reg_man->get_program_counter()->set_value(0);
machine->execute();
ASSERT_EQ(8, reg_man->get_status_register()->get_value().to_ulong());
// stack is empty
ASSERT_THROW(machine->get_memory()->stack_pop(), runtime_error);
}