Files
MyLittleCPU/src/emu.c

259 lines
6.6 KiB
C

#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;
}