2026-07-15 15:02:54 +10:00
|
|
|
#include "emu.h"
|
|
|
|
|
|
2026-07-16 12:31:29 +10:00
|
|
|
static inline Instruction fetchInst(Emulator* emu) {
|
2026-07-16 14:05:01 +10:00
|
|
|
return decodeInstruction(emu->mem, &emu->pc);
|
2026-07-15 17:31:10 +10:00
|
|
|
}
|
|
|
|
|
|
2026-07-16 12:31:29 +10:00
|
|
|
static inline void updateFlagsRegister(Emulator* emu, int32_t value) {
|
2026-07-16 17:57:14 +10:00
|
|
|
|
|
|
|
|
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;
|
2026-07-15 17:31:10 +10:00
|
|
|
}
|
|
|
|
|
|
2026-07-16 14:05:01 +10:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-16 18:28:55 +10:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-15 17:31:10 +10:00
|
|
|
void startEmulator(Emulator* emu) {
|
|
|
|
|
static void* dispatchTable[] = {
|
|
|
|
|
&&MOV,
|
|
|
|
|
&&MVI,
|
|
|
|
|
&&ADD,
|
|
|
|
|
&&SUB,
|
|
|
|
|
&&LOAD,
|
|
|
|
|
&&STORE,
|
|
|
|
|
&&AND,
|
|
|
|
|
&&OR,
|
|
|
|
|
&&XOR,
|
|
|
|
|
&&SHF,
|
|
|
|
|
&&JMP,
|
|
|
|
|
&&JIZ,
|
|
|
|
|
&&CALL,
|
|
|
|
|
&&RET,
|
2026-07-16 14:05:01 +10:00
|
|
|
&&PORT,
|
2026-07-15 17:31:10 +10:00
|
|
|
&&HLT
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
emu->halted = false;
|
|
|
|
|
Instruction inst;
|
|
|
|
|
|
2026-07-16 18:28:55 +10:00
|
|
|
#define DISPATCH() if (emu->irq != 0) handleInterrupt(emu); \
|
|
|
|
|
updateDevices(emu); \
|
2026-07-16 19:13:03 +10:00
|
|
|
if (emu->halted) { \
|
|
|
|
|
return; \
|
|
|
|
|
} \
|
2026-07-16 14:05:01 +10:00
|
|
|
inst = fetchInst(emu); \
|
2026-07-16 19:13:03 +10:00
|
|
|
printf("%s\n", instructionToCStr(inst)); \
|
2026-07-16 14:05:01 +10:00
|
|
|
goto *dispatchTable[inst.opcode]
|
2026-07-15 17:31:10 +10:00
|
|
|
|
|
|
|
|
DISPATCH();
|
|
|
|
|
|
|
|
|
|
MOV: {
|
2026-07-16 18:28:55 +10:00
|
|
|
setRegister(emu, inst.operands[0], getRegister(emu, inst.operands[1]));
|
2026-07-15 17:31:10 +10:00
|
|
|
DISPATCH();
|
|
|
|
|
}
|
|
|
|
|
MVI: {
|
2026-07-16 18:28:55 +10:00
|
|
|
setRegister(emu, inst.operands[0], inst.immediate);
|
2026-07-15 17:31:10 +10:00
|
|
|
DISPATCH();
|
|
|
|
|
}
|
|
|
|
|
ADD: {
|
2026-07-16 18:28:55 +10:00
|
|
|
int32_t result = getRegister(emu, inst.operands[0]) + getRegister(emu, inst.operands[1]);
|
2026-07-15 17:31:10 +10:00
|
|
|
updateFlagsRegister(emu, result);
|
2026-07-16 18:28:55 +10:00
|
|
|
setRegister(emu, inst.operands[0], result);
|
2026-07-15 17:31:10 +10:00
|
|
|
DISPATCH();
|
|
|
|
|
}
|
|
|
|
|
SUB: {
|
2026-07-16 18:28:55 +10:00
|
|
|
int32_t result = getRegister(emu, inst.operands[0]) - getRegister(emu, inst.operands[1]);
|
2026-07-15 17:31:10 +10:00
|
|
|
updateFlagsRegister(emu, result);
|
2026-07-16 18:28:55 +10:00
|
|
|
setRegister(emu, inst.operands[0], result);
|
2026-07-15 17:31:10 +10:00
|
|
|
DISPATCH();
|
|
|
|
|
}
|
|
|
|
|
LOAD: {
|
|
|
|
|
uint32_t addr = getAddress(
|
2026-07-16 18:28:55 +10:00
|
|
|
getRegister(emu, inst.operands[1]),
|
|
|
|
|
getRegister(emu, inst.operands[2])
|
2026-07-15 17:31:10 +10:00
|
|
|
);
|
|
|
|
|
|
2026-07-16 18:28:55 +10:00
|
|
|
setRegister(emu, inst.operands[0], memoryLoadWord(emu->mem, addr));
|
2026-07-15 17:31:10 +10:00
|
|
|
DISPATCH();
|
|
|
|
|
}
|
|
|
|
|
STORE: {
|
|
|
|
|
uint32_t addr = getAddress(
|
2026-07-16 18:28:55 +10:00
|
|
|
getRegister(emu, inst.operands[1]),
|
|
|
|
|
getRegister(emu, inst.operands[2])
|
2026-07-15 17:31:10 +10:00
|
|
|
);
|
2026-07-16 18:28:55 +10:00
|
|
|
|
|
|
|
|
memorySaveWord(emu->mem, addr, getRegister(emu, inst.operands[2]));
|
2026-07-15 17:31:10 +10:00
|
|
|
DISPATCH();
|
|
|
|
|
}
|
|
|
|
|
AND: {
|
2026-07-16 18:28:55 +10:00
|
|
|
int32_t result = getRegister(emu, inst.operands[0]) & getRegister(emu, inst.operands[1]);
|
2026-07-15 17:31:10 +10:00
|
|
|
updateFlagsRegister(emu, result);
|
2026-07-16 18:28:55 +10:00
|
|
|
setRegister(emu, inst.operands[0], result);
|
2026-07-15 17:31:10 +10:00
|
|
|
DISPATCH();
|
|
|
|
|
}
|
|
|
|
|
OR: {
|
2026-07-16 18:28:55 +10:00
|
|
|
int32_t result = getRegister(emu, inst.operands[0]) | getRegister(emu, inst.operands[1]);
|
2026-07-15 17:31:10 +10:00
|
|
|
updateFlagsRegister(emu, result);
|
2026-07-16 18:28:55 +10:00
|
|
|
setRegister(emu, inst.operands[0], result);
|
2026-07-15 17:31:10 +10:00
|
|
|
DISPATCH();
|
|
|
|
|
}
|
|
|
|
|
XOR: {
|
2026-07-16 18:28:55 +10:00
|
|
|
int32_t result = getRegister(emu, inst.operands[0]) ^ getRegister(emu, inst.operands[1]);
|
2026-07-15 17:31:10 +10:00
|
|
|
updateFlagsRegister(emu, result);
|
2026-07-16 18:28:55 +10:00
|
|
|
setRegister(emu, inst.operands[0], result);
|
2026-07-15 17:31:10 +10:00
|
|
|
DISPATCH();
|
|
|
|
|
}
|
|
|
|
|
SHF: {
|
|
|
|
|
int32_t result;
|
2026-07-16 18:28:55 +10:00
|
|
|
|
|
|
|
|
if (inst.operands[0] == 0)
|
|
|
|
|
result = getRegister(emu, inst.operands[0]) << getRegister(emu, inst.operands[1]);
|
2026-07-15 17:31:10 +10:00
|
|
|
else
|
2026-07-16 18:28:55 +10:00
|
|
|
result = getRegister(emu, inst.operands[0]) >> getRegister(emu, inst.operands[1]);
|
2026-07-15 17:31:10 +10:00
|
|
|
|
|
|
|
|
updateFlagsRegister(emu, result);
|
2026-07-16 18:28:55 +10:00
|
|
|
setRegister(emu, inst.operands[0], result);
|
2026-07-15 17:31:10 +10:00
|
|
|
DISPATCH();
|
|
|
|
|
}
|
|
|
|
|
JMP: {
|
2026-07-16 18:28:55 +10:00
|
|
|
emu->pc = inst.immediate;
|
2026-07-15 17:31:10 +10:00
|
|
|
DISPATCH();
|
|
|
|
|
}
|
|
|
|
|
JIZ: {
|
2026-07-16 18:28:55 +10:00
|
|
|
if (((FlagsRegister)emu->regs[REG_F]).fields.zero)
|
|
|
|
|
emu->pc = inst.immediate;
|
2026-07-15 17:31:10 +10:00
|
|
|
DISPATCH();
|
|
|
|
|
}
|
|
|
|
|
CALL: {
|
|
|
|
|
// save the pc at the current sp and decrement the sp (stack grows downward)
|
2026-07-16 18:28:55 +10:00
|
|
|
pushProgramCounter(emu);
|
2026-07-15 17:31:10 +10:00
|
|
|
|
|
|
|
|
// jump to given addr
|
2026-07-16 18:28:55 +10:00
|
|
|
emu->pc = inst.immediate;
|
2026-07-15 17:31:10 +10:00
|
|
|
|
|
|
|
|
DISPATCH();
|
|
|
|
|
}
|
|
|
|
|
RET: {
|
|
|
|
|
// return to value at sp and increment it
|
|
|
|
|
emu->pc = ((uint32_t*)emu->mem->data)[++emu->sp];
|
|
|
|
|
DISPATCH();
|
|
|
|
|
}
|
2026-07-16 14:05:01 +10:00
|
|
|
PORT: {
|
2026-07-16 18:28:55 +10:00
|
|
|
uint16_t port = getRegister(emu, inst.operands[0]);
|
2026-07-16 14:05:01 +10:00
|
|
|
if (port >= MAX_DEVICES) {
|
|
|
|
|
DISPATCH();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t direction = inst.operands[2];
|
|
|
|
|
|
|
|
|
|
Device* device = emu->devices[port];
|
|
|
|
|
if (!device) {
|
|
|
|
|
DISPATCH();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (direction == 0) {
|
2026-07-16 18:28:55 +10:00
|
|
|
uint16_t value = getRegister(emu, inst.operands[1]);
|
2026-07-16 14:05:01 +10:00
|
|
|
|
|
|
|
|
if (device->inPos == DEVICE_BUFFER_LENGTH) {
|
|
|
|
|
DISPATCH();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
device->in[device->inPos] = value;
|
|
|
|
|
device->inPos++;
|
|
|
|
|
} else {
|
|
|
|
|
if (device->outPos == 0) {
|
2026-07-16 18:28:55 +10:00
|
|
|
setRegister(emu, inst.operands[1], 0);
|
2026-07-16 14:05:01 +10:00
|
|
|
DISPATCH();
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-16 18:28:55 +10:00
|
|
|
setRegister(emu, inst.operands[1], device->out[device->outPos-1]);
|
2026-07-16 14:05:01 +10:00
|
|
|
if (device->outPos > 0)
|
|
|
|
|
device->outPos--;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-15 17:31:10 +10:00
|
|
|
DISPATCH();
|
|
|
|
|
}
|
|
|
|
|
HLT: {
|
|
|
|
|
emu->halted = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-15 15:02:54 +10:00
|
|
|
Emulator* initEmulator(uint32_t memorySize) {
|
|
|
|
|
Emulator* newEmu = malloc(sizeof(Emulator));
|
|
|
|
|
if (!newEmu)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
// zero out registers
|
|
|
|
|
memset(newEmu->regs, 0, sizeof(newEmu->regs));
|
|
|
|
|
|
2026-07-15 15:54:51 +10:00
|
|
|
// zero out special regs
|
2026-07-15 17:31:10 +10:00
|
|
|
newEmu->pc = MEM_PROGRAM_START;
|
2026-07-15 15:54:51 +10:00
|
|
|
newEmu->sp = 0;
|
2026-07-16 18:28:55 +10:00
|
|
|
newEmu->iht = 0;
|
2026-07-15 15:54:51 +10:00
|
|
|
|
2026-07-15 15:02:54 +10:00
|
|
|
// init memory
|
|
|
|
|
newEmu->mem = initMemory(memorySize);
|
|
|
|
|
if (!newEmu->mem) {
|
|
|
|
|
free(newEmu);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return newEmu;
|
2026-07-15 15:54:51 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void freeEmulator(Emulator* emu) {
|
|
|
|
|
freeMemory(emu->mem);
|
|
|
|
|
free(emu);
|
2026-07-16 12:31:29 +10:00
|
|
|
}
|
2026-07-16 14:05:01 +10:00
|
|
|
|
|
|
|
|
void installDevice(Emulator* emu, Device* device, uint8_t deviceNumber) {
|
|
|
|
|
assert(deviceNumber < MAX_DEVICES);
|
|
|
|
|
emu->devices[deviceNumber] = device;
|
|
|
|
|
}
|