#include #include #include #include #include #include #include #include #include #include #include #include /* * As the name implies, this UM is unsafe. Bad UM code referencing * unknown arrays would make the interpreter crash. There are also a * few other checks that are only assertions. It's also dirty in * that it will only work on 32bit architectures. But it's fast! * * Also, this wasn't written with portability in mind: * - the sys/endian.h header and the be32toh() macro taken from it * may not exist on your system; * - the getprogname() function is also not very portable; * - finally, we're using the C99 uintXX_t types. * * The rest should be OK, unless you're using Solaris :-). */ #define UM_OP_INSN_MASK 0xf0000000 /* 11110000000000000000000000000000 */ #define UM_OP_REGA_MASK 0x000001c0 /* 00000000000000000000000111000000 */ #define UM_OP_REGB_MASK 0x00000038 /* 00000000000000000000000000111000 */ #define UM_OP_REGC_MASK 0x00000007 /* 00000000000000000000000000000111 */ #define UM_OP_INSN(insn) ((insn & UM_OP_INSN_MASK) >> 28) #define UM_OP_REGA(insn) ((insn & UM_OP_REGA_MASK) >> 6) #define UM_OP_REGB(insn) ((insn & UM_OP_REGB_MASK) >> 3) #define UM_OP_REGC(insn) (insn & UM_OP_REGC_MASK) /* * Those are very evil; they reference a variable (um) that isn't * passed to the macro. However, it is hella convenient. */ #define UM_REGA(insn) (um->um_regs[UM_OP_REGA(insn)]) #define UM_REGB(insn) (um->um_regs[UM_OP_REGB(insn)]) #define UM_REGC(insn) (um->um_regs[UM_OP_REGC(insn)]) /* The ``Orthography'' instruction is special. */ #define UM_OPO_REG_MASK 0x0e000000 /* 00001110000000000000000000000000 */ #define UM_OPO_VAL_MASK 0x01ffffff /* 00000001111111111111111111111111 */ #define UM_OPO_REG(insn) ((insn & UM_OPO_REG_MASK) >> 25) #define UM_OPO_VAL(insn) (insn & UM_OPO_VAL_MASK) #define UM_OREG(insn) (um->um_regs[UM_OPO_REG(insn)]) #define UM_OP_CMOV 0 /* Conditional Move */ #define UM_OP_INDEX 1 /* Array Index */ #define UM_OP_AMEND 2 /* Array Amendment */ #define UM_OP_ADD 3 /* Addition */ #define UM_OP_MUL 4 /* Multiplication */ #define UM_OP_DIV 5 /* Division */ #define UM_OP_NOTAND 6 /* Not-And */ #define UM_OP_HALT 7 /* Halt */ #define UM_OP_ALLOC 8 /* Allocation */ #define UM_OP_ABANDON 9 /* Abandonment */ #define UM_OP_OUT 10 /* Output */ #define UM_OP_IN 11 /* Input */ #define UM_OP_LOAD 12 /* Load Program */ #define UM_OP_ORTHO 13 /* Orthography */ #define UM_REGS_SIZE 8 typedef uint32_t um_platter_t; typedef um_platter_t um_register_t; typedef uint32_t um_index_t; typedef size_t um_size_t; typedef struct { um_size_t ua_size; um_platter_t *ua_data; } um_array_t; struct um { um_register_t um_regs[UM_REGS_SIZE]; um_array_t *um_a0; um_size_t um_exec; /* Execution finger. */ }; static um_index_t um_array_index(um_array_t *); static um_array_t *um_array_new(um_size_t, um_array_t *); static um_array_t *um_array_dup(um_array_t *); static void um_array_free(struct um *, um_index_t); static inline um_index_t um_array_index(um_array_t *ua) { um_index_t idx; idx = (um_index_t)ua; /* OMG! */ return (idx); } static void um_array_free(struct um *um, um_index_t idx) { um_array_t *ua; if (idx == 0) { free(um->um_a0); um->um_a0 = NULL; } else { ua = (um_array_t *)idx; /* OMG!!1 */ free(ua); } } static um_array_t * um_array_new(um_size_t size, um_array_t *from) { um_array_t *ua; /* evil++ :-) */ ua = malloc(sizeof(um_array_t) + size); if (ua == NULL) err(1, "malloc"); ua->ua_size = size; ua->ua_data = (um_platter_t *)(ua + 1); if (from != NULL) memcpy(ua->ua_data, from->ua_data, size); else memset(ua->ua_data, 0, size); return (ua); } static um_array_t * um_array_dup(um_array_t *from) { um_array_t *ua; ua = um_array_new(from->ua_size, from); return (ua); } static um_array_t * um_array_lookup(struct um *um, um_index_t idx) { um_array_t *ua; if (idx == 0) return (um->um_a0); ua = (um_array_t *)idx; return (ua); } static struct um * um_load(const char *file) { struct stat sb; struct um *um; um_array_t *a0; um_size_t i; ssize_t n; int fd, error; fd = open(file, O_RDONLY); if (fd == -1) err(1, "open"); error = fstat(fd, &sb); if (error) err(1, "fstat"); um = malloc(sizeof(struct um)); if (um == NULL) err(1, "malloc"); /* Zero the registers and hash table. */ memset(um->um_regs, 0, UM_REGS_SIZE * sizeof(um_register_t)); a0 = um_array_new(sb.st_size, NULL); /* We're not even handling short reads, but they shouldn't happen. */ n = read(fd, a0->ua_data, sb.st_size); if (n == -1) err(1, "read"); assert((unsigned)n == sb.st_size); for (i = 0; i < a0->ua_size / 4; i++) a0->ua_data[i] = be32toh(a0->ua_data[i]); um->um_a0 = a0; um->um_exec = 0; return (um); } static void um_run(struct um *um) { um_array_t *ua, *a0; um_platter_t insn; uint32_t val; int c; for (;;) { a0 = um->um_a0; assert(a0 != NULL); if (um->um_exec >= a0->ua_size) errx(1, "out-of-bounds execution"); insn = a0->ua_data[um->um_exec++]; switch (UM_OP_INSN(insn)) { case UM_OP_CMOV: if (UM_REGC(insn) != 0) UM_REGA(insn) = UM_REGB(insn); break; case UM_OP_INDEX: ua = um_array_lookup(um, UM_REGB(insn)); if (UM_REGC(insn) >= ua->ua_size) errx(1, "exception: out-of-bounds access"); UM_REGA(insn) = ua->ua_data[UM_REGC(insn)]; break; case UM_OP_AMEND: ua = um_array_lookup(um, UM_REGA(insn)); if (UM_REGB(insn) >= ua->ua_size) errx(1, "exception: out-of-bounds access"); ua->ua_data[UM_REGB(insn)] = UM_REGC(insn); break; case UM_OP_ADD: UM_REGA(insn) = UM_REGB(insn) + UM_REGC(insn); break; case UM_OP_MUL: UM_REGA(insn) = UM_REGB(insn) * UM_REGC(insn); break; case UM_OP_DIV: UM_REGA(insn) = UM_REGB(insn) / UM_REGC(insn); break; case UM_OP_NOTAND: UM_REGA(insn) = ~(UM_REGB(insn) & UM_REGC(insn)); break; case UM_OP_HALT: exit(0); break; case UM_OP_ALLOC: val = UM_REGC(insn); ua = um_array_new(val * sizeof(um_platter_t), NULL); UM_REGB(insn) = um_array_index(ua); break; case UM_OP_ABANDON: if (UM_REGC(insn) == 0) errx(1, "trying to anbandon array 0"); um_array_free(um, UM_REGC(insn)); break; case UM_OP_OUT: val = UM_REGC(insn); assert(val <= 255); putchar(val); break; case UM_OP_IN: c = getchar(); if (c == EOF) errx(1, "exception: eof"); assert(c >= 0 && c <= 255); UM_REGC(insn) = c; break; case UM_OP_LOAD: if (UM_REGB(insn) != 0) { ua = um_array_lookup(um, UM_REGB(insn)); a0 = um_array_dup(ua); um_array_free(um, 0); um->um_a0 = a0; } um->um_exec = UM_REGC(insn); break; case UM_OP_ORTHO: UM_OREG(insn) = UM_OPO_VAL(insn); break; default: errx(1, "unknown instruction (%d)\n", UM_OP_INSN(insn)); } } } int main(int argc, char *argv[]) { struct um *um; if (argc != 2) { fprintf(stderr, "usage: %s \n", getprogname()); return (1); } um = um_load(argv[1]); um_run(um); return (0); }