#include "emu.h" static inline Instruction fetchInst(Emulator* emu) { return decodeInstruction(emu->mem, &emu->pc); } static inline void updateFlagsRegister(Emulator* emu, int32_t value) { FlagsRegister flags = (FlagsRegister)emu->regs[REG_F]; flags.fields.zero = value == 0; flags.fields.negative = value < 0; flags.fields.overflow = value > UINT16_MAX; emu->regs[REG_F] = flags.raw; } static inline void updateDevices(Emulator* emu) { for (size_t i = 0; i < MAX_DEVICES; i++) { Device* device = emu->devices[i]; if (!device) continue; if (device->update) { device->update(device); } } } static inline void pushProgramCounter(Emulator* emu) { ((uint32_t*)emu->mem->data)[emu->sp--] = emu->pc; } void handleInterrupt(Emulator* emu) { FlagsRegister flags = (FlagsRegister)emu->regs[REG_F]; if (!flags.fields.interrupts) return; InterruptHandlerTable interruptTable = *((InterruptHandlerTable*)&emu->mem->data[emu->iht]); pushProgramCounter(emu); emu->pc = interruptTable.interruptHandlers[emu->irq-1]; emu->irq = 0; } uint32_t getRegister(Emulator* emu, uint8_t reg) { if (reg == REG_IHT_OFFSET) { return emu->iht; } return emu->regs[reg]; } void setRegister(Emulator* emu, uint8_t reg, uint32_t value) { if (reg == REG_IHT_OFFSET) { emu->iht = value; return; } emu->regs[reg] = value; } void startEmulator(Emulator* emu) { static void* dispatchTable[] = { &&MOV, &&MVI, &&ADD, &&SUB, &&LOAD, &&STORE, &&AND, &&OR, &&XOR, &&SHF, &&JMP, &&JIZ, &&CALL, &&RET, &&PORT, &&HLT }; emu->halted = false; Instruction inst; #define DISPATCH() if (emu->irq != 0) handleInterrupt(emu); \ updateDevices(emu); \ if (emu->halted) { \ return; \ } \ inst = fetchInst(emu); \ printf("%s\n", instructionToCStr(inst)); \ goto *dispatchTable[inst.opcode] DISPATCH(); MOV: { setRegister(emu, inst.operands[0], getRegister(emu, inst.operands[1])); DISPATCH(); } MVI: { setRegister(emu, inst.operands[0], inst.immediate); DISPATCH(); } ADD: { int32_t result = getRegister(emu, inst.operands[0]) + getRegister(emu, inst.operands[1]); updateFlagsRegister(emu, result); setRegister(emu, inst.operands[0], result); DISPATCH(); } SUB: { int32_t result = getRegister(emu, inst.operands[0]) - getRegister(emu, inst.operands[1]); updateFlagsRegister(emu, result); setRegister(emu, inst.operands[0], result); DISPATCH(); } LOAD: { uint32_t addr = getAddress( getRegister(emu, inst.operands[1]), getRegister(emu, inst.operands[2]) ); setRegister(emu, inst.operands[0], memoryLoadWord(emu->mem, addr)); DISPATCH(); } STORE: { uint32_t addr = getAddress( getRegister(emu, inst.operands[1]), getRegister(emu, inst.operands[2]) ); memorySaveWord(emu->mem, addr, getRegister(emu, inst.operands[2])); DISPATCH(); } AND: { int32_t result = getRegister(emu, inst.operands[0]) & getRegister(emu, inst.operands[1]); updateFlagsRegister(emu, result); setRegister(emu, inst.operands[0], result); DISPATCH(); } OR: { int32_t result = getRegister(emu, inst.operands[0]) | getRegister(emu, inst.operands[1]); updateFlagsRegister(emu, result); setRegister(emu, inst.operands[0], result); DISPATCH(); } XOR: { int32_t result = getRegister(emu, inst.operands[0]) ^ getRegister(emu, inst.operands[1]); updateFlagsRegister(emu, result); setRegister(emu, inst.operands[0], result); DISPATCH(); } SHF: { int32_t result; if (inst.operands[0] == 0) result = getRegister(emu, inst.operands[0]) << getRegister(emu, inst.operands[1]); else result = getRegister(emu, inst.operands[0]) >> getRegister(emu, inst.operands[1]); updateFlagsRegister(emu, result); setRegister(emu, inst.operands[0], result); DISPATCH(); } JMP: { emu->pc = inst.immediate; DISPATCH(); } JIZ: { if (((FlagsRegister)emu->regs[REG_F]).fields.zero) emu->pc = inst.immediate; DISPATCH(); } CALL: { // save the pc at the current sp and decrement the sp (stack grows downward) pushProgramCounter(emu); // jump to given addr emu->pc = inst.immediate; DISPATCH(); } RET: { // return to value at sp and increment it emu->pc = ((uint32_t*)emu->mem->data)[++emu->sp]; DISPATCH(); } PORT: { uint16_t port = getRegister(emu, inst.operands[0]); if (port >= MAX_DEVICES) { DISPATCH(); } uint8_t direction = inst.operands[2]; Device* device = emu->devices[port]; if (!device) { DISPATCH(); } if (direction == 0) { uint16_t value = getRegister(emu, inst.operands[1]); if (device->inPos == DEVICE_BUFFER_LENGTH) { DISPATCH(); } device->in[device->inPos] = value; device->inPos++; } else { if (device->outPos == 0) { setRegister(emu, inst.operands[1], 0); DISPATCH(); } setRegister(emu, inst.operands[1], device->out[device->outPos-1]); if (device->outPos > 0) device->outPos--; } DISPATCH(); } HLT: { emu->halted = true; return; } } Emulator* initEmulator(uint32_t memorySize) { Emulator* newEmu = malloc(sizeof(Emulator)); if (!newEmu) return NULL; // zero out registers memset(newEmu->regs, 0, sizeof(newEmu->regs)); // zero out special regs newEmu->pc = MEM_PROGRAM_START; newEmu->sp = 0; newEmu->iht = 0; // init memory newEmu->mem = initMemory(memorySize); if (!newEmu->mem) { free(newEmu); return NULL; } return newEmu; } void freeEmulator(Emulator* emu) { freeMemory(emu->mem); free(emu); } void installDevice(Emulator* emu, Device* device, uint8_t deviceNumber) { assert(deviceNumber < MAX_DEVICES); emu->devices[deviceNumber] = device; }