Compare commits

...

10 Commits

8 changed files with 152 additions and 103 deletions

View File

@@ -9,6 +9,7 @@
#include <inttypes.h>
#include "../instruction.h"
#include "../util.h"
// Table of strings to instructions
struct _stringToInstructionOpcodeTable {char* str; InstructionOpcode opcode;} stringToInstructionOpcodeTable[] = {
@@ -41,6 +42,104 @@ InstructionOpcode stringToInstructionOpcode(const char* string) {
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.
* @param [input] The text to create a program from. NOT the file name
@@ -71,79 +170,8 @@ Program createProgramFromText(const char* input) {
break;
}
if (processingInstWord) {
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;
processBufferedToken(buf, bufSize, &instruction, &processingInstWord, &processingArgNum);
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);
}
break;
}
}
processingArgNum++;
}
buf[0] = '\0';
bufSize = 0;
break;
@@ -153,6 +181,9 @@ Program createProgramFromText(const char* input) {
// Ignore extra new lines
break;
}
if (bufSize > 0) {
processBufferedToken(buf, bufSize, &instruction, &processingInstWord, &processingArgNum);
}
addInstructionToProgram(&program, instruction);
instruction = (Instruction){};
processingInstWord = true;
@@ -196,6 +227,12 @@ Program createProgramFromText(const char* input) {
current++;
if (current >= programLen) {
if (!(processingInstWord && bufSize == 0)) {
if (bufSize > 0) {
processBufferedToken(buf, bufSize, &instruction, &processingInstWord, &processingArgNum);
}
addInstructionToProgram(&program, instruction);
}
break;
}
}
@@ -211,28 +248,21 @@ void serializeProgramToFile(Program* program, const char* 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
size_t written = fwrite("mlc\0", 4, 1, fp);
if (written != 1) {
fprintf(stderr, "failed to write magic header into file %s\n", outputFileName);
fclose(fp);
exit(1);
}
ROMFileHeader header = {
.magic = "mlc\0",
.version = 1,
.numInstructions = totalWords
};
// Write program version
uint8_t version = 1;
written = fwrite(&version, sizeof(uint8_t), 1, fp);
size_t written = fwrite(&header, sizeof(ROMFileHeader), 1, fp);
if (written != 1) {
fprintf(stderr, "failed to write version header into file %s\n", outputFileName);
fclose(fp);
exit(1);
}
// Write instruction count
uint32_t instCount = program->len;
written = fwrite(&instCount, sizeof(uint32_t), 1, fp);
if (written != 1) {
fprintf(stderr, "failed to write instruction count header into file %s\n", outputFileName);
fprintf(stderr, "failed to write file header into file %s\n", outputFileName);
fclose(fp);
exit(1);
}

View File

@@ -30,7 +30,7 @@ void addInstructionToProgram(Program* program, Instruction inst) {
}
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) {
fprintf(stderr, "malloc failed while expanding program\n");
exit(1);
@@ -50,7 +50,7 @@ void addLabelToProgram(Program* program, const char* name, uint8_t position) {
}
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) {
fprintf(stderr, "malloc failed while expanding program labels\n");
exit(1);

View File

@@ -84,7 +84,11 @@ void startEmulator(Emulator* emu) {
#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();
@@ -222,7 +226,7 @@ void startEmulator(Emulator* emu) {
}
Emulator* initEmulator(uint32_t memorySize) {
Emulator* newEmu = malloc(sizeof(Emulator));
Emulator* newEmu = calloc(1, sizeof(Emulator));
if (!newEmu)
return NULL;
@@ -252,4 +256,4 @@ void freeEmulator(Emulator* emu) {
void installDevice(Emulator* emu, Device* device, uint8_t deviceNumber) {
assert(deviceNumber < MAX_DEVICES);
emu->devices[deviceNumber] = device;
}
}

View File

@@ -24,6 +24,8 @@ int main(int argc, char** argv) {
{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]));
*/
@@ -33,9 +35,22 @@ int main(int argc, char** argv) {
return 1;
}
memoryLoadProgram(emu->mem, rom->instructions, rom->header.numInstructions);
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]);
freeEmulator(emu);
return 0;

View File

@@ -83,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
(*pc) += sizeof(SerializedInst);
uint16_t nextDataOver = m->data[*pc];
uint16_t nextDataOver;
memcpy(&nextDataOver, &m->data[*pc], sizeof(uint16_t));
outputInst.immediate = nextDataOver;
outputInst.hasImm16 = true;
break;
@@ -109,4 +110,4 @@ void freeMemory(Memory* m) {
inline uint32_t getAddress(uint16_t low, uint16_t high) {
return (high << 16) | low;
}
}

View File

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

View File

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

View File

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