summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob McDonnell <jacob@jacobmcdonnell.com>2024-05-05 02:13:51 -0400
committerJacob McDonnell <jacob@jacobmcdonnell.com>2024-05-05 02:13:51 -0400
commit1a93e04fe50d59f0c524e8f4dd53d48a287adfde (patch)
treed374692f894b09b0ac869d3e0c7cb4df47e3a738
Initial Commit
-rw-r--r--README.md70
-rw-r--r--binwrite.c21
-rw-r--r--makefile13
-rwxr-xr-xmipsbin0 -> 17624 bytes
-rw-r--r--mips.c315
-rw-r--r--mips.obin0 -> 11504 bytes
-rw-r--r--test.binbin0 -> 32 bytes
7 files changed, 419 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..370e3e6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,70 @@
+# QDME - Quick and Dirty MIPS Emulator
+
+## Intro
+
+QDME is a simple MIPS emulator written in C. Currently the binary file must be
+manually created, eventually this will change.
+
+## Instructions
+|Instruction|Type|Action|
+|-----------|----|------|
+|add |R |rd = rs + rt|
+|addu |R |rd = rs + rt|
+|and |R |rd = rs & rt|
+|or |R |rd = rs \| rt|
+|slt |R |rd = rs < rt|
+|sltu |R |rd = rs < rt|
+|sub |R |rd = rs - rt|
+|subu |R |rd = rs - rt|
+|xor |R |rd = rs ^ rt|
+|sll |R |rd = rt << shamt|
+|sra |R |rd = rt >> shamt|
+|srl |R |rd = rt >> shamt|
+|div |R |hi = rs % rt lo = rs / rt|
+|divu |R |hi = rs % rt lo = rs / rt|
+|mult |R |hi, lo = rs * rt|
+|multu |R |hi, lo = rs * rt|
+|MFHI |R |rd = hi|
+|MFLO |R |rd = lo|
+|MTHI |R |rs = hi|
+|MTLO |R |rs = lo|
+|jr |R |pc = addr, pc = $ra|
+|jalr |R |rd = pc, pc = addr|
+|syscall |R |System Call|
+|jal |J |$ra = pc, pc = addr|
+|j |J |pc = addr|
+|beq |I | pc = (rs == rt) ? imm + pc + 4 : pc|
+|bne |I | pc = (rs != rt) ? imm + pc + 4 : pc|
+|addi |I |rt = rs + imm|
+|addiu |I |rt = rs + imm|
+|andi |I |rt = rs & imm|
+|ori |I |rt = rs \| imm|
+|xori |I |rt = rs ^ imm|
+|slti |R |rt = rs < imm|
+|sltiu |R |rt = rs < imm|
+|lui |I |rt = rs << 16|
+|lw |I |rs = imm(rt)|
+|sw |I |imm(rt) = rs|
+
+## Syscalls
+
+|Syscall|Action|
+|-------|------|
+|1 |Print integer in $a0|
+|2 |Not Implemented Yet|
+|3 |Not Implemented Yet|
+|4 |Print Null Terminated String, address in $a0|
+|5 |Read integer into $v0|
+|6 |Not Implemented Yet|
+|7 |Not Implemented Yet|
+|8 |Read string of length $a0 into memory at $a1|
+|9 |Not Implemented Yet|
+|10 |Exit with Status code 0|
+|11 |Print Character in $a0|
+|12 |Read Character into $v0|
+|13 |Not Implemented Yet|
+|14 |Not Implemented Yet|
+|15 |Not Implemented Yet|
+|16 |Not Implemented Yet|
+|17 |Exit with Status Code in $0|
+
diff --git a/binwrite.c b/binwrite.c
new file mode 100644
index 0000000..e70edbc
--- /dev/null
+++ b/binwrite.c
@@ -0,0 +1,21 @@
+#include <stdio.h>
+#include <stdint.h>
+
+int main(void) {
+ uint32_t insts[] = {
+ 0x2008000a, // addi $t0, $zero, 10
+ 0x2009000b, // addi $t1, $zero, 11
+ 0x01285020, // add $t2, $t1, $t0
+ 0x20020001, // addi $v0, $zero, 1
+ 0x000a2020, // add $a0, $zero, $t2
+ 0x0000000C, // syscall
+ 0x2002000a, // addi $v0, $zero, 10
+ 0x0000000C, // syscall
+ };
+ FILE *fp = fopen("test.bin", "w");
+ for (size_t i = 0; i < sizeof(insts) / sizeof(insts[0]); i++) {
+ fwrite(insts + i, 1, 4, fp);
+ }
+ fclose(fp);
+ return 0;
+}
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..e5c8228
--- /dev/null
+++ b/makefile
@@ -0,0 +1,13 @@
+CC=gcc
+CFLAGS=-Wall -Werror -pedantic --std=c17
+OBJS=mips.o
+TARGET=mips
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c $@ $^
+
+all: $(TARGET)
+
+mips: $(OBJS)
+ $(CC) $(CFLAGS) -o $@ $^
+
diff --git a/mips b/mips
new file mode 100755
index 0000000..3a7a0a9
--- /dev/null
+++ b/mips
Binary files differ
diff --git a/mips.c b/mips.c
new file mode 100644
index 0000000..dcda5f7
--- /dev/null
+++ b/mips.c
@@ -0,0 +1,315 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#define MEM_SIZE 36
+#define RA 31
+#define V0 2
+#define V1 3
+#define A0 4
+#define A1 5
+#define A2 6
+#define A3 7
+#define WORD_SIZE 4
+
+typedef struct {
+ unsigned int op: 6;
+ unsigned int rs: 5;
+ unsigned int rt: 5;
+ unsigned int rd: 5;
+ unsigned int shamt: 5;
+ unsigned int func: 6;
+} rtype_t;
+
+typedef struct {
+ unsigned int op: 6;
+ unsigned int rs: 5;
+ unsigned int rt: 5;
+ unsigned int imm: 16;
+} itype_t;
+
+typedef struct {
+ unsigned int op: 6;
+ unsigned int addr: 26;
+} jtype_t;
+
+typedef enum { NOP, RTYPE, ITYPE, JTYPE } type_t;
+
+typedef struct {
+ type_t type;
+ union {
+ uint32_t value;
+ rtype_t r;
+ jtype_t j;
+ itype_t i;
+ };
+} inst_t;
+
+uint8_t memory[MEM_SIZE] = {0};
+uint32_t pc = 0, hi = 0, lo = 0;
+uint32_t regFile[32] = {0};
+
+/* InstFetch: Fetch the next instruction from memory into the given
+ * instruction */
+void InstFetch(inst_t *inst) {
+ uint32_t value = 0;
+ memcpy(&value, memory + pc, WORD_SIZE);
+ switch ((value & 0xFC000000) >> 24) {
+ case 0:
+ inst->type = RTYPE;
+ inst->r.op = (value & 0xFC000000) >> 26;
+ inst->r.rs = (value & 0x03E00000) >> 21;
+ inst->r.rt = (value & 0x001F0000) >> 16;
+ inst->r.rd = (value & 0x0000F800) >> 11;
+ inst->r.shamt = (value & 0x000007C0) >> 6;
+ inst->r.func = value & 0x0000002F;
+ break;
+ case 2:
+ case 3:
+ inst->type = JTYPE;
+ inst->j.op = (value & 0xFC000000) >> 26;
+ inst->j.addr = value & 0X03FFFFFF;
+ break;
+ default:
+ inst->type = ITYPE;
+ inst->i.op = (value & 0xFC000000) >> 26;
+ inst->i.rs = (value & 0x03E00000) >> 21;
+ inst->i.rt = (value & 0x001F0000) >> 16;
+ inst->i.imm = value & 0x0000FFFF;
+ break;
+ }
+ if (inst->value == 0) {
+ inst->type = NOP;
+ }
+ pc += WORD_SIZE;
+}
+
+void Syscall(void) {
+ char c, *s;
+ switch (regFile[V0]) {
+ case 1: // print integer
+ printf("%d", regFile[A0]);
+ break;
+ case 2: // print float
+ break;
+ case 3: // print double
+ break;
+ case 4: // print string
+ printf("%s", memory + regFile[A0]);
+ break;
+ case 5: // read integer
+ scanf("%d", (int *)regFile + V0);
+ break;
+ case 6: // read float
+ break;
+ case 7: // read double
+ break;
+ case 8: // read string
+ s = (char *)calloc(regFile[A0], sizeof(char));
+ fgets(s, regFile[A0], stdin);
+ memcpy(memory + regFile[A1], s, regFile[A0]);
+ free(s);
+ break;
+ case 9: // sbrk
+ break;
+ case 10: //exit
+ exit(0);
+ case 11: // print char
+ printf("%c", regFile[A0]);
+ break;
+ case 12: // read char
+ c = 0;
+ scanf("%c", &c);
+ regFile[V0] = c;
+ break;
+ case 13: // open file
+ break;
+ case 14: // read from file
+ break;
+ case 15: // write to file
+ break;
+ case 16: // close file
+ break;
+ case 17: // exit2
+ exit(regFile[A0]);
+ }
+}
+
+/* ExecRtype: Execute a given R-Type instruction */
+void ExecRtype(const inst_t inst) {
+ rtype_t r = inst.r;
+ uint32_t a = regFile[r.rs], b = regFile[r.rt];
+ uint64_t mult = 0;
+ switch (r.func) {
+ case 32: // add
+ regFile[r.rd] = (signed)a + (signed)b;
+ break;
+ case 33: // addu
+ regFile[r.rd] = a + b;
+ break;
+ case 36: // and
+ regFile[r.rd] = a & b;
+ break;
+ case 37: // or
+ regFile[r.rd] = a | b;
+ break;
+ case 42: // slt
+ regFile[r.rd] = (signed)a < (signed)b;
+ break;
+ case 43: // sltu
+ regFile[r.rd] = a < b;
+ break;
+ case 34: // sub
+ regFile[r.rd] = (signed)a - (signed)b;
+ break;
+ case 35: // subu
+ regFile[r.rd] = a - b;
+ break;
+ case 38: // xor
+ regFile[r.rd] = a ^ b;
+ break;
+ case 0: // sll
+ regFile[r.rd] = b << r.shamt;
+ break;
+ case 3: // sra
+ regFile[r.rd] = (signed)b >> r.shamt;
+ break;
+ case 2: // srl
+ regFile[r.rd] = b >> r.shamt;
+ break;
+ case 26: // div
+ hi = (signed)a % (signed)b;
+ lo = (signed)a / (signed)b;
+ break;
+ case 27: // divu
+ hi = a % b;
+ lo = a / b;
+ break;
+ case 24: // mult
+ mult = (signed)a * (signed)b;
+ hi = (0xFFFFFFFF00000000 & mult) >> 32;
+ lo = 0x00000000FFFFFFFF & mult;
+ break;
+ case 25: // multu
+ mult = a * b;
+ hi = (0xFFFFFFFF00000000 & mult) >> 32;
+ lo = 0x00000000FFFFFFFF & mult;
+ break;
+ case 16: // MFHI
+ regFile[r.rd] = hi;
+ break;
+ case 18: // MFLO
+ regFile[r.rd] = lo;
+ break;
+ case 17: // MTHI
+ hi = regFile[r.rs];
+ break;
+ case 19: // MTLO
+ lo = regFile[r.rs];
+ break;
+ case 8: // jr
+ pc = a;
+ break;
+ case 9: // jalr
+ regFile[r.rd] = pc + 4;
+ pc = a;
+ break;
+ case 12: // syscall
+ Syscall();
+ break;
+ }
+}
+
+/* InstExec: Execute a given instruction */
+void InstExec(const inst_t inst) {
+ if (inst.type == NOP) {
+ return;
+ }
+ switch (inst.j.op) {
+ case 0: // R-type instruction
+ ExecRtype(inst);
+ break;
+ case 3: // jal
+ regFile[RA] = pc + 4;
+ /* intentional fall through */
+ case 2: // j
+ pc = (0xF0000000 & pc ) | inst.j.addr;
+ break;
+ case 4: // beq
+ pc = (regFile[inst.i.rs] == regFile[inst.i.rt]) ? pc + 4 +
+ inst.i.imm : pc;
+ break;
+ case 5: // bne
+ pc = (regFile[inst.i.rs] != regFile[inst.i.rt]) ? pc + 4 +
+ inst.i.imm : pc;
+ break;
+ case 8: // addi
+ regFile[inst.i.rt] = (signed)inst.i.rs + (signed)inst.i.imm;
+ break;
+ case 9: // addiu
+ regFile[inst.i.rt] = inst.i.rs + inst.i.imm;
+ break;
+ case 12: // andi
+ regFile[inst.i.rt] = inst.i.rs & inst.i.imm;
+ break;
+ case 13: // ori
+ regFile[inst.i.rt] = inst.i.rs | inst.i.imm;
+ break;
+ case 14: // xori
+ regFile[inst.i.rt] = inst.i.rs ^ inst.i.imm;
+ break;
+ case 10: // slti
+ regFile[inst.i.rt] = (signed)inst.i.rs < (signed)inst.i.imm;
+ break;
+ case 11: // sltiu
+ regFile[inst.i.rt] = inst.i.rs < inst.i.imm;
+ break;
+ case 15: // lui
+ regFile[inst.i.rt] = inst.i.imm << 16;
+ break;
+ case 35: // lw
+ memcpy(regFile + inst.i.rs, memory + inst.i.rt + inst.i.imm,
+ WORD_SIZE);
+ break;
+ case 43: // sw
+ memcpy(memory + inst.i.rt + inst.i.imm, regFile + inst.i.rs,
+ WORD_SIZE);
+ break;
+ }
+}
+
+void CycleCpu(void) {
+ inst_t instFetch, instExec;
+ memset(&instFetch, 0, sizeof(inst_t));
+ memset(&instExec, 0, sizeof(inst_t));
+ while (pc < MEM_SIZE) {
+ InstFetch(&instFetch);
+ InstExec(instExec);
+ instExec = instFetch;
+ }
+}
+
+int LoadBinary(const char * const path) {
+ FILE *fp = fopen(path, "r");
+ if (fp == NULL) {
+ perror("LoadBinary");
+ return -1;
+ }
+ int8_t byte = 0;
+ size_t i = 0;
+ while ((byte = fgetc(fp)) != EOF && i < MEM_SIZE) {
+ memory[i++] = byte;
+ }
+ fclose(fp);
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ if (LoadBinary(*++argv) == -1) {
+ return -1;
+ }
+ CycleCpu();
+}
+
diff --git a/mips.o b/mips.o
new file mode 100644
index 0000000..1a23fe9
--- /dev/null
+++ b/mips.o
Binary files differ
diff --git a/test.bin b/test.bin
new file mode 100644
index 0000000..cb24090
--- /dev/null
+++ b/test.bin
Binary files differ