aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorkdx <kikoodx@paranoici.org>2023-04-13 13:01:40 +0200
committerkdx <kikoodx@paranoici.org>2023-04-13 13:01:40 +0200
commit18d4f15b4df89e7ffdd0b7d3fce915e9e71590df (patch)
treef644c73d4354c79ec18a24169832c28b6924c3fb /src
downloadorga-18d4f15b4df89e7ffdd0b7d3fce915e9e71590df.tar.gz
i'm fucking insane
Diffstat (limited to 'src')
-rw-r--r--src/drain.c30
-rw-r--r--src/drain.h4
-rw-r--r--src/main.c240
3 files changed, 274 insertions, 0 deletions
diff --git a/src/drain.c b/src/drain.c
new file mode 100644
index 0000000..6d9bd43
--- /dev/null
+++ b/src/drain.c
@@ -0,0 +1,30 @@
+#include "drain.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+char *
+drain(FILE *fp)
+{
+ if (fseek(fp, 0, SEEK_END) < 0) {
+ perror("drain:SEEK_END");
+ return NULL;
+ }
+ const long size = ftell(fp);
+ if (fseek(fp, 0, SEEK_SET) < 0) {
+ perror("drain:SEEK_SET");
+ return NULL;
+ }
+ char *const data = malloc(size + 1);
+ if (data == NULL) {
+ perror("drain:malloc");
+ return NULL;
+ }
+ if ((long)fread(data, 1, size, fp) != size) {
+ perror("drain:fread");
+ free(data);
+ return NULL;
+ }
+ data[size] = '\0';
+ return data;
+}
+
diff --git a/src/drain.h b/src/drain.h
new file mode 100644
index 0000000..830261a
--- /dev/null
+++ b/src/drain.h
@@ -0,0 +1,4 @@
+#pragma once
+#include <stdio.h>
+
+char *drain(FILE *fp);
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..fd3ead7
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,240 @@
+#include "drain.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+typedef struct Label Label;
+struct Label {
+ const char *s;
+ size_t addr;
+ Label *next;
+};
+
+static const char ops[][4] = {
+ "NOP", "LIT", "POP", "NIP", "SWP", "ROT", "DUP", "OVR", "EQU", "NEQ",
+ "GTH", "LTH", "JEZ", "JNZ", "JMP", "JRT", "RET", "LDA", "STA", "RED",
+ "WRT", "ADD", "SUB", "MUL", "DIV", "AND", "ORA", "XOR", "LSF", "RSF",
+ "SLP"
+};
+static const int hextable[256] = {
+ ['0'] = 0x0, ['1'] = 0x1, ['2'] = 0x2, ['3'] = 0x3, ['4'] = 0x4,
+ ['5'] = 0x5, ['6'] = 0x6, ['7'] = 0x7, ['8'] = 0x8, ['9'] = 0x9,
+ ['a'] = 0xa, ['b'] = 0xb, ['c'] = 0xc, ['d'] = 0xd, ['e'] = 0xe,
+ ['f'] = 0xf,
+};
+static const char *sep = "\t\n\v\f\r ";
+static int verbose = 0;
+static char *data = NULL, *data_dup = NULL;
+FILE *outfile = NULL;
+static Label *label_root = NULL;
+
+static int
+fail(void)
+{
+ if (data != NULL)
+ free(data);
+ if (data_dup != NULL)
+ free(data_dup);
+ while (label_root != NULL) {
+ Label *const next = label_root->next;
+ free(label_root);
+ label_root = next;
+ }
+ if (outfile != NULL)
+ fclose(outfile);
+ return 1;
+}
+
+static void
+skip_comments(void)
+{
+ char *tok;
+ do
+ tok = strtok(NULL, sep);
+ while (tok != NULL && strcmp(tok, ")") != 0);
+
+ if (tok == NULL) {
+ fprintf(stderr, "unclosed comment");
+ exit(fail());
+ }
+}
+
+static void
+write_short(uint_least16_t v)
+{
+ if (verbose) printf("write: %04x\n", v);
+ if (fputc(v & 0x00ff, outfile) == EOF ||
+ fputc(v / 0x0100, outfile) == EOF) {
+ fprintf(stderr, "fputc failed\n");
+ exit(fail());
+ }
+}
+
+static void
+register_label(const char *s, size_t pc)
+{
+ Label *const label = malloc(sizeof(Label));
+ if (label == NULL) {
+ perror("register_label:malloc");
+ exit(fail());
+ }
+
+ label->s = s;
+ label->addr = pc;
+ label->next = label_root;
+ label_root = label;
+ if (verbose) printf("label: '%s' at pc %zu\n", s, pc);
+}
+
+static void
+write_label(const char *s)
+{
+ for (Label *label = label_root; label != NULL; label = label->next) {
+ if (strcmp(s, label->s) == 0) {
+ write_short(label->addr);
+ return;
+ }
+ }
+ fprintf(stderr, "unknown label '%s'\n", s);
+}
+
+static int
+opcmp(const char *s0, const char *s1)
+{
+ return (s0[0] == s1[0] && s0[1] == s1[1] && s0[2] == s1[2]);
+}
+
+static void
+write_opcode(const char *s)
+{
+ for (int i = 0; i < sizeof(ops) / sizeof(ops[0]); i++) {
+ if (opcmp(s, ops[i])) {
+ write_short(i);
+ return;
+ }
+ }
+ fprintf(stderr, "unknown opcode '%s'\n", s);
+ exit(fail());
+}
+
+static int
+ishex(int c)
+{
+ return ((c >= 'a' && c <= 'f') || (c >= '0' && c <= '9'));
+}
+
+static void
+write_literal(const char *s)
+{
+ if (!ishex(s[0]) && !ishex(s[1]) && !ishex(s[2]) && !ishex(s[3])) {
+ fprintf(stderr, "invalid literal '%s'\n", s);
+ exit(fail());
+ }
+ write_short((hextable[s[0]] * 0x1000) |
+ (hextable[s[1]] * 0x100) |
+ (hextable[s[2]] * 0x10) |
+ (hextable[s[3]]));
+}
+
+static void
+first_pass(char *s)
+{
+ char *tok;
+ size_t pc = 0;
+ for (tok = strtok(s, sep); tok != NULL; tok = strtok(NULL, sep)) {
+ /* Skip comment. */
+ if (strcmp(tok, "(") == 0) {
+ skip_comments();
+ continue;
+ }
+
+ /* Label marker. */
+ if (tok[0] == '@')
+ register_label(tok + 1, pc);
+ else
+ pc += 1;
+ }
+}
+
+static void
+second_pass(char *s)
+{
+ char *tok;
+ for (tok = strtok(s, sep); tok != NULL; tok = strtok(NULL, sep)) {
+ switch (tok[0]) {
+ case '@': /* label declaration, skip */
+ continue;
+ case ',': /* label insertion */
+ write_label(tok + 1);
+ continue;
+ default:
+ break;
+ }
+
+ /* Skip comment. */
+ if (strcmp(tok, "(") == 0) {
+ skip_comments();
+ continue;
+ }
+
+ const size_t tok_len = strlen(tok);
+ switch (tok_len) {
+ case 3: /* opcode */
+ if (verbose) printf("opcode: %s\n", tok);
+ write_opcode(tok);
+ break;
+ case 4: /* literal */
+ if (verbose) printf("literal: %s\n", tok);
+ write_literal(tok);
+ break;
+ default: /* wtf */
+ fprintf(stderr, "unknown token '%s'\n", tok);
+ exit(fail());
+ }
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ if (!(argc == 3 || (argc == 4 && strcmp(argv[3], "-v") == 0))) {
+ fprintf(stderr, "usage: %s <asm> <rom> [-v]\n", argv[0]);
+ return fail();
+ }
+ verbose = (argc == 4);
+
+ /* Dump file to memory. */
+ FILE *const fp = fopen(argv[1], "rb");
+ if (fp == NULL) {
+ perror(argv[1]);
+ return fail();
+ }
+ data = drain(fp);
+ fclose(fp);
+ if (data == NULL)
+ return fail();
+
+ /* Duplicate data. */
+ data_dup = malloc(strlen(data) + 1);
+ if (data_dup == NULL) {
+ perror("main:malloc");
+ return fail();
+ }
+ strcpy(data_dup, data);
+
+ /* First pass, find labels. */
+ first_pass(data);
+
+ /* Open out file. */
+ outfile = fopen(argv[2], "wb");
+ if (outfile == NULL) {
+ perror(argv[2]);
+ return fail();
+ }
+
+ /* Second pass, generate bytecode. */
+ second_pass(data_dup);
+
+ return fail(), 0;
+}