128 lines
3.4 KiB
C++
128 lines
3.4 KiB
C++
#include "emulator.h"
|
|
#include "instruction.h"
|
|
#include <cstdint>
|
|
#include <stdexcept>
|
|
#include <limits>
|
|
#include <cstring>
|
|
|
|
void Memory::addInstruction(const BytecodeInstruction& inst) {
|
|
// make sure we won't write an instruction out of bounds
|
|
constexpr size_t maxoffset = (GENERAL_PURPOSE_START - PROGRAM_ROM_START) / sizeof(BytecodeInstruction);
|
|
if (instructions >= maxoffset) {
|
|
throw std::runtime_error("out of space for program");
|
|
}
|
|
|
|
// get offset for next instruction
|
|
size_t offset = PROGRAM_ROM_START + (sizeof(Instruction) * instructions++);
|
|
uint8_t* memoffset = &this->memory[offset];
|
|
std::memcpy(memoffset, &inst, sizeof(Instruction));
|
|
}
|
|
|
|
void Memory::addCall(const uint16_t address) {
|
|
// make sure we're not out of call stack space
|
|
constexpr size_t maxoffset = (GENERAL_PURPOSE_START - PROGRAM_ROM_START) / sizeof(Instruction);
|
|
if (instructions >= maxoffset) {
|
|
throw std::runtime_error("out of space for call stack");
|
|
}
|
|
|
|
size_t offset = sizeof(uint16_t) * calls++;
|
|
uint8_t* memoffset = &this->memory[offset];
|
|
std::memcpy(memoffset, &address, sizeof(uint16_t));
|
|
}
|
|
|
|
uint8_t* Memory::operator[](const size_t& offset) {
|
|
if (offset >= MEMORY_SIZE) {
|
|
throw std::runtime_error("offset out of bounds");
|
|
}
|
|
|
|
return &memory[offset];
|
|
}
|
|
|
|
uint32_t combineLowAndHighBits(uint16_t low, uint16_t high) {
|
|
return (((uint32_t)high << 16) | low);
|
|
}
|
|
|
|
void Emulator::updateFlags(int32_t opResult) {
|
|
if (opResult > std::numeric_limits<uint16_t>::max()) {
|
|
FLAGS = FLAGS_CARRY;
|
|
} else if (opResult == 0) {
|
|
FLAGS = FLAGS_ZERO;
|
|
} else if (opResult < 0) {
|
|
FLAGS = FLAGS_NEGATIVE;
|
|
}
|
|
}
|
|
|
|
Instruction Emulator::fetchNextInst() {
|
|
Instruction* insts = reinterpret_cast<Instruction*>(memory.memory); \
|
|
Instruction inst = (insts + PROGRAM_ROM_START)[PC++];
|
|
return inst;
|
|
}
|
|
|
|
void Emulator::start() {
|
|
halted = false;
|
|
|
|
static const void* dispatchTable[] = {
|
|
&&MOV,
|
|
&&MVI,
|
|
&&ADD,
|
|
&&SUB,
|
|
&&LOAD,
|
|
&&STORE,
|
|
&&AND,
|
|
&&OR,
|
|
&&XOR,
|
|
&&SHF,
|
|
&&JMP,
|
|
&&JIZ,
|
|
&&CALL,
|
|
&&RET,
|
|
&&SYS,
|
|
&&HLT
|
|
};
|
|
|
|
Instruction inst;
|
|
|
|
#define DISPATCH() if (halted) return; \
|
|
inst = fetchNextInst(); \
|
|
goto *dispatchTable[static_cast<size_t>(inst.opcode)];
|
|
|
|
DISPATCH();
|
|
|
|
MOV: {
|
|
registers[inst.arg1] = registers[inst.arg2];
|
|
DISPATCH();
|
|
}
|
|
MVI: {
|
|
PC++; // the instruction reads the space where the next instruction would be as a number
|
|
registers[inst.arg1] = *reinterpret_cast<uint16_t*>(memory[PC]);
|
|
DISPATCH();
|
|
}
|
|
ADD: {
|
|
int32_t result = registers[inst.arg1] + registers[inst.arg2];
|
|
updateFlags(result);
|
|
registers[inst.arg1] = static_cast<uint16_t>(result);
|
|
|
|
DISPATCH();
|
|
}
|
|
SUB: {
|
|
int32_t result = registers[inst.arg1] - registers[inst.arg2];
|
|
updateFlags(result);
|
|
registers[inst.arg1] = static_cast<uint16_t>(result);
|
|
|
|
DISPATCH();
|
|
}
|
|
LOAD: {
|
|
registers[inst.arg1] = *reinterpret_cast<uint16_t*>(memory[combineLowAndHighBits(inst.arg2, inst.arg3)]);
|
|
DISPATCH();
|
|
}
|
|
STORE: {
|
|
*(reinterpret_cast<uint16_t*>(memory[combineLowAndHighBits(inst.arg1, inst.arg2)])) = registers[inst.arg3];
|
|
DISPATCH();
|
|
}
|
|
AND: {
|
|
|
|
}
|
|
|
|
|
|
}
|