2019-04-15 22:51:04 +00:00
|
|
|
#include "machine.h"
|
2019-04-23 20:19:00 +00:00
|
|
|
#include "terminal.h"
|
2019-04-15 22:51:04 +00:00
|
|
|
#include "../opcode/opcode-handler-directory.h"
|
|
|
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
|
|
|
namespace emu_6502 {
|
|
|
|
class MachineImpl {
|
|
|
|
private:
|
2019-04-23 20:19:00 +00:00
|
|
|
bool terminal_initialised = false;
|
2019-04-15 22:51:04 +00:00
|
|
|
uint16_t code_loaded_at;
|
|
|
|
uint16_t code_size;
|
|
|
|
|
|
|
|
unique_ptr<Cpu> cpu;
|
|
|
|
unique_ptr<Memory> memory;
|
|
|
|
unique_ptr<Stack> stack;
|
2019-04-23 20:19:00 +00:00
|
|
|
unique_ptr<Terminal> terminal;
|
2019-04-15 22:51:04 +00:00
|
|
|
unique_ptr<OpcodeHandlerDirectory> opcode_handler_dir;
|
|
|
|
public:
|
2019-04-23 20:19:00 +00:00
|
|
|
MachineImpl(bool init_terminal) {
|
2019-04-15 22:51:04 +00:00
|
|
|
cpu = make_unique<Cpu>();
|
|
|
|
memory = make_unique<Memory>();
|
|
|
|
stack = make_unique<Stack>(*memory, cpu->get_sp());
|
2019-04-23 20:19:00 +00:00
|
|
|
|
|
|
|
if (init_terminal) {
|
|
|
|
terminal = make_unique<Terminal>(*memory);
|
|
|
|
terminal_initialised = true;
|
|
|
|
}
|
|
|
|
|
2019-04-15 22:51:04 +00:00
|
|
|
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);
|
2019-04-23 20:19:00 +00:00
|
|
|
|
|
|
|
if (terminal_initialised)
|
|
|
|
terminal->refresh();
|
2019-04-15 22:51:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-04-23 20:19:00 +00:00
|
|
|
Machine::Machine(bool init_terminal) : pimpl(make_unique<MachineImpl>(init_terminal)) {
|
2019-04-15 22:51:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-04-18 00:09:44 +00:00
|
|
|
bool Machine::is_eop() const {
|
2019-04-15 22:51:04 +00:00
|
|
|
return pimpl->is_eop();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Machine::read_program_byte() {
|
|
|
|
return pimpl->read_program_byte();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Machine::execute() {
|
|
|
|
pimpl->execute(*this);
|
|
|
|
}
|
|
|
|
}
|