mirror of
https://github.com/tdinucci/6502-emulator.git
synced 2024-06-16 15:29:36 +00:00
Scaled back to just LD* ops but fundamental improvements
This commit is contained in:
parent
99c0834f0d
commit
3cb4bcb0e1
33
CMakeLists.txt
Normal file
33
CMakeLists.txt
Normal 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
15
CMakeLists.txt.in
Normal 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
49
src/machine/cpu.cpp
Normal 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
38
src/machine/cpu.h
Normal 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
106
src/machine/machine.cpp
Normal 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
36
src/machine/machine.h
Normal 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
27
src/machine/memory.cpp
Normal 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
29
src/machine/memory.h
Normal 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
|
7
src/machine/program-counter.cpp
Normal file
7
src/machine/program-counter.cpp
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#include "program-counter.h"
|
||||||
|
|
||||||
|
namespace emu_6502 {
|
||||||
|
void ProgramCounter::inc() {
|
||||||
|
value += 1;
|
||||||
|
}
|
||||||
|
}
|
18
src/machine/program-counter.h
Normal file
18
src/machine/program-counter.h
Normal 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
33
src/machine/register.cpp
Normal 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
30
src/machine/register.h
Normal 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
22
src/machine/stack.cpp
Normal 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
30
src/machine/stack.h
Normal 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
|
59
src/machine/status-register.cpp
Normal file
59
src/machine/status-register.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
47
src/machine/status-register.h
Normal file
47
src/machine/status-register.h
Normal 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
37
src/machine/terminal.cpp
Normal 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
33
src/machine/terminal.h
Normal 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
30
src/main.cpp
Normal 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;
|
||||||
|
}
|
92
src/opcode/handler/load-opcode-handler-container.cpp
Normal file
92
src/opcode/handler/load-opcode-handler-container.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
58
src/opcode/handler/load-opcode-handler-container.h
Normal file
58
src/opcode/handler/load-opcode-handler-container.h
Normal 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
|
9
src/opcode/handler/opcode-handler-container.cpp
Normal file
9
src/opcode/handler/opcode-handler-container.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
24
src/opcode/handler/opcode-handler-container.h
Normal file
24
src/opcode/handler/opcode-handler-container.h
Normal 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
|
28
src/opcode/opcode-handler-directory.cpp
Normal file
28
src/opcode/opcode-handler-directory.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
31
src/opcode/opcode-handler-directory.h
Normal file
31
src/opcode/opcode-handler-directory.h
Normal 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
57
src/utils.cpp
Normal 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
22
src/utils.h
Normal 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
|
760
test/load-opcode-handler-test.cpp
Normal file
760
test/load-opcode-handler-test.cpp
Normal 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
36
test/test-utils.h
Normal 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();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user