Files
MyLittleCPU/emulator.cpp
2026-07-14 19:54:23 +10:00

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: {
}
}