Initial commit
This commit is contained in:
127
emulator.cpp
Normal file
127
emulator.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
#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: {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user