Compare commits

...

19 Commits

Author SHA1 Message Date
74159849c9 Print out result of execution after running 2026-07-17 12:53:56 +10:00
8f64b17777 Include imm16's in the word count (fix the infinite loop bug yay!) 2026-07-17 12:48:04 +10:00
f786235064 use calloc to hopefully not read random stuff 2026-07-17 12:31:42 +10:00
e7350a254f refactor some things to fix things (maybe) 2026-07-17 12:31:29 +10:00
be61ecec0f small fix 2026-07-16 19:29:02 +10:00
ba4121b6c4 Fix bug with imm16 processing 2026-07-16 19:18:49 +10:00
c5fc6a8493 fix instruction loading i think 2026-07-16 19:13:03 +10:00
3bf340d9f2 stuffs 2026-07-16 19:04:36 +10:00
c0da7748d2 Merge branch 'main' of https://chookspace.com/SpookyDervish/MyLittleCPU 2026-07-16 18:43:14 +10:00
14700bdf59 fix file loading 2026-07-16 18:41:52 +10:00
afa45ea82d Update main.c to accept command line input 2026-07-16 18:41:14 +10:00
9828ef2811 Add header generation to assembler 2026-07-16 18:29:51 +10:00
0504067a51 Merge branch 'main' of https://chookspace.com/SpookyDervish/MyLittleCPU 2026-07-16 18:29:05 +10:00
b58b5adf65 start work on interrupts 2026-07-16 18:28:55 +10:00
80bca7059f we assembled something! 2026-07-16 18:21:35 +10:00
82f9644e15 change how flags works 2026-07-16 17:57:14 +10:00
bb0709f6ce stuffs 2026-07-16 14:11:16 +10:00
8e1f83ab31 Merge branch 'main' of https://chookspace.com/SpookyDervish/MyLittleCPU 2026-07-16 14:05:07 +10:00
fb3efc77c3 devices working 2026-07-16 14:05:01 +10:00
14 changed files with 455 additions and 148 deletions

View File

@@ -5,7 +5,8 @@ sources = files(
'src/emu.c', 'src/emu.c',
'src/memory.c', 'src/memory.c',
'src/instruction.c', 'src/instruction.c',
'src/util.c' 'src/util.c',
'src/device.c'
) )
asmSources = files( asmSources = files(

View File

@@ -5,8 +5,11 @@
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <time.h>
#include <inttypes.h>
#include "../instruction.h" #include "../instruction.h"
#include "../util.h"
// Table of strings to instructions // Table of strings to instructions
struct _stringToInstructionOpcodeTable {char* str; InstructionOpcode opcode;} stringToInstructionOpcodeTable[] = { struct _stringToInstructionOpcodeTable {char* str; InstructionOpcode opcode;} stringToInstructionOpcodeTable[] = {
@@ -39,6 +42,104 @@ InstructionOpcode stringToInstructionOpcode(const char* string) {
exit(1); exit(1);
} }
static void processBufferedToken(
char* buf,
size_t bufSize,
Instruction* instruction,
bool* processingInstWord,
uint8_t* processingArgNum
) {
if (*processingInstWord) {
instruction->opcode = stringToInstructionOpcode(buf);
*processingInstWord = false;
return;
}
if (*processingArgNum >= 3) {
fprintf(stderr, "expecting new line after instruction args, got %s\n", buf);
exit(1);
}
OperandType type = INSTRUCTION_OPERAND_TYPES[instruction->opcode][*processingArgNum];
switch (type) {
case OP_NONE: {
fprintf(stderr, "expecting new line after instruction args, got %s\n", buf);
exit(1);
}
case OP_REG: {
if (bufSize > 1) {
fprintf(stderr, "expecting register identifier, got %s\n", buf);
exit(1);
}
switch (buf[0]) {
case 'a':
case 'A':
instruction->operands[*processingArgNum] = REG_A;
break;
case 'b':
case 'B':
instruction->operands[*processingArgNum] = REG_B;
break;
case 'c':
case 'C':
instruction->operands[*processingArgNum] = REG_C;
break;
case 'x':
case 'X':
instruction->operands[*processingArgNum] = REG_X;
break;
case 'y':
case 'Y':
instruction->operands[*processingArgNum] = REG_Y;
break;
case 'z':
case 'Z':
instruction->operands[*processingArgNum] = REG_Z;
break;
case 'r':
case 'R':
instruction->operands[*processingArgNum] = REG_R;
break;
case 'f':
case 'F':
instruction->operands[*processingArgNum] = REG_F;
break;
default: {
fprintf(stderr, "expecting register identifier, got %s\n", buf);
exit(1);
}
}
break;
}
case OP_IMM4: {
char* endptr;
long value = strtol(buf, &endptr, 0);
if (endptr == (char*)buf) {
fprintf(stderr, "couldn't convert %s to a number\n", buf);
exit(1);
}
if (value < 0 || value > 0xF) {
fprintf(stderr, "%s is out of range for a 4-bit immediate\n", buf);
exit(1);
}
instruction->operands[*processingArgNum] = (Register)value;
break;
}
case OP_IMM16: {
char* endptr;
instruction->immediate = strtol(buf, &endptr, 0);
instruction->hasImm16 = true;
if (endptr == (char*)buf) {
fprintf(stderr, "couldn't convert %s to a number\n", buf);
exit(1);
}
break;
}
}
(*processingArgNum)++;
}
/** /**
* Essentially a lexer for the assembly language. * Essentially a lexer for the assembly language.
* @param [input] The text to create a program from. NOT the file name * @param [input] The text to create a program from. NOT the file name
@@ -52,6 +153,8 @@ Program createProgramFromText(const char* input) {
size_t programLen = strlen(input); size_t programLen = strlen(input);
size_t current = 0; size_t current = 0;
uint8_t currentInstruction = 0;
bool processingInstWord = true; bool processingInstWord = true;
uint8_t processingArgNum = 0; uint8_t processingArgNum = 0;
@@ -67,78 +170,8 @@ Program createProgramFromText(const char* input) {
break; break;
} }
if (processingInstWord) { processBufferedToken(buf, bufSize, &instruction, &processingInstWord, &processingArgNum);
instruction.opcode = stringToInstructionOpcode(buf);
processingInstWord = false;
} else {
if (processingArgNum >= 3) {
fprintf(stderr, "expecting new line after instruction args, got %s\n", buf);
exit(1);
}
OperandType type = INSTRUCTION_OPERAND_TYPES[instruction.opcode][processingArgNum];
switch (type) {
case OP_NONE: {
fprintf(stderr, "expecting new line after instruction args, got %s\n", buf);
exit(1);
}
case OP_REG: {
if (bufSize > 1) {
fprintf(stderr, "expecting register identifier, got %s\n", buf);
exit(1);
}
switch (buf[0]) {
case 'a':
case 'A':
instruction.operands[processingArgNum] = REG_A;
break;
case 'b':
case 'B':
instruction.operands[processingArgNum] = REG_B;
break;
case 'c':
case 'C':
instruction.operands[processingArgNum] = REG_C;
break;
case 'x':
case 'X':
instruction.operands[processingArgNum] = REG_X;
break;
case 'y':
case 'Y':
instruction.operands[processingArgNum] = REG_Y;
break;
case 'z':
case 'Z':
instruction.operands[processingArgNum] = REG_Z;
break;
case 'r':
case 'R':
instruction.operands[processingArgNum] = REG_R;
break;
case 'f':
case 'F':
instruction.operands[processingArgNum] = REG_F;
break;
default: {
fprintf(stderr, "expecting register identifier, got %s\n", buf);
exit(1);
}
}
break;
}
case OP_IMM16: {
char* endptr;
instruction.immediate = strtol(buf, &endptr, 0);
if (endptr == (char*)buf) {
fprintf(stderr, "couldn't convert %s to a number\n", buf);
exit(1);
}
}
}
processingArgNum++;
}
buf[0] = '\0'; buf[0] = '\0';
bufSize = 0; bufSize = 0;
break; break;
@@ -148,15 +181,26 @@ Program createProgramFromText(const char* input) {
// Ignore extra new lines // Ignore extra new lines
break; break;
} }
if (bufSize > 0) {
processBufferedToken(buf, bufSize, &instruction, &processingInstWord, &processingArgNum);
}
addInstructionToProgram(&program, instruction);
instruction = (Instruction){};
processingInstWord = true; processingInstWord = true;
processingArgNum = 0; processingArgNum = 0;
buf[0] = '\0'; buf[0] = '\0';
bufSize = 0; bufSize = 0;
currentInstruction++;
break; break;
} }
case ':': { case ':': {
if (!processingInstWord) { if (!processingInstWord) {
fprintf(stderr, "label must be first word on a new line"); fprintf(stderr, "label must be first word on a new line\n");
exit(1);
}
addLabelToProgram(&program, buf, currentInstruction);
if (input[current + 1] != '\n') {
fprintf(stderr, "expecting new line after label\n");
exit(1); exit(1);
} }
break; break;
@@ -183,6 +227,12 @@ Program createProgramFromText(const char* input) {
current++; current++;
if (current >= programLen) { if (current >= programLen) {
if (!(processingInstWord && bufSize == 0)) {
if (bufSize > 0) {
processBufferedToken(buf, bufSize, &instruction, &processingInstWord, &processingArgNum);
}
addInstructionToProgram(&program, instruction);
}
break; break;
} }
} }
@@ -190,6 +240,75 @@ Program createProgramFromText(const char* input) {
return program; return program;
} }
void assemble(const char* input, const char* outputFileName) { void serializeProgramToFile(Program* program, const char* outputFileName) {
Program program = createProgramFromText(input);
FILE* fp = fopen(outputFileName, "wb");
if (fp == NULL) {
fprintf(stderr, "Couldn't open file %s for writing\n", outputFileName);
exit(1);
}
size_t totalWords = 0;
for (size_t i = 0; i < program->len; i++) {
totalWords += program->at[i].hasImm16 ? 2 : 1;
}
// Write magic header
ROMFileHeader header = {
.magic = "mlc\0",
.version = 1,
.numInstructions = totalWords
};
size_t written = fwrite(&header, sizeof(ROMFileHeader), 1, fp);
if (written != 1) {
fprintf(stderr, "failed to write file header into file %s\n", outputFileName);
fclose(fp);
exit(1);
}
for (size_t i = 0; i < program->len; i++) {
Instruction* inst = &program->at[i];
uint16_t 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;
size_t written = fwrite(&packed, sizeof(uint16_t), 1, fp);
if (written != 1) {
fprintf(stderr, "failed to write %" PRIx16 " into file %s\n", packed, outputFileName);
fclose(fp);
exit(1);
}
if (inst->hasImm16) {
size_t written = fwrite(&inst->immediate, sizeof(uint16_t), 1, fp);
if (written != 1) {
fprintf(stderr, "failed to write %" PRIx16 " into file %s\n", packed, outputFileName);
fclose(fp);
exit(1);
}
}
}
fclose(fp);
}
void assemble(const char* input, const char* outputFileName) {
clock_t start = clock();
Program program = createProgramFromText(input);
clock_t parsed = clock();
serializeProgramToFile(&program, outputFileName);
clock_t serialized = clock();
printf("Finished assembling %s, took %lg seconds (%lg secs parsing, %lg secs serializing)\n",
outputFileName,
(serialized - start) / (long double) CLOCKS_PER_SEC,
(parsed - start) / (long double) CLOCKS_PER_SEC,
(serialized - parsed) / (long double) CLOCKS_PER_SEC
);
} }

View File

@@ -30,7 +30,7 @@ void addInstructionToProgram(Program* program, Instruction inst) {
} }
if (program->len + 1 >= program->capacity) { if (program->len + 1 >= program->capacity) {
Instruction* tmp = malloc(sizeof(Instruction) * program->capacity * 2); Instruction* tmp = realloc(program->at, sizeof(Instruction) * program->capacity * 2);
if (tmp == NULL) { if (tmp == NULL) {
fprintf(stderr, "malloc failed while expanding program\n"); fprintf(stderr, "malloc failed while expanding program\n");
exit(1); exit(1);
@@ -50,7 +50,7 @@ void addLabelToProgram(Program* program, const char* name, uint8_t position) {
} }
if (program->labels.len + 1 >= program->labels.capacity) { if (program->labels.len + 1 >= program->labels.capacity) {
Label* tmp = malloc(sizeof(Label) * program->labels.capacity * 2); Label* tmp = realloc(program->labels.at, sizeof(Label) * program->labels.capacity * 2);
if (tmp == NULL) { if (tmp == NULL) {
fprintf(stderr, "malloc failed while expanding program labels\n"); fprintf(stderr, "malloc failed while expanding program labels\n");
exit(1); exit(1);

View File

@@ -2,11 +2,37 @@
Device* createDevice( Device* createDevice(
const char* name, const char* name,
void (*sendToDevice)(uint16_t number), void (*update)(Device* self)
uint16_t (*receiveFromDevice)(),
void (*update)()
) { ) {
Device* new = malloc(sizeof(Device)); Device* new = malloc(sizeof(Device));
if (!new) if (!new)
return NULL; return NULL;
size_t nameLen = strlen(name) + 1;
nameLen = nameLen < 32 ? nameLen : 32;
memcpy(new->name, name, nameLen);
new->name[nameLen] = 0;
new->update = update;
return new;
}
uint16_t deviceRead(Device* self) {
if (self->inPos == 0)
return 0;
return self->in[--self->inPos];
}
void deviceSend(Device* self, uint16_t value) {
if (self->outPos == DEVICE_BUFFER_LENGTH)
return;
self->out[self->outPos++] = value;
}
bool deviceCanRead(Device* self) {
return self->inPos > 0;
} }

View File

@@ -1,18 +1,30 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
typedef struct { #define DEVICE_BUFFER_LENGTH 128
typedef struct Device Device;
struct Device {
char name[32]; char name[32];
void (*sendToDevice)(uint16_t number); uint16_t in[DEVICE_BUFFER_LENGTH];
uint16_t (*receiveFromDevice)(); uint8_t inPos;
void (*update)();
} Device; uint16_t out[DEVICE_BUFFER_LENGTH];
uint8_t outPos;
void (*update)(Device* self);
};
Device* createDevice( Device* createDevice(
const char* name, const char* name,
void (*sendToDevice)(uint16_t number), void (*update)(Device* self)
uint16_t (*receiveFromDevice)(), );
void (*update)()
); uint16_t deviceRead(Device* self);
void deviceSend(Device* self, uint16_t value);
bool deviceCanRead(Device* self);

173
src/emu.c
View File

@@ -1,21 +1,64 @@
#include "emu.h" #include "emu.h"
static inline Instruction fetchInst(Emulator* emu) { static inline Instruction fetchInst(Emulator* emu) {
Instruction inst = decodeInstruction(emu->mem, &emu->pc); return decodeInstruction(emu->mem, &emu->pc);
printf("%s\n", instructionToCStr(inst));
return inst;
} }
static inline void updateFlagsRegister(Emulator* emu, int32_t value) { static inline void updateFlagsRegister(Emulator* emu, int32_t value) {
if (value == 0) {
emu->regs[REG_F] = FLAG_ZERO; FlagsRegister flags = (FlagsRegister)emu->regs[REG_F];
} else if (value < 0) {
emu->regs[REG_F] = FLAG_NEGATIVE; flags.fields.zero = value == 0;
} else if (value > UINT16_MAX) { flags.fields.negative = value < 0;
emu->regs[REG_F] = FLAG_OVERFLOW; 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) { void startEmulator(Emulator* emu) {
static void* dispatchTable[] = { static void* dispatchTable[] = {
&&MOV, &&MOV,
@@ -32,99 +75,107 @@ void startEmulator(Emulator* emu) {
&&JIZ, &&JIZ,
&&CALL, &&CALL,
&&RET, &&RET,
&&SYS, &&PORT,
&&HLT &&HLT
}; };
emu->halted = false; emu->halted = false;
Instruction inst; Instruction inst;
#define DISPATCH() inst = fetchInst(emu); \ #define DISPATCH() if (emu->irq != 0) handleInterrupt(emu); \
goto *dispatchTable[inst.opcode]; updateDevices(emu); \
if (emu->halted) { \
return; \
} \
inst = fetchInst(emu); \
printf("%s\n", instructionToCStr(inst)); \
goto *dispatchTable[inst.opcode]
DISPATCH(); DISPATCH();
MOV: { MOV: {
emu->regs[inst.operands[0]] = emu->regs[inst.operands[1]]; setRegister(emu, inst.operands[0], getRegister(emu, inst.operands[1]));
DISPATCH(); DISPATCH();
} }
MVI: { MVI: {
emu->regs[inst.operands[0]] = inst.immediate; setRegister(emu, inst.operands[0], inst.immediate);
DISPATCH(); DISPATCH();
} }
ADD: { ADD: {
int32_t result = emu->regs[inst.operands[0]] + emu->regs[inst.operands[1]]; int32_t result = getRegister(emu, inst.operands[0]) + getRegister(emu, inst.operands[1]);
updateFlagsRegister(emu, result); updateFlagsRegister(emu, result);
emu->regs[inst.operands[0]] = result; setRegister(emu, inst.operands[0], result);
DISPATCH(); DISPATCH();
} }
SUB: { SUB: {
int32_t result = emu->regs[inst.operands[0]] - emu->regs[inst.operands[1]]; int32_t result = getRegister(emu, inst.operands[0]) - getRegister(emu, inst.operands[1]);
updateFlagsRegister(emu, result); updateFlagsRegister(emu, result);
emu->regs[inst.operands[0]] = result; setRegister(emu, inst.operands[0], result);
DISPATCH(); DISPATCH();
} }
LOAD: { LOAD: {
uint32_t addr = getAddress( uint32_t addr = getAddress(
emu->regs[inst.operands[1]], getRegister(emu, inst.operands[1]),
emu->regs[inst.operands[2]] getRegister(emu, inst.operands[2])
); );
emu->regs[inst.operands[0]] = memoryLoadWord(emu->mem, addr); setRegister(emu, inst.operands[0], memoryLoadWord(emu->mem, addr));
DISPATCH(); DISPATCH();
} }
STORE: { STORE: {
uint32_t addr = getAddress( uint32_t addr = getAddress(
emu->regs[inst.operands[0]], getRegister(emu, inst.operands[1]),
emu->regs[inst.operands[1]] getRegister(emu, inst.operands[2])
); );
memorySaveWord(emu->mem, addr, inst.operands[2]);
memorySaveWord(emu->mem, addr, getRegister(emu, inst.operands[2]));
DISPATCH(); DISPATCH();
} }
AND: { AND: {
int32_t result = emu->regs[inst.operands[0]] & emu->regs[inst.operands[1]]; int32_t result = getRegister(emu, inst.operands[0]) & getRegister(emu, inst.operands[1]);
updateFlagsRegister(emu, result); updateFlagsRegister(emu, result);
emu->regs[inst.operands[0]] = result; setRegister(emu, inst.operands[0], result);
DISPATCH(); DISPATCH();
} }
OR: { OR: {
int32_t result = emu->regs[inst.operands[0]] | emu->regs[inst.operands[1]]; int32_t result = getRegister(emu, inst.operands[0]) | getRegister(emu, inst.operands[1]);
updateFlagsRegister(emu, result); updateFlagsRegister(emu, result);
emu->regs[inst.operands[0]] = result; setRegister(emu, inst.operands[0], result);
DISPATCH(); DISPATCH();
} }
XOR: { XOR: {
int32_t result = emu->regs[inst.operands[0]] ^ emu->regs[inst.operands[1]]; int32_t result = getRegister(emu, inst.operands[0]) ^ getRegister(emu, inst.operands[1]);
updateFlagsRegister(emu, result); updateFlagsRegister(emu, result);
emu->regs[inst.operands[0]] = result; setRegister(emu, inst.operands[0], result);
DISPATCH(); DISPATCH();
} }
SHF: { SHF: {
int32_t result; int32_t result;
if (inst.operands[2] == 0)
result = emu->regs[inst.operands[0]] << emu->regs[inst.operands[1]]; if (inst.operands[0] == 0)
result = getRegister(emu, inst.operands[0]) << getRegister(emu, inst.operands[1]);
else else
result = emu->regs[inst.operands[0]] >> emu->regs[inst.operands[1]]; result = getRegister(emu, inst.operands[0]) >> getRegister(emu, inst.operands[1]);
updateFlagsRegister(emu, result); updateFlagsRegister(emu, result);
emu->regs[inst.operands[0]] = result; setRegister(emu, inst.operands[0], result);
DISPATCH(); DISPATCH();
} }
JMP: { JMP: {
emu->pc = getAddress(inst.operands[0], inst.operands[1]); emu->pc = inst.immediate;
DISPATCH(); DISPATCH();
} }
JIZ: { JIZ: {
if (emu->regs[REG_F] & FLAG_ZERO) if (((FlagsRegister)emu->regs[REG_F]).fields.zero)
emu->pc = getAddress(inst.operands[0], inst.operands[1]); emu->pc = inst.immediate;
DISPATCH(); DISPATCH();
} }
CALL: { CALL: {
// save the pc at the current sp and decrement the sp (stack grows downward) // save the pc at the current sp and decrement the sp (stack grows downward)
((uint32_t*)emu->mem->data)[emu->sp--] = emu->pc; pushProgramCounter(emu);
// jump to given addr // jump to given addr
emu->pc = getAddress(inst.operands[0], inst.operands[1]); emu->pc = inst.immediate;
DISPATCH(); DISPATCH();
} }
@@ -133,8 +184,39 @@ void startEmulator(Emulator* emu) {
emu->pc = ((uint32_t*)emu->mem->data)[++emu->sp]; emu->pc = ((uint32_t*)emu->mem->data)[++emu->sp];
DISPATCH(); DISPATCH();
} }
SYS: { PORT: {
// TODO 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(); DISPATCH();
} }
HLT: { HLT: {
@@ -144,7 +226,7 @@ void startEmulator(Emulator* emu) {
} }
Emulator* initEmulator(uint32_t memorySize) { Emulator* initEmulator(uint32_t memorySize) {
Emulator* newEmu = malloc(sizeof(Emulator)); Emulator* newEmu = calloc(1, sizeof(Emulator));
if (!newEmu) if (!newEmu)
return NULL; return NULL;
@@ -154,7 +236,7 @@ Emulator* initEmulator(uint32_t memorySize) {
// zero out special regs // zero out special regs
newEmu->pc = MEM_PROGRAM_START; newEmu->pc = MEM_PROGRAM_START;
newEmu->sp = 0; newEmu->sp = 0;
newEmu->ihp = 0; newEmu->iht = 0;
// init memory // init memory
newEmu->mem = initMemory(memorySize); newEmu->mem = initMemory(memorySize);
@@ -170,3 +252,8 @@ void freeEmulator(Emulator* emu) {
freeMemory(emu->mem); freeMemory(emu->mem);
free(emu); free(emu);
} }
void installDevice(Emulator* emu, Device* device, uint8_t deviceNumber) {
assert(deviceNumber < MAX_DEVICES);
emu->devices[deviceNumber] = device;
}

View File

@@ -4,6 +4,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include <assert.h>
#include "memory.h" #include "memory.h"
#include "device.h" #include "device.h"
@@ -11,19 +12,39 @@
#define FLAG_NEGATIVE (1 << 1) #define FLAG_NEGATIVE (1 << 1)
#define FLAG_OVERFLOW (1 << 2) #define FLAG_OVERFLOW (1 << 2)
#define MAX_DEVICES 64
// -- TYPES -- // // -- TYPES -- //
typedef struct { typedef struct {
uint16_t regs[REG_MAX]; uint16_t regs[REG_MAX];
Memory* mem; Memory* mem;
Device* devices[MAX_DEVICES];
uint32_t pc; uint32_t pc;
uint32_t ihp; uint32_t iht;
uint32_t sp; uint32_t sp;
uint16_t irq;
bool halted; bool halted;
} Emulator; } Emulator;
typedef struct {
uint32_t interruptHandlers[32];
} __attribute__((packed)) InterruptHandlerTable;
typedef union {
struct {
uint16_t zero : 1;
uint16_t overflow : 1;
uint16_t negative : 1;
uint16_t interrupts : 1;
} fields;
uint16_t raw;
} FlagsRegister;
// -- FUNCTIONS -- // // -- FUNCTIONS -- //
Emulator* initEmulator(uint32_t memorySize); Emulator* initEmulator(uint32_t memorySize);
void freeEmulator(Emulator* emu); void freeEmulator(Emulator* emu);
void startEmulator(Emulator* emu); void startEmulator(Emulator* emu);
void installDevice(Emulator* emu, Device* device, uint8_t deviceNumber);

View File

@@ -11,8 +11,8 @@ const OperandType INSTRUCTION_OPERAND_TYPES[][3] = {
{OP_REG, OP_REG, OP_NONE}, // OR {OP_REG, OP_REG, OP_NONE}, // OR
{OP_REG, OP_REG, OP_NONE}, // XOR {OP_REG, OP_REG, OP_NONE}, // XOR
{OP_REG, OP_REG, OP_REG}, // SHF {OP_REG, OP_REG, OP_REG}, // SHF
{OP_REG, OP_REG, OP_NONE}, // JMP {OP_IMM16, OP_NONE, OP_NONE}, // JMP
{OP_REG, OP_REG, OP_NONE}, // JIZ {OP_IMM16, OP_NONE, OP_NONE}, // JIZ
{OP_IMM16, OP_NONE, OP_NONE},// CALL {OP_IMM16, OP_NONE, OP_NONE},// CALL
{OP_NONE, OP_NONE, OP_NONE}, // RET {OP_NONE, OP_NONE, OP_NONE}, // RET
{OP_REG, OP_REG, OP_IMM4}, // PORT {OP_REG, OP_REG, OP_IMM4}, // PORT

View File

@@ -28,6 +28,8 @@ typedef enum : uint8_t {
// automatically know the length of the registers array // automatically know the length of the registers array
} Register; } Register;
#define REG_IHT_OFFSET 8 // offset of interrupt handler table register
typedef uint16_t SerializedInst; typedef uint16_t SerializedInst;
typedef struct { typedef struct {

View File

@@ -2,18 +2,56 @@
#include "emu.h" #include "emu.h"
#include "util.h" #include "util.h"
int main() { int main(int argc, char** argv) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <ROM to emulate>\n", argv[0]);
return 1;
}
Emulator* emu = initEmulator(0x00010FFF); Emulator* emu = initEmulator(0x00010FFF);
if (!emu) { if (!emu) {
fprintf(stderr, "emu: error: failed to allocate memory for emulator\n"); fprintf(stderr, "emu: error: failed to allocate memory for emulator\n");
return 1; return 1;
} }
RomFile* romFile = readProgramFromFile("../test.bin"); /*
memoryLoadProgram(emu->mem, romFile->instructions, romFile->header.numInstructions); Instruction program[] = {
{INST_MVI, {REG_A}, 0, true},
{INST_MVI, {REG_B}, 123, true},
{INST_PORT, {REG_A, REG_B, 0}, 0, false},
{INST_PORT, {REG_A, REG_B, 1}, 0, false},
{INST_HLT, {}, 0, false}
};
RomFile* f = readProgramFromFile("../add.o");
memcpy(emu->mem + MEM_PROGRAM_START, f->instructions, sizeof(SerializedInst) * f->header.numInstructions);
memoryLoadProgram(emu->mem, program, sizeof(program)/sizeof(program[0]));
*/
RomFile* rom = readProgramFromFile(argv[1]);
if (rom == NULL) {
fprintf(stderr, "Failed to read rom from %s\n", argv[1]);
return 1;
}
memcpy(emu->mem->data + MEM_PROGRAM_START, rom->instructions, sizeof(SerializedInst) * rom->header.numInstructions);
emu->pc = MEM_PROGRAM_START;
startEmulator(emu);
printf("Emulation completed!\n");
printf("Registers:\n");
printf(" A: %d\n", emu->regs[REG_A]);
printf(" B: %d\n", emu->regs[REG_B]);
printf(" C: %d\n", emu->regs[REG_C]);
printf(" X: %d\n", emu->regs[REG_X]);
printf(" Y: %d\n", emu->regs[REG_Y]);
printf(" Z: %d\n", emu->regs[REG_Z]);
printf(" R: %d\n", emu->regs[REG_R]);
printf(" F: %d\n", emu->regs[REG_F]);
//startEmulator(emu);
freeEmulator(emu); freeEmulator(emu);
return 0; return 0;
} }

View File

@@ -71,6 +71,7 @@ Instruction decodeInstruction(Memory* m, uint32_t* pc) {
uint8_t offset = 0; uint8_t offset = 0;
for (uint8_t i = 0; i < 3; i++) { for (uint8_t i = 0; i < 3; i++) {
switch (opTypes[i]) { switch (opTypes[i]) {
case OP_IMM4:
case OP_REG: { case OP_REG: {
// get the data at the current offset, and grab the 4 bits that are there with a bitwise // get the data at the current offset, and grab the 4 bits that are there with a bitwise
// "and" operator // "and" operator
@@ -82,7 +83,8 @@ Instruction decodeInstruction(Memory* m, uint32_t* pc) {
case OP_IMM16: { // the immediate 16 takes up the space where the next instruction would be case OP_IMM16: { // the immediate 16 takes up the space where the next instruction would be
(*pc) += sizeof(SerializedInst); (*pc) += sizeof(SerializedInst);
uint16_t nextDataOver = m->data[*pc]; uint16_t nextDataOver;
memcpy(&nextDataOver, &m->data[*pc], sizeof(uint16_t));
outputInst.immediate = nextDataOver; outputInst.immediate = nextDataOver;
outputInst.hasImm16 = true; outputInst.hasImm16 = true;
break; break;
@@ -108,4 +110,4 @@ void freeMemory(Memory* m) {
inline uint32_t getAddress(uint16_t low, uint16_t high) { inline uint32_t getAddress(uint16_t low, uint16_t high) {
return (high << 16) | low; return (high << 16) | low;
} }

View File

@@ -21,7 +21,7 @@ RomFile* readProgramFromFile(char* path) {
fclose(f); fclose(f);
ROMFileHeader* header = (ROMFileHeader*)buffer; ROMFileHeader* header = (ROMFileHeader*)buffer;
Instruction* instructions = calloc(header->numInstructions, sizeof(SerializedInst)); SerializedInst* instructions = calloc(header->numInstructions, sizeof(SerializedInst));
if (!instructions) { if (!instructions) {
free(buffer); free(buffer);
return NULL; return NULL;

View File

@@ -13,7 +13,7 @@ typedef struct {
typedef struct { typedef struct {
ROMFileHeader header; ROMFileHeader header;
Instruction* instructions; SerializedInst* instructions;
} RomFile; } RomFile;
// -- FUNCTIONS -- // // -- FUNCTIONS -- //

View File

@@ -1,7 +1,6 @@
dingus:
mvi a 2 mvi a 2
mvi b 2 mvi b 2
add a b add a b
hlt hlt
hlt