diff --git a/meson.build b/meson.build index d62363e..e37f8a7 100644 --- a/meson.build +++ b/meson.build @@ -7,6 +7,6 @@ sources = [ 'src/instruction.c' ] -args = ['-Wall', '-Wextra', '-Wpedantic', '-O3', '-ggdb', '-fsanitize=address'] +args = ['-Wall', '-Wextra', '-O3', '-ggdb', '-fsanitize=address'] executable('emu', sources, c_args: args) \ No newline at end of file diff --git a/src/emu.c b/src/emu.c index e507b3c..64ca695 100644 --- a/src/emu.c +++ b/src/emu.c @@ -1,5 +1,146 @@ #include "emu.h" +inline Instruction fetchInst(Emulator* emu) { + return decodeInstruction(emu->mem, &emu->pc); +} + +inline void updateFlagsRegister(Emulator* emu, int32_t value) { + if (value == 0) { + emu->regs[REG_F] = FLAG_ZERO; + } else if (value < 0) { + emu->regs[REG_F] = FLAG_NEGATIVE; + } else if (value > UINT16_MAX) { + emu->regs[REG_F] = FLAG_OVERFLOW; + } +} + +void startEmulator(Emulator* emu) { + static void* dispatchTable[] = { + &&MOV, + &&MVI, + &&ADD, + &&SUB, + &&LOAD, + &&STORE, + &&AND, + &&OR, + &&XOR, + &&SHF, + &&JMP, + &&JIZ, + &&CALL, + &&RET, + &&SYS, + &&HLT + }; + + emu->halted = false; + Instruction inst; + + #define DISPATCH() inst = fetchInst(emu); \ + goto *dispatchTable[inst.opcode]; + + DISPATCH(); + + MOV: { + emu->regs[inst.operands[0]] = emu->regs[inst.operands[1]]; + DISPATCH(); + } + MVI: { + emu->regs[inst.operands[0]] = inst.immediate; + DISPATCH(); + } + ADD: { + int32_t result = emu->regs[inst.operands[0]] + emu->regs[inst.operands[1]]; + updateFlagsRegister(emu, result); + emu->regs[inst.operands[0]] = result; + DISPATCH(); + } + SUB: { + int32_t result = emu->regs[inst.operands[0]] - emu->regs[inst.operands[1]]; + updateFlagsRegister(emu, result); + emu->regs[inst.operands[0]] = result; + DISPATCH(); + } + LOAD: { + uint32_t addr = getAddress( + emu->regs[inst.operands[1]], + emu->regs[inst.operands[2]] + ); + + emu->regs[inst.operands[0]] = memoryLoadWord(emu->mem, addr); + DISPATCH(); + } + STORE: { + uint32_t addr = getAddress( + emu->regs[inst.operands[0]], + emu->regs[inst.operands[1]] + ); + memorySaveWord(emu->mem, addr, inst.operands[2]); + DISPATCH(); + } + AND: { + int32_t result = emu->regs[inst.operands[0]] & emu->regs[inst.operands[1]]; + updateFlagsRegister(emu, result); + emu->regs[inst.operands[0]] = result; + DISPATCH(); + } + OR: { + int32_t result = emu->regs[inst.operands[0]] | emu->regs[inst.operands[1]]; + updateFlagsRegister(emu, result); + emu->regs[inst.operands[0]] = result; + DISPATCH(); + } + XOR: { + int32_t result = emu->regs[inst.operands[0]] ^ emu->regs[inst.operands[1]]; + updateFlagsRegister(emu, result); + emu->regs[inst.operands[0]] = result; + DISPATCH(); + } + SHF: { + int32_t result; + if (inst.operands[2] == 0) + result = emu->regs[inst.operands[0]] << emu->regs[inst.operands[1]]; + else + result = emu->regs[inst.operands[0]] >> emu->regs[inst.operands[1]]; + + updateFlagsRegister(emu, result); + emu->regs[inst.operands[0]] = result; + DISPATCH(); + } + JMP: { + emu->pc = getAddress(inst.operands[0], inst.operands[1]); + DISPATCH(); + } + JIZ: { + if (emu->regs[REG_F] & FLAG_ZERO) + emu->pc = getAddress(inst.operands[0], inst.operands[1]); + DISPATCH(); + } + CALL: { + // save the pc at the current sp and decrement the sp (stack grows downward) + ((uint32_t*)emu->mem->data)[emu->sp--] = emu->pc; + + // jump to given addr + emu->pc = getAddress(inst.operands[0], inst.operands[1]); + + DISPATCH(); + } + RET: { + // return to value at sp and increment it + emu->pc = ((uint32_t*)emu->mem->data)[++emu->sp]; + DISPATCH(); + } + SYS: { + // TODO + DISPATCH(); + } + HLT: { + emu->halted = true; + return; + } +} + Emulator* initEmulator(uint32_t memorySize) { Emulator* newEmu = malloc(sizeof(Emulator)); if (!newEmu) @@ -9,7 +150,7 @@ Emulator* initEmulator(uint32_t memorySize) { memset(newEmu->regs, 0, sizeof(newEmu->regs)); // zero out special regs - newEmu->pc = 0; + newEmu->pc = MEM_PROGRAM_START; newEmu->sp = 0; newEmu->ihp = 0; diff --git a/src/emu.h b/src/emu.h index 3e1d04d..294a4ce 100644 --- a/src/emu.h +++ b/src/emu.h @@ -3,8 +3,13 @@ #include #include #include +#include #include "memory.h" +#define FLAG_ZERO (1 << 0) +#define FLAG_NEGATIVE (1 << 1) +#define FLAG_OVERFLOW (1 << 2) + // -- TYPES -- // typedef struct { uint16_t regs[REG_MAX]; @@ -13,8 +18,11 @@ typedef struct { uint32_t pc; uint32_t ihp; uint32_t sp; + + bool halted; } Emulator; // -- FUNCTIONS -- // Emulator* initEmulator(uint32_t memorySize); -void freeEmulator(Emulator* emu); \ No newline at end of file +void freeEmulator(Emulator* emu); +void startEmulator(Emulator* emu); \ No newline at end of file diff --git a/src/instruction.c b/src/instruction.c index 8b4330f..c82e76d 100644 --- a/src/instruction.c +++ b/src/instruction.c @@ -57,7 +57,7 @@ char* registerToCStr(Register reg) { char* instructionToCStr(Instruction inst) { - size_t bufferSize = 64; + size_t bufferSize = 16; char* buffer = malloc(bufferSize); if (!buffer) return NULL; diff --git a/src/instruction.h b/src/instruction.h index d36ee5b..bde9f4a 100644 --- a/src/instruction.h +++ b/src/instruction.h @@ -2,6 +2,7 @@ #include #include +#include #include // -- TYPES -- // @@ -28,6 +29,7 @@ typedef struct { InstructionOpcode opcode; uint8_t operands[3]; uint16_t immediate; // unused for almost all instructions + bool hasImm16; } Instruction; typedef enum { diff --git a/src/main.c b/src/main.c index 91a59b2..e8b5c49 100644 --- a/src/main.c +++ b/src/main.c @@ -8,8 +8,16 @@ int main() { return 1; } - Instruction inst = decodeInstruction(emu->mem, &emu->pc); - printf("%s\n", instructionToCStr(inst)); + Instruction program[] = { + {INST_MVI, {REG_A, 0, 0}, 5, true}, + {INST_MVI, {REG_B, 0, 0}, 3, true}, + {INST_ADD, {REG_A, REG_B, 0}, 0, false} + }; + memoryLoadProgram(emu->mem, program, sizeof(program)/sizeof(program[0])); + + printf("%s\n", instructionToCStr(decodeInstruction(emu->mem, &emu->pc))); + printf("%s\n", instructionToCStr(decodeInstruction(emu->mem, &emu->pc))); + printf("%s\n", instructionToCStr(decodeInstruction(emu->mem, &emu->pc))); freeEmulator(emu); diff --git a/src/memory.c b/src/memory.c index 57dbfca..4092e4c 100644 --- a/src/memory.c +++ b/src/memory.c @@ -23,8 +23,44 @@ inline void memorySaveWord(Memory* m, uint32_t address, uint16_t value) { ((uint16_t*)m->data)[address] = value; } +void putInst(Memory* m, SerializedInst inst, uint32_t offset) { + memcpy((SerializedInst*)(&m->data[MEM_PROGRAM_START]) + offset, &inst, sizeof(SerializedInst)); +} + +uint32_t memorySerializeInst(Memory* m, Instruction inst, uint32_t offset) { + SerializedInst packed = 0; + + packed |= (SerializedInst)(inst.opcode & 0b0000000000001111) ; + packed |= (SerializedInst)(inst.operands[0] & 0b0000000000001111) << 4; + packed |= (SerializedInst)(inst.operands[1] & 0b0000000000001111) << 8; + packed |= (SerializedInst)(inst.operands[2] & 0b0000000000001111) << 12; + + memcpy(m->data + MEM_PROGRAM_START + (offset * sizeof(SerializedInst)), &packed, sizeof(SerializedInst)); + + + offset++; + + // write the imm16 if the instruction has one + if (inst.hasImm16) { + memcpy(m->data + MEM_PROGRAM_START + (offset * sizeof(SerializedInst)), &inst.immediate, sizeof(SerializedInst)); + + offset++; + } + + return offset; +} + +void memoryLoadProgram(Memory* m, Instruction program[], size_t numInstructions) { + size_t offset = 0; + for (size_t i = 0; i < numInstructions; i++) { + offset = memorySerializeInst(m, program[i], offset); + } +} + Instruction decodeInstruction(Memory* m, uint32_t* pc) { - SerializedInst serialized = m->data[*pc]; + SerializedInst serialized; + memcpy(&serialized, &m->data[*pc], sizeof(SerializedInst)); + Instruction outputInst = {}; uint8_t opcode = serialized & 0b0000000000001111; // get four lowest bits for the opcode @@ -38,14 +74,17 @@ Instruction decodeInstruction(Memory* m, uint32_t* pc) { case OP_REG: { // get the data at the current offset, and grab the 4 bits that are there with a bitwise // "and" operator - outputInst.operands[offset] = (serialized >> (4 * (offset + 1))) & 0b0000000000001111; + outputInst.operands[i] = (serialized >> (4 * (offset + 1))) & 0b0000000000001111; offset++; break; } case OP_IMM16: { // the immediate 16 takes up the space where the next instruction would be - uint16_t nextDataOver = m->data[++*pc]; + (*pc) += sizeof(SerializedInst); + + uint16_t nextDataOver = m->data[*pc]; outputInst.immediate = nextDataOver; + outputInst.hasImm16 = true; break; } @@ -53,12 +92,20 @@ Instruction decodeInstruction(Memory* m, uint32_t* pc) { break; } } + + if (opTypes[i] == OP_IMM16) break; } + (*pc) += sizeof(SerializedInst); + return outputInst; } void freeMemory(Memory* m) { free(m->data); free(m); +} + +inline uint32_t getAddress(uint16_t low, uint16_t high) { + return (high << 16) | low; } \ No newline at end of file diff --git a/src/memory.h b/src/memory.h index ab750b8..70575bb 100644 --- a/src/memory.h +++ b/src/memory.h @@ -2,6 +2,8 @@ #include #include +#include +#include #include "instruction.h" #define MEM_CALL_STACK_OFFSET 0x00000FFF @@ -20,4 +22,7 @@ Memory* initMemory(uint32_t size); void freeMemory(Memory* m); uint16_t memoryLoadWord(Memory* m, uint32_t address); void memorySaveWord(Memory* m, uint32_t address, uint16_t value); -Instruction decodeInstruction(Memory* m, uint32_t* pc); \ No newline at end of file +Instruction decodeInstruction(Memory* m, uint32_t* pc); +uint32_t getAddress(uint16_t low, uint16_t high); +uint32_t memorySerializeInst(Memory* m, Instruction inst, uint32_t offset); +void memoryLoadProgram(Memory* m, Instruction program[], size_t numInstructions); \ No newline at end of file