aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.textile44
-rw-r--r--as.c149
-rw-r--r--as.h37
-rw-r--r--emu.c42
-rw-r--r--test.s3
5 files changed, 275 insertions, 0 deletions
diff --git a/README.textile b/README.textile
new file mode 100644
index 0000000..c554351
--- /dev/null
+++ b/README.textile
@@ -0,0 +1,44 @@
+h1. subleq assembler and emulator
+
+An assembler and emulator for the subleq OISC(One Instruction Set Computer) from "Wikipedia":https://en.wikipedia.org/wiki/One_instruction_set_computer#Subtract_and_branch_if_less_than_or_equal_to_zero.
+
+h2. Why?
+
+I got bored.
+
+h2. What does it do?
+
+There are two programs included.
+
+h3. as
+
+as is the subleq compiler. It will take an assembly file and assemble it into emu machine code.
+
+Run it as @./as <input file> <output file>@.
+
+You can use any of instructions on Wikipedia, along with:
+
+* NOP: Do nothing
+* SUBLEQ: This instruction will be inserted into the output verbatim. If c is not given, it is assumed to be the next instruction.
+* HALT: Stop the machine
+* DATA: This can take up to three arguments, which will be inserted verbatim. Arguments not given will be assumed zero.
+
+h3. emu
+
+emu is the subleq computer emulator.
+
+Run it as @./emu <machine code file>@
+
+The file will be mutated to represent the state of memory after the execution.
+
+h2. Building
+
+Compile everything with @make@, or @make as@ and @make emu@. Clean up with @make clean@.
+
+h2. Bugs
+
+@not@ is not yet implemented, as I haven't gotten around to converting it from subleq2 to subleq. PR accepted.
+
+h2. Future work
+
+More Instructions! PR accepted!
diff --git a/as.c b/as.c
new file mode 100644
index 0000000..47890f2
--- /dev/null
+++ b/as.c
@@ -0,0 +1,149 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "as.h"
+
+void gettoken(TOKEN* token, FILE* file) {
+ char i[10];
+ int a, b, c;
+ char line[30];
+ int res;
+
+ if (fgets(line, 30, file) == NULL) {
+ if (feof(file)) {
+ token->eof = true;
+ token->valid = true;
+ } else {
+ token->eof = false;
+ token->valid = true;
+ }
+ } else {
+ res = sscanf(line, "%s %x, %x, %x", i, &a, &b, &c);
+
+ if (!strncmp(i, "nop", 3)) token->i = NOP;
+ if (!strncmp(i, "subleq", 6)) token->i = SUBLEQ;
+ if (!strncmp(i, "sub", 3)) token->i = SUB;
+ if (!strncmp(i, "add", 3)) token->i = ADD;
+ if (!strncmp(i, "jmp", 3)) token->i = JMP;
+ if (!strncmp(i, "mov", 3)) token->i = MOV;
+ if (!strncmp(i, "beq", 3)) token->i = BEQ;
+ if (!strncmp(i, "not", 3)) token->i = NOT;
+ if (!strncmp(i, "halt", 4)) token->i = HALT;
+ if (!strncmp(i, "data", 4)) token->i = DATA;
+
+ token->args = res - 1;
+ token->a = a;
+ token->b = b;
+ token->c = c;
+ token->eof = false;
+ token->valid = true;
+ }
+}
+
+bool handletoken(FILE* file, int* pc, TOKEN* token) {
+ switch(token->i) {
+ case NOP:
+ if (token->args != 0) return false;
+ *pc += 3; out(file, Z, Z, *pc);
+ break;
+ case SUBLEQ:
+ switch (token->args) {
+ case 2:
+ *pc += 3; out(file, token->a, token->b, *pc);
+ break;
+ case 3:
+ *pc += 3; out(file, token->a, token->b, token->c);
+ break;
+ default:
+ return false;
+ }
+ break;
+ case SUB:
+ if (token->args != 2) return false;
+ *pc += 3; out(file, token->a, token->b, *pc);
+ break;
+ case ADD:
+ if (token->args != 2) return false;
+ *pc += 3; out(file, token->a, Z, *pc);
+ *pc += 3; out(file, Z, token->b, *pc);
+ *pc += 3; out(file, Z, Z, *pc);
+ break;
+ case MOV:
+ if (token->args != 2) return false;
+ *pc += 3; out(file, token->b, token->b, *pc);
+ *pc += 3; out(file, token->a, Z, *pc);
+ *pc += 3; out(file, Z, token->b, *pc);
+ *pc += 3; out(file, Z, Z, *pc);
+ break;
+ case BEQ:
+ if (token->args != 2) return false;
+ *pc += 3; out(file, token->b, Z, *pc + 3);
+ *pc += 3; out(file, Z, Z, *pc + 6);
+ *pc += 3; out(file, Z, Z, *pc);
+ *pc += 3; out(file, Z, token->b, token->c);
+ break;
+ case JMP:
+ if (token->args != 1) return false;
+ *pc += 3; out(file, Z, Z, token->a);
+ break;
+ case NOT:
+ if (token->args != 1) return false;
+ *pc += 3;
+ break;
+ case HALT:
+ *pc += 3; out(file, -1, -1, -1);
+ break;
+ case DATA:
+ *pc += 3;
+ switch (token->args) {
+ case 0:
+ out(file, 0, 0, 0);
+ break;
+ case 1:
+ out(file, token->a, 0, 0);
+ break;
+ case 2:
+ out(file, token->a, token->b, 0);
+ break;
+ case 3:
+ out(file, token->a, token->b, token->c);
+ }
+ }
+ return true;
+}
+
+bool parse(TOKEN* token, FILE *out, FILE *in) {
+ int pc = PLEN;
+ int line = 0;
+
+ while(true) {
+ line++;
+ gettoken(token, in);
+ if (token->eof) break;
+ if (!token->valid || !handletoken(out, &pc, token)) {
+ fprintf(stderr, "Error parsing line %d\n", line);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int main(int argc, char** argv) {
+ if (argc < 3)
+ return 1;
+
+ int ret;
+ FILE *in = fopen(argv[1], "r"), *out = fopen(argv[2], "w");
+ TOKEN* token = malloc(sizeof(token));
+
+ preamble(out);
+ ret = parse(token, out, in);
+ postamble(out);
+
+ fclose(in);
+ fclose(out);
+ free(token);
+
+ return !ret;
+}
diff --git a/as.h b/as.h
new file mode 100644
index 0000000..a67e15f
--- /dev/null
+++ b/as.h
@@ -0,0 +1,37 @@
+#define bool char
+#define true 1
+#define false 0
+
+typedef enum {
+ NOP, SUBLEQ, SUB, ADD, JMP, MOV, BEQ, NOT, HALT, DATA
+} INSTRUCTION;
+
+typedef struct {
+ INSTRUCTION i;
+ int a, b, c;
+ int args;
+ bool eof;
+ bool valid;
+} TOKEN;
+
+void out(FILE* file, int a, int b, int c) {
+ fwrite(&a, sizeof(int), 1, file);
+ fwrite(&b, sizeof(int), 1, file);
+ fwrite(&c, sizeof(int), 1, file);
+ fflush(file);
+}
+
+#define PLEN 6
+#define Z 3
+
+void preamble(FILE* file) {
+ // Jump over Z
+ out(file, 1, 1, PLEN);
+ // Z
+ out(file, 0, 0, 0);
+}
+
+void postamble(FILE* file) {
+ // Halt
+ out(file, -1, -1, -1);
+}
diff --git a/emu.c b/emu.c
new file mode 100644
index 0000000..490268c
--- /dev/null
+++ b/emu.c
@@ -0,0 +1,42 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+int main(int argc, char** argv) {
+ if (argc < 2)
+ return 1;
+
+ FILE* file = fopen(argv[1], "r");
+ fseek(file, 0, SEEK_END);
+ int flen = ftell(file) / 4;
+ fseek(file, 0, SEEK_SET);
+
+ int* mem = calloc(flen, sizeof(int));
+ fread(mem, sizeof(int), flen, file);
+ fclose(file);
+
+ int a, b, c;
+ int pc = 0;
+
+ while (pc >= 0) {
+ a = mem[pc];
+ b = mem[pc + 1];
+ c = mem[pc + 2];
+ if (a < 0 || b < 0)
+ pc = -1;
+ else {
+ mem[b] = mem[b] - mem[a];
+ if (mem[b] > 0)
+ pc += 3;
+ else
+ pc = c;
+ }
+ }
+
+ file = fopen(argv[1], "w");
+ fwrite(mem, sizeof(int), flen, file);
+ fflush(file);
+ fclose(file);
+ free(mem);
+
+ return 0;
+}
diff --git a/test.s b/test.s
new file mode 100644
index 0000000..d511dd1
--- /dev/null
+++ b/test.s
@@ -0,0 +1,3 @@
+add 12, 13
+halt
+data 3, 4