Compare commits
8 Commits
c0da7748d2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 74159849c9 | |||
| 8f64b17777 | |||
| f786235064 | |||
| e7350a254f | |||
| be61ecec0f | |||
| ba4121b6c4 | |||
| c5fc6a8493 | |||
| 3bf340d9f2 |
@@ -9,6 +9,7 @@
|
|||||||
#include <inttypes.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[] = {
|
||||||
@@ -41,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
|
||||||
@@ -71,79 +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);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
processingArgNum++;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf[0] = '\0';
|
buf[0] = '\0';
|
||||||
bufSize = 0;
|
bufSize = 0;
|
||||||
break;
|
break;
|
||||||
@@ -153,6 +181,9 @@ 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);
|
addInstructionToProgram(&program, instruction);
|
||||||
instruction = (Instruction){};
|
instruction = (Instruction){};
|
||||||
processingInstWord = true;
|
processingInstWord = true;
|
||||||
@@ -196,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -211,28 +248,21 @@ void serializeProgramToFile(Program* program, const char* outputFileName) {
|
|||||||
exit(1);
|
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
|
// Write magic header
|
||||||
size_t written = fwrite("mlc\0", 4, 1, fp);
|
ROMFileHeader header = {
|
||||||
if (written != 1) {
|
.magic = "mlc\0",
|
||||||
fprintf(stderr, "failed to write magic header into file %s\n", outputFileName);
|
.version = 1,
|
||||||
fclose(fp);
|
.numInstructions = totalWords
|
||||||
exit(1);
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// Write program version
|
size_t written = fwrite(&header, sizeof(ROMFileHeader), 1, fp);
|
||||||
uint8_t version = 1;
|
|
||||||
written = fwrite(&version, sizeof(uint8_t), 1, fp);
|
|
||||||
if (written != 1) {
|
if (written != 1) {
|
||||||
fprintf(stderr, "failed to write version header into file %s\n", outputFileName);
|
fprintf(stderr, "failed to write file 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);
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -84,7 +84,11 @@ void startEmulator(Emulator* emu) {
|
|||||||
|
|
||||||
#define DISPATCH() if (emu->irq != 0) handleInterrupt(emu); \
|
#define DISPATCH() if (emu->irq != 0) handleInterrupt(emu); \
|
||||||
updateDevices(emu); \
|
updateDevices(emu); \
|
||||||
|
if (emu->halted) { \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
inst = fetchInst(emu); \
|
inst = fetchInst(emu); \
|
||||||
|
printf("%s\n", instructionToCStr(inst)); \
|
||||||
goto *dispatchTable[inst.opcode]
|
goto *dispatchTable[inst.opcode]
|
||||||
|
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
@@ -222,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;
|
||||||
|
|
||||||
@@ -252,4 +256,4 @@ void freeEmulator(Emulator* emu) {
|
|||||||
void installDevice(Emulator* emu, Device* device, uint8_t deviceNumber) {
|
void installDevice(Emulator* emu, Device* device, uint8_t deviceNumber) {
|
||||||
assert(deviceNumber < MAX_DEVICES);
|
assert(deviceNumber < MAX_DEVICES);
|
||||||
emu->devices[deviceNumber] = device;
|
emu->devices[deviceNumber] = device;
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/main.c
15
src/main.c
@@ -35,9 +35,22 @@ int main(int argc, char** argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(emu->mem + MEM_PROGRAM_START, rom->instructions, sizeof(SerializedInst) * rom->header.numInstructions);
|
memcpy(emu->mem->data + MEM_PROGRAM_START, rom->instructions, sizeof(SerializedInst) * rom->header.numInstructions);
|
||||||
|
emu->pc = MEM_PROGRAM_START;
|
||||||
|
|
||||||
startEmulator(emu);
|
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);
|
freeEmulator(emu);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ uint32_t memorySerializeInst(Memory* m, Instruction inst, uint32_t offset) {
|
|||||||
void memoryLoadProgram(Memory* m, Instruction program[], size_t numInstructions) {
|
void memoryLoadProgram(Memory* m, Instruction program[], size_t numInstructions) {
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
for (size_t i = 0; i < numInstructions; i++) {
|
for (size_t i = 0; i < numInstructions; i++) {
|
||||||
printf("%zu. %s\n", i, instructionToCStr(program[i]));
|
|
||||||
offset = memorySerializeInst(m, program[i], offset);
|
offset = memorySerializeInst(m, program[i], offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,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;
|
||||||
@@ -110,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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ mvi a 2
|
|||||||
mvi b 2
|
mvi b 2
|
||||||
|
|
||||||
add a b
|
add a b
|
||||||
hlt
|
hlt
|
||||||
|
hlt
|
||||||
|
|||||||
Reference in New Issue
Block a user