#include "emulator.h" #include "instruction.h" #include #include #include #include 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::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(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(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(memory[PC]); DISPATCH(); } ADD: { int32_t result = registers[inst.arg1] + registers[inst.arg2]; updateFlags(result); registers[inst.arg1] = static_cast(result); DISPATCH(); } SUB: { int32_t result = registers[inst.arg1] - registers[inst.arg2]; updateFlags(result); registers[inst.arg1] = static_cast(result); DISPATCH(); } LOAD: { registers[inst.arg1] = *reinterpret_cast(memory[combineLowAndHighBits(inst.arg2, inst.arg3)]); DISPATCH(); } STORE: { *(reinterpret_cast(memory[combineLowAndHighBits(inst.arg1, inst.arg2)])) = registers[inst.arg3]; DISPATCH(); } AND: { } }