#define _POSIX_C_SOURCE 200809L // #include "../pu3/pu3.cpu.c" // #include #include #include #include #include #include #include #include #include #include #pragma clang diagnostic ignored "-Wparentheses" #pragma clang diagnostic ignored "-Wreturn-type" #pragma clang diagnostic ignored "-Wempty-body" #pragma clang diagnostic error "-Wimplicit-function-declaration" const bool true = 1; const bool false = 0; string read_file(string path, uint32_t *out_size) { FILE *f = fopen(path, "rb"); string buf; uint32_t size; if (!f) return NULL; fseek(f, 0, SEEK_END); size = ftell(f); fseek(f, 0, SEEK_SET); buf = malloc(size + 1); if (!buf) { fclose(f); return NULL; } if (fread(buf, 1, size, f) != size) { free(buf); fclose(f); return NULL; } buf[ size ] = '\0'; fclose(f); if (out_size) *out_size = size; return buf; } string canonicalize(string input) { string buf = strdup(input); if (!buf) return NULL; size_t i; string tok; size_t cap = 16; size_t top = 0; char **stack = malloc(cap * sizeof(char *)); if (!stack) { free(buf); return NULL; } uint32_t absolute = (buf[ 0 ] == '/'); char *saveptr; for (tok = strtok_r(buf, "/", &saveptr); tok; tok = strtok_r(NULL, "/", &saveptr)) { if (!strcmp(tok, ".")) continue; else if (!strcmp(tok, "..")) { if (top > 0) top--; } else { if (top == cap) { cap *= 2; char **tmp = realloc(stack, cap * sizeof(char *)); if (!tmp) { free(stack); free(buf); return NULL; } stack = tmp; } stack[ top++ ] = tok; } } size_t outlen = absolute ? 1 : 0; if (top == 0) outlen++; for (i = 0; i < top; i++) outlen += strlen(stack[ i ]) + 1; char *out = malloc(outlen); if (!out) { free(stack); free(buf); return NULL; } char *p = out; if (absolute) *p++ = '/'; for (i = 0; i < top; i++) { size_t n = strlen(stack[ i ]); memcpy(p, stack[ i ], n); p += n; if (i < top - 1) *p++ = '/'; } if (p == out) { *p++ = '/'; } *p = '\0'; free(stack); free(buf); return out; } error_debug(string fail_point, string code, uint32_t sz) { uint32_t error_pos = (uint32_t) (fail_point - code); uint32_t i; uint32_t line = 1, col = 1; for (i = 0; i < error_pos && i < sz; i++) { if (code[ i ] == '\n') { line++; col = 1; } else { col++; } } uint32_t line_start = error_pos; while (line_start > 0 && code[ line_start - 1 ] != '\n') { line_start--; } uint32_t line_end = error_pos; while (line_end < sz && code[ line_end ] != '\n' && code[ line_end ] != '\0') { line_end++; } uint32_t tmp = line; uint32_t lineno_width = 1; while (tmp >= 10) { lineno_width++; tmp /= 10; } fprintf( stderr, "\x1b[1m\x1b[31merror:\x1b[0m \x1b[1massembly failed\x1b[0m " "at \x1b[36mline %d\x1b[0m, \x1b[36mcolumn %d\x1b[0m " "(\x1b[33mposition %d\x1b[0m):\n", line, col, error_pos); fprintf( stderr, " %*d | \x1b[1m%.*s\x1b[0m\n", lineno_width, line, line_end - line_start, code + line_start); /* print caret line aligned under the column */ fprintf(stderr, " %*s | ", lineno_width, ""); for (i = line_start; i < error_pos; i++) { fputc((code[ i ] == '\t') ? '\t' : ' ', stderr); } fprintf(stderr, "\x1b[1m\x1b[31m^\x1b[0m \x1b[90mparsing stopped here\x1b[0m\n"); } long powi(long x, unsigned n) { long p = x, r = 1; while (n > 0) { if (n & 1) r *= p; p *= p, n /= 2; } return p == 0 ? 0 : r; } typedef struct { uint32_t pid; uint32_t ring_level; pu3_ctx context; struct { bool spawn_process : 1; bool make_syscall : 1; } permissions; } emu_process; #define pid_count 512 emu_process processes[ pid_count ]; // maximum process count bool process_occupied[ pid_count ]; struct emu_return { bool time_finished; uint32_t e_code; } emu(uint32_t time_slice, uint32_t pid) { process_occupied[ pid ] = true; emu_process process = processes[ pid ]; uint32_t *m = process.context.memory.memory_u32; while (1) { process.context.time_slice = time_slice; uint32_t remaining_time = pu3(process.context); uint32_t e_code = m[ 0 ]; // printf("exit code: %i\n", e_code); if (remaining_time <= 0 && time_slice != -1) { // process_occupied[ pid ] = false; return (struct emu_return) { true, e_code }; } if (e_code != 2) { process_occupied[ pid ] = false; return (struct emu_return) { false, e_code }; } // handle hardware interrupts uint32_t int_code = m[ 1 ]; // 0: output string if (int_code == 0) { uint32_t ptr = m[ 2 ]; uint32_t size = m[ 3 ], i = 0; while (size--) putchar(m[ ptr++ ]); } // 1: input character else if (int_code == 1) { m[ 2 ] = getchar(); } // 2: arithmetics unit else if (int_code == 2) { uint32_t op = m[ 2 ]; uint32_t val1 = m[ 3 ]; uint32_t val2 = m[ 4 ]; m[ 2 ] = 0; if (op == 0) { // multiply m[ 3 ] = val1 * val2; } else if (op == 1) { // divide m[ 3 ] = val1 / val2; } else if (op == 2) { // modulo m[ 3 ] = val1 % val2; } else if (op == 3) { // left shift m[ 3 ] = val1 << val2; } else if (op == 4) { // right shift m[ 3 ] = val1 >> val2; } else if (op == 5) { // pow if (val1 == 2) m[ 3 ] = 1 << val2; else { m[ 3 ] = powi(val1, val2); } } else { // unknown op, set m[2] m[ 2 ] = 1; } } // permission features else if (int_code == 3) { uint32_t op = m[ 2 ]; if (op == 0) { // spawn process uint32_t target_ring = m[ 10 ]; uint32_t memory_start = m[ 11 ]; uint32_t memory_length = m[ 12 ]; uint32_t can_spawn_processes = m[ 13 ]; uint32_t can_make_syscalls = m[ 14 ]; uint32_t max_time_limit = m[ 15 ]; if (target_ring < process.ring_level) { // error - process cannot spawn a process on a lower ring level m[ 2 ] = 2; goto HAD_ERROR; } if ((can_spawn_processes && !(process.permissions.spawn_process & 0b1)) || (can_make_syscalls && !(process.permissions.make_syscall & 0b1))) { // error - host process cannot elevate permissions of the spawning process m[ 2 ] = 3; goto HAD_ERROR; } uint32_t new_pid = 0; for (new_pid = 0; new_pid < pid_count; new_pid++) { if (!process_occupied[ new_pid ]) goto FOUND; } // error - process pool exhausted m[ 2 ] = 2; goto HAD_ERROR; FOUND:; pu3_ctx *__ctx = init_pu3(); process_occupied[ new_pid ] = true; processes[ new_pid ] = (emu_process) { .pid = new_pid, .ring_level = target_ring, .context = *__ctx, .permissions = { .make_syscall = !!can_make_syscalls, .spawn_process = !!can_spawn_processes } }; // copy memory memcpy( __ctx->memory.memory_u32, m + memory_start, memory_length * sizeof(uint32_t)); // run process struct emu_return result = emu(max_time_limit, new_pid); // store results m[ 2 ] = 0; m[ 3 ] = new_pid; m[ 4 ] = result.e_code; m[ 5 ] = result.time_finished; } else if (op == 1) { // resume process pid (if it's halted) uint32_t process_pid = m[ 10 ]; uint32_t max_time_limit = m[ 11 ]; if (!process_occupied[ process_pid ]) { // error - process is not running, start/initialize it first m[ 2 ] = 2; goto HAD_ERROR; } if (processes[ process_pid ].ring_level < processes[ pid ].ring_level) { // error - cannot interact with higher-privileged process m[ 2 ] = 3; goto HAD_ERROR; } // run process struct emu_return result = emu(max_time_limit, process_pid); // store results m[ 2 ] = 0; m[ 3 ] = result.e_code; m[ 4 ] = result.time_finished; } else if (op == 2) { // read memory from another process uint32_t process_pid = m[ 10 ]; uint32_t memory_address = m[ 11 ]; uint32_t read_length = m[ 12 ]; uint32_t target_location = m[ 13 ]; if (processes[ process_pid ].ring_level < processes[ pid ].ring_level || processes[ pid ].ring_level > 3) { // error - cannot interact with higher-privileged process m[ 2 ] = 2; goto HAD_ERROR; } // copy information uint32_t *memory = processes[ process_pid ].context.memory.memory_u32; memcpy( m + target_location, memory + memory_address, read_length * sizeof(uint32_t)); // done! m[ 2 ] = 0; } else if (op == 3) { // write memory to target process uint32_t process_pid = m[ 10 ]; uint32_t memory_address = m[ 11 ]; uint32_t write_length = m[ 12 ]; uint32_t target_location = m[ 13 ]; if (processes[ process_pid ].ring_level < processes[ pid ].ring_level || processes[ pid ].ring_level > 3) { // error - cannot interact with higher-privileged process m[ 2 ] = 2; goto HAD_ERROR; } // write information uint32_t *memory = processes[ process_pid ].context.memory.memory_u32; memcpy( memory + memory_address, m + target_location, write_length * sizeof(uint32_t)); // done! m[ 2 ] = 0; } else { // unknown op, set m[2] m[ 2 ] = 1; } } else { // unknown interrupt. what to do? } HAD_ERROR: m[ 0 ] = 0; } process_occupied[ pid ] = false; } skip_space(string *I) { while ((**I == '\n' || isspace(**I)) && (**I != 0)) (*I)++; } // IR parser for pu2's as2 // use this instead of strdup'ing and allocating extra storage! typedef struct { uint32_t len; string ref; } slice_t; typedef struct { slice_t key; slice_t value; } entry_t; typedef struct { slice_t name; uint32_t location; } label_t; typedef struct { entry_t data; byte arg_count; } macro_t; typedef struct { slice_t name; pu3_inst_t *replace_location; // where to put the resolved address string init_I; } label_delegated_t; clean_slice_ref(slice_t *val) { free(val->ref); } #define static_len(G) (sizeof(G) / sizeof((G)[ 0 ])) #define run_err(result, ...) \ ({ \ ERR_code = init_I; \ fprintf(stderr, "\n"); \ error_debug((result), ERR_code, strlen(ERR_code)); \ fprintf(stderr, "\n\x1b[1m\x1b[31mir2 error:\x1b[0m "); \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, "\n\n"); \ exit(1); \ }) #define checkskip(char, ...) \ ({ \ if (!*I) { run_err(I, __VA_ARGS__); } \ if (*I++ != (char) ) { run_err(I - 1, __VA_ARGS__); } \ }) #define scanword() \ ({ \ slice_t VAR = { 0, I }; \ while (isalnum(*I) || *I == '_') VAR.len++, I++; \ VAR; \ }) #define memslice_t slice_t __attribute__((cleanup(clean_slice_ref))) #define readd() ((used == new.len) ? (new.ref = realloc(new.ref, new.len *= 2)) : 0) #define scanvalue() \ ({ /* value scanner! numbers, characters, strings, variable derefs. */ \ uint32_t value; \ if (isdigit(*I)) { \ value = strtol(I, &I, 10); \ } else if (*I == '\'') { \ I++; \ value = *I++; \ checkskip('\'', "couldn't find closing quote on character"); \ } else if (*I == '"') { \ string str_start = ++I; \ bool T = false; \ uint32_t str_len = 0; \ while (*I != '"' || T) { \ if (*I == '\\') { \ T = !T; \ if (!T && I[ 1 ] == '"') str_len--; \ } \ str_len++; \ I++; \ } \ I++; \ str_len++; \ uint32_t *dest_start = pointers.data -= str_len + 1; \ uint32_t y; \ for (y = 0; y < str_len - 1; y++) { dest_start[ y ] = str_start[ y ]; } \ pointers.data[ -1 ] = 0; \ value = pointers.data - (uint32_t *) storage.assembly_target; \ } else if (*I == '&') { \ I++; \ string starting_I = I; \ slice_t name = scanword(); \ value = __ir2_get_var_loc(name, storage.variables, starting_I, init_I); \ } else { \ run_err(I, "expected compile-time value (number, character, string, dereference)"); \ } \ value; \ }) #define scanvar() \ ({ /* memory scanner! finds @10, var_name */ \ uint32_t value; \ if (*I == '@') { \ if (!isdigit(*++I)) run_err(I - 1, "expected memory index"); \ value = strtol(I, &I, 10); \ } else if (isalpha(*I) || *I == '_') { \ string starting_I = I; \ slice_t name = scanword(); \ value = __ir2_get_var_loc(name, storage.variables, starting_I, init_I); \ } else \ run_err(I, "expected memory index or variable"); \ value; \ }) #define varqual(F) ((F) == '@' || isalpha(F) || (F) == '_') #define litqual(F) (isdigit(F) || (F) == '\'' || (F) == '"' || (F) == '&') string ERR; string ERR_code; struct ir2_pointers { pu3_inst_t *code; uint32_t *data; pu3_inst_t *stack; uint32_t stack_pointer; } pointers; uint32_t init_ir2(pu3_inst_t *target_mem, pu3_inst_t max_size) { static const pu3_inst_t starting_code = 5000; pointers.stack = &target_mem[ 4998 ]; // stack is downward growing pointers.stack_pointer = 4999; // stack pointer pointers.code = &target_mem[ starting_code ]; // code pointer pointers.data = (uint32_t *) &target_mem[ max_size - 1 ]; // data grows downwards return starting_code; } uint32_t ir2_depth = 0; #define space skip_space(&I) // bool has_jump_warned = false; bool has_useless_if_warned = false; bool has_false_while_warned = false; __ir2_get_var_loc(slice_t name, label_t variables[ 1 << 11 ], string error_I, string init_I) { uint32_t j; for (j = 0; j < 1 << 11; j++) { label_t cur = variables[ j ]; if (cur.name.ref == NULL) break; if (cur.name.len != name.len) continue; if (strncmp(cur.name.ref, name.ref, name.len) == 0) { return cur.location; } } run_err(error_I, "unknown variable name \"%.*s\"", name.len, name.ref); } typedef struct { pu3_inst_t *assembly_target; macro_t *macros; label_t *labels; label_delegated_t *forward_refs; label_t *variables; } ir2_storage; // #define IR2_INIT_COMPILE_TIME const int SPARES_LOC = 1005; #define write(inst) (*pointers.code++ = inst) #define get(__opcode, lhs, rhs) build_inst(false, false, opcode.__opcode, lhs, rhs) #define getde(__opcode, lhs, rhs) build_inst(false, true, opcode.__opcode, lhs, rhs) #define getrev(__opcode, lhs, rhs) build_inst(true, false, opcode.__opcode, lhs, rhs) #define getderev(__opcode, lhs, rhs) build_inst(true, true, opcode.__opcode, lhs, rhs) inline void mov_mem_mem(uint32_t indexL, uint32_t indexR) { if (indexR > UINT16_MAX) { // some trickery write(getderev(mov, indexR, SPARES_LOC)); // *spare = *indexR write(getde(mov, indexL, SPARES_LOC)); // *indexL = *spare } else { write(getde(mov, indexL, indexR)); // *indexL = *indexR } } inline void halt() { write(get(hlt, 0, 0)); } inline void set_imm(uint32_t value) { write(getrev(mov, value, 0)); } inline void imm_in_mem(uint32_t addr, uint32_t value) { if (value > UINT16_MAX) { // more trickery write(getrev(mov, value, SPARES_LOC)); // *spare = value write(getde(mov, addr, SPARES_LOC)); // *addr = *spare } else { write(get(mov, addr, value)); // *addr = value } } string _ir2(string I, string filepath, ir2_storage storage) { char T; string init_I = I; ERR = "all good"; while (T = *I) { if (isspace(T)) { space; continue; } string start_I = I; if (T == '}') { if (ir2_depth == 0) run_err(I, "unexpected closing bracket"); ir2_depth--; return ++I; } if (T == '/' && I[ 1 ] == '/') { // comment I += 2; while (*I != '\n' && *I != '\0') I++; continue; } if (T == '/' && I[ 1 ] == '*') { // multi-line comment string old_I = I; I += 2; while (*I != '*' || I[ 1 ] != '/') { if (I[ 1 ] == '\0') { run_err(old_I, "reached EOF whilst looking for closing comment delimeter"); } I++; } I += 2; continue; } // define variable if (strncmp("let", I, 3) == 0 && isspace(I[ 3 ])) { I += 3; space; string backname_I = I; slice_t name = scanword(); space; uint32_t *m_index = --pointers.data; uint32_t start_index = m_index - (uint32_t *) storage.assembly_target; if (*I == ':') { I++; space; checkskip('@', "expected memory index in variable declaration"); if (!isdigit(*I)) { run_err( I, "expected memory index in variable declaration (i think i remember this " "one...)"); } pointers.data++; // free the taken space start_index = strtol(I, &I, 10); space; } // comment out this flag to initialize values at runtime instead of compile-time // #define IR2_INIT_COMPILE_TIME if (*I == ';') { // no starter value, just initialize it to zero I++; #ifdef IR2_INIT_COMPILE_TIME *m_index = 0; #else imm_in_mem(start_index, 0); #endif } else { checkskip('=', "expected semicolon or assignment in variable declaration"); space; if (litqual(*I)) { // literal #ifdef IR2_INIT_COMPILE_TIME *m_index = scanvalue(); #else imm_in_mem(start_index, scanvalue()); #endif } else if (varqual(*I)) { // memory address uint32_t mem_index = scanvar(); mov_mem_mem(start_index, mem_index); } else { run_err(I, "expected compile-time constant, variable or memory index"); } space; checkskip(';', "expected semicolon after variable value"); } uint32_t j; for (j = 0; j < 1 << 11; j++) { label_t cur = storage.variables[ j ]; if (cur.name.ref == NULL) { storage.variables[ j ].name = name; storage.variables[ j ].location = start_index; goto VAR_DONE; } if (cur.name.len == name.len && strncmp(cur.name.ref, name.ref, name.len) == 0) { run_err(backname_I, "duplicate variable name \"%.*s\"", name.len, name.ref); } } run_err(backname_I, "variables count exceeded 1 << 11 (literally how)"); VAR_DONE: continue; } if (strncmp("import", I, 6) == 0 && isspace(I[ 6 ])) { I += 6; space; slice_t path = { 0, I }; while (*I != ';') { I++; path.len++; if (*I == 0 || *I == '\n') run_err(path.ref, "reached invalid character / EOF whilst reading import path"); } checkskip(';', "expected semicolon"); string full; if (path.ref[ 0 ] == '/') full = canonicalize(path.ref); else { string dir = strdup(filepath); // because dirname() mutates string dirpart = dirname(dir); string tmp; asprintf(&tmp, "%s/%.*s", dirpart, path.len, path.ref); full = canonicalize(tmp); free(tmp); free(dir); } uint32_t out_size; string text = read_file(full, &out_size); if (text == NULL) { printf("failed to import \"%s\"\n", full); perror("unable to use import"); free(full); exit(1); } _ir2(text, filepath, storage); free(full); free(text); continue; } // register macros if (strncmp("macro", I, 5) == 0 && isspace(I[ 5 ])) { I += 6; space; // parse macro stuff if (!isalpha(*I) && *I != '_') { run_err(I - 1, "expected letter or underscore at macro name"); } string old_I = I; slice_t name = scanword(); space; checkskip('(', "expected open paren in macro defintion"); space; string before = I; uint32_t arg_count = strtol(I, &I, 10); if (I == before) { run_err(I, "expected number (argument count) in macro definition parens"); } space; checkskip( ')', "expected close paren in macro definition (hint: macro defintions only take an " "argument count)"); space; checkskip('{', "expected open curly bracket in macro body"); // find open space in macros uint32_t x; for (x = 0; x < (1 << 9); x++) { macro_t g = storage.macros[ x ]; if (g.data.key.ref == NULL) goto DONE; if (g.data.key.len == name.len && strncmp(g.data.key.ref, name.ref, name.len) == 0) { run_err(old_I, "redefinition of macro"); } } run_err(I - 1, "registered macro count exceeded 1<<9 (how?)"); DONE:; space; // might aswell skip whitespace for funsies. macro_t *g = &storage.macros[ x ]; g->data.key = name; g->data.value.ref = I; g->data.value.len = 0; g->arg_count = arg_count; uint32_t dep = 0; while (dep != 0 || *I != '}') { if (*I == '{') dep++; if (*I == '}') dep--; g->data.value.len++; I++; if (*I == 0) { checkskip('}', "expected close curly bracket in macro body (met EOF instead)"); } } I++; continue; } // emit exit calls // wow, this was ooold code. had to patch quite a few things here. if (strncmp("exit", I, 4) == 0 && !varqual(I[ 4 ])) { I += 4; space; checkskip('(', "expected open paren on exit blob"); string old = I; uint32_t value = strtol(I, &I, 10); space; if (old == I && *I != ')') { run_err(I - 1, "invalid input to exit blob, expected number"); } space; checkskip(')', "expected open paren on exit blob"); space; checkskip(';', "expected semicolon after call"); // actually emit stuff if (old != I) set_imm(value); halt(); continue; } // emit system if (strncmp("system", I, 6) == 0 && !varqual(I[ 6 ])) { I += 6; space; checkskip('(', "expected open paren on system blob"); space; checkskip( ')', "expected closing paren on system blob (hint: system does not take any arguments)"); space; checkskip(';', "expected semicolon after system blob"); imm_in_mem(0, 2); halt(); continue; } if (strncmp("jump", I, 4) == 0 && !varqual(I[ 4 ])) { I += 4; goto PARSE_JUMP_BLOB; } if (strncmp("jmp", I, 3) == 0 && !varqual(I[ 3 ])) { I += 3; PARSE_JUMP_BLOB: space; checkskip('(', "expected open paren on jump blob"); space; if (!isalpha(*I)) run_err(I, "expected a label name for jump blob argument"); slice_t label = scanword(); space; checkskip(')', "expected closing paren after label name in jump blob"); space; checkskip(';', "excpected semicolon after jump blob"); // resolve right now if the label exists label_t *lbdb = storage.labels; while (lbdb->name.ref != NULL) { label_t cur = *lbdb++; if (cur.name.len == label.len && strncmp(cur.name.ref, label.ref, label.len) == 0) { write(get(jmp, cur.location, 0)); goto FINISH_JMP; } } // not resolved yet.. // find delegations slot label_delegated_t *del; uint32_t j; for (j = 0; j < 1 << 10; j++) { if (storage.forward_refs[ j ].name.ref == NULL) { // found one! del = &storage.forward_refs[ j ]; goto WRITE_DEL; } } run_err( label.ref, "couldn't reserve a spot for this label as the forward references table is full " "(how?)"); WRITE_DEL: write(get(jmp, 0xDEAD10C4, 0)); // DEADLOCK - placeholder for the resolver del->replace_location = pointers.code - 1; // place of imm in code del->name = label; del->init_I = init_I; FINISH_JMP: continue; } if ((strncmp("jnz", I, 3) == 0 && !varqual(I[ 3 ])) || (strncmp("jz", I, 2) == 0 && !varqual(I[ 2 ]))) { bool was_jnz = !!strncmp("jz", I, 2); string name = was_jnz ? "jnz" : "jz"; I += was_jnz ? 3 : 2; space; checkskip('(', "expected open paren on %s blob", name); space; uint32_t target_mem = scanvar(); space; checkskip( ',', "expected comma (second argument) in %s blob (example: `%s(@5, label_name);`)", name, name); space; if (!isalpha(*I)) run_err( I, "expected a label name for %s blob's second argument (example: `%s(@5, " "label_name);`)", name, name); slice_t label = scanword(); space; checkskip( ')', "expected closing paren after label name in %s blob (do i really need to explain " "this to you again?)", name); space; checkskip(';', "excpected semicolon after %s blob", name); // resolve right now if the label exists label_t *lbdb = storage.labels; while (lbdb->name.ref != NULL) { label_t cur = *lbdb++; if (cur.name.len == label.len && strncmp(cur.name.ref, label.ref, label.len) == 0) { // emit j(n)z write(getderev(mov, SPARES_LOC, target_mem)); // *spare = *mem; if (!was_jnz) write(get(not, SPARES_LOC, 0)); // *spare = !*spare; write(getde(jnz, cur.location, SPARES_LOC)); // jmp(cur.location) if *spare; goto FINISH_JNZ; } } // not resolved yet.. // find delegations slot label_delegated_t *del; uint32_t j; for (j = 0; j < 1 << 10; j++) { if (storage.forward_refs[ j ].name.ref == NULL) { // found one! del = &storage.forward_refs[ j ]; goto WRITE_DEL_JNZ; } } run_err( label.ref, "couldn't reserve a spot for this label as the forward references table is full " "(how?)"); WRITE_DEL_JNZ: // emit j(n)z write(getderev(mov, SPARES_LOC, target_mem)); // *spare = *mem; if (!was_jnz) write(get(not, SPARES_LOC, 0)); // *spare = !*spare; write(getde(jnz, 0xDEAD10C4, SPARES_LOC)); // jmp(0xDEAD10C4) if *spare; // DEADLOCK - // placeholder for the resolver del->replace_location = pointers.code - 1; // place of jump in code del->name = label; del->init_I = init_I; FINISH_JNZ: continue; } if (strncmp("if", I, 2) == 0 && !varqual(I[ 2 ])) { // if statement I += 2; space; checkskip('(', "expected open paren for if statement"); space; ir2_depth++; if (isdigit(*I)) { if (!has_useless_if_warned) { has_useless_if_warned = true; printf("warn: redundant if statement\n"); } // redundant if statement: always true / always false uint32_t value = strtol(I, &I, 10); space; checkskip(')', "expected closing paren after if statement argument"); space; checkskip('{', "expected opening bracket for if statement body"); space; if (!value) { // just skip over everything inside struct ir2_pointers pointers_B = pointers; // completely eliminate code generated in useless if statement uint32_t *m2 = calloc(S, sizeof(uint32_t)); label_t *variables_G = calloc(1 << 5, sizeof(label_t)); pointers.data = m2; pointers.code = (uint64_t *) m2; ir2_storage useless_storage = storage; useless_storage.variables = variables_G; useless_storage.assembly_target = (uint64_t *) m2; // get new I location string new_I = _ir2(I, filepath, useless_storage); I = new_I; // restore values pointers = pointers_B; free(m2); free(variables_G); } else { // just... i mean, i guess run the code..? string new_I = _ir2(I, filepath, storage); I = new_I; } continue; } bool was_negated = false; if (*I == '!') { was_negated = true; I++; space; } uint32_t mem_index = scanvar(); space; checkskip(')', "expected closing paren after if statement argument"); space; checkskip('{', "expected opening bracket for if statement body"); space; write(getderev(mov, SPARES_LOC, mem_index)); // *spare = *mem; if (!was_negated) write(get(not, SPARES_LOC, 0)); // *spare = !*spare; write(getde(jnz, 0xDEAD10C4, SPARES_LOC)); // jmp(cur.location) if *spare; pu3_inst_t *patchback = pointers.code - 1; // handle the code stuff string new_I = _ir2(I, filepath, storage); I = new_I; *patchback = getde( jnz, pointers.code - storage.assembly_target, SPARES_LOC); // patch the jump instruction continue; } if (strncmp("while", I, 5) == 0 && !varqual(I[ 5 ])) { I += 5; space; checkskip('(', "expected open paren for while loop"); space; ir2_depth++; if (isdigit(*I)) { uint32_t value = strtol(I, &I, 10); space; checkskip(')', "expected closing paren after while loop argument"); space; checkskip('{', "expected opening bracket for while loop body"); space; if (!value) { // non-running while loop if (!has_false_while_warned) { has_false_while_warned = true; printf("warn: dead code inside while loop\n"); } // just skip over everything inside struct ir2_pointers pointers_B = pointers; // completely eliminate code generated in useless while loop uint32_t *m2 = calloc(S, sizeof(uint32_t)); label_t *variables_G = calloc(1 << 5, sizeof(label_t)); pointers.data = m2; pointers.code = (uint64_t *) m2; ir2_storage useless_storage = storage; useless_storage.assembly_target = (uint64_t *) m2; useless_storage.variables = variables_G; // get new I location string new_I = _ir2(I, filepath, useless_storage); I = new_I; // restore values pointers = pointers_B; free(m2); free(variables_G); } else { // run forever! uint32_t start = pointers.code - storage.assembly_target; string new_I = _ir2(I, filepath, storage); I = new_I; write(get(jmp, start, 0)); // always jump back to the start } continue; } bool was_negated = false; if (*I == '!') { was_negated = true; I++; space; } uint32_t mem_index = scanvar(); space; checkskip(')', "expected closing paren after while loop condition"); space; checkskip('{', "expected opening bracket for while loop body"); space; write(getderev(mov, mem_index, SPARES_LOC)); if (!was_negated) write(get(not, SPARES_LOC, 0)); write(getde(jnz, 0xDEAD10C4, SPARES_LOC)); uint64_t *patchback = pointers.code - 1; uint32_t start_index = pointers.code - storage.assembly_target; // handle the code stuff string new_I = _ir2(I, filepath, storage); I = new_I; // check the condition and jump back if true write(getderev(mov, mem_index, SPARES_LOC)); if (was_negated) write(get(not, SPARES_LOC, 0)); write(getde(jnz, start_index, SPARES_LOC)); *patchback = getde( jnz, pointers.code - storage.assembly_target, SPARES_LOC); // patch the jump instruction continue; } // macro call, maybe? if (isalpha(*I)) { string old_I = I; slice_t name = scanword(); space; if (*I == ':') { I++; // this is a label! label_t *slot; uint32_t j; for (j = 0; j < 1 << 10; j++) { if (storage.labels[ j ].name.ref != NULL) { if (storage.labels[ j ].name.len == name.len && strncmp(storage.labels[ j ].name.ref, name.ref, name.len) == 0) { run_err(old_I, "redefinition of label"); } continue; } slot = &storage.labels[ j ]; goto FOUND_SLOT; } run_err(old_I, "registered label count exceeded 1<<10 (how?)"); FOUND_SLOT: slot->name = name; slot->location = pointers.code - storage.assembly_target; continue; } if (*I != '(') { I = old_I; goto REST_AFTER_MACRO_CALL; } checkskip('(', "expected open paren after alphanumeric word (assuming this is a blob)"); space; slice_t arguments[ 1 << 7 ]; uint32_t arg_count = 0; string fail_I = I; while (*I != ')') { if (!*I) run_err(fail_I, "reached EOF whilst looking for closing paren on blob"); checkskip('[', "expected bracket in blob argument (macro-type)"); arguments[ arg_count ].len = 0; arguments[ arg_count ].ref = I; uint32_t dep = 0; while (dep != 0 || *I != ']') { if (*I == '[') dep++; if (*I == ']') dep--; arguments[ arg_count ].len++; I++; if (*I == 0) { checkskip( ']', "expected closing bracket in macro argument (met EOF instead)"); } } space; checkskip(']', "expected closing bracket in macro arguments"); space; if (*I != ',' && *I != ')') { run_err(I, "expected comma or closing paren in blob"); } if (*I == ',') { I++; space; } arg_count++; } fail_I = I; checkskip(')', "expected closing paren after blob arguments"); space; checkskip(';', "expected semicolon after blob"); // done reading blob! check it and parse macro :) // find index uint32_t i; for (i = 0; i < (1 << 9); i++) { macro_t cur = storage.macros[ i ]; if (cur.data.key.len != name.len) continue; if (strncmp(name.ref, cur.data.key.ref, name.len) == 0) goto FOUND; } run_err(I, "could not find a macro by that name"); FOUND:; if (arg_count != storage.macros[ i ].arg_count) { run_err( fail_I, "macro argument count mismatch (expected %i, got %i)", storage.macros[ i ].arg_count, arg_count); } slice_t target = storage.macros[ i ].data.value; string N = target.ref; string oldN = N; memslice_t new = { target.len, malloc(target.len) }; uint32_t used = 0; bool needs_scan = true; while (needs_scan) { needs_scan = false; while (*N) { if (N - oldN >= target.len) break; if (*N == '/' && isdigit(N[ 1 ])) { N++; string old = N - 1; uint32_t index = strtol(N, &N, 10); if (index > arg_count) { N--; goto END; } if (*N++ != '/') { N = old + 1; while (*N != '/') { new.ref[ used++ ] = *N++; readd(); } goto END; } needs_scan = true; slice_t rep = arguments[ index - 1 ]; string val = rep.ref; for (i = 0; i < rep.len; i++) { new.ref[ used++ ] = *val++; readd(); } continue; } END: new.ref[ used++ ] = *N++; readd(); } } new.ref[ used ] = 0; _ir2(new.ref, filepath, storage); continue; } REST_AFTER_MACRO_CALL: // handle assignments! if (T == '*' || varqual(T)) { bool lhs_deref = T == '*'; if (lhs_deref) { I++; space; if (!varqual(*I)) run_err(I, "expected memory index or variable after dereference"); } string starting_qual = I; uint32_t mem_index = scanvar(); space; if (*I == '-' && I[ 1 ] == '-') { // decrement I += 2; space; checkskip(';', "expected semicolon"); if (lhs_deref) { // *spare = *mem; // *spare = **spare; // *spare--; // *mem = *spare; write(getderev(mov, mem_index, SPARES_LOC)); write(get(dmv, SPARES_LOC, 0)); write(get(dec, SPARES_LOC, 0)); write(getde(mov, mem_index, SPARES_LOC)); } else { write(get(dec, mem_index, 0)); } continue; } if (*I == '+' && I[ 1 ] == '+') { // increment I += 2; space; checkskip(';', "expected semicolon"); if (lhs_deref) { // *spare = *mem; // *spare = **spare; // *spare++; // *mem = *spare; write(getderev(mov, mem_index, SPARES_LOC)); write(get(dmv, SPARES_LOC, 0)); write(get(inc, SPARES_LOC, 0)); write(getde(mov, mem_index, SPARES_LOC)); } else { write(get(inc, mem_index, 0)); } continue; } string expr_I = I; char extra_op = '='; if (*I == '+' || *I == '-' || *I == '&' || *I == '!') extra_op = *I++; checkskip('=', "expected assigment after memory blob (hint: only +-&! are supported)"); space; // if (mem_index == 7 && !has_jump_warned) { // has_jump_warned = true; // printf("\nwarn: writing to memory index 7 is equivalent to a jump\n"); // printf(" if this behavior is intended, use the jump blob instead.\n\n"); // } if (*I == '*' || varqual(*I)) { bool rhs_deref = *I == '*'; if (rhs_deref) { I++; space; if (!varqual(*I)) run_err(I, "expected memory index or variable after dereference"); } if ((lhs_deref || rhs_deref) && extra_op != '=') { run_err( expr_I, "dereferencing is not possible with arithmetic. (moon is lazy)"); } uint32_t mem2_index = scanvar(); space; if (extra_op == '=') { if (lhs_deref) { // **a = *b; // **a = **b; write(getderev(mov, mem2_index, SPARES_LOC)); if (rhs_deref) write(getde(dmv, SPARES_LOC, 0)); write(getde(ddm, mem_index, SPARES_LOC)); } else { if (rhs_deref) { // *a = **b write(getderev(mov, mem2_index, SPARES_LOC)); write(getde(dmv, SPARES_LOC, 0)); write(getde(mov, mem_index, SPARES_LOC)); } else { // *a = *b mov_mem_mem(mem_index, mem2_index); } } } else if (extra_op == '+') { if (mem2_index > UINT16_MAX) { write(getderev(mov, mem2_index, SPARES_LOC)); write(getde(add, mem_index, SPARES_LOC)); } else { write(getde(add, mem_index, mem2_index)); } } else if (extra_op == '-') { if (mem2_index > UINT16_MAX) { write(getderev(mov, mem2_index, SPARES_LOC)); write(getde(sub, mem_index, SPARES_LOC)); } else { write(getde(sub, mem_index, mem2_index)); } } else if (extra_op == '&') { if (mem2_index > UINT16_MAX) { write(getderev(mov, mem2_index, SPARES_LOC)); write(getde(and, mem_index, SPARES_LOC)); } else { write(getde(and, mem_index, mem2_index)); } } else if (extra_op == '!') { // @1 != @2; (@1 = !@2) if (mem2_index > UINT16_MAX) { write(getderev(mov, mem2_index, SPARES_LOC)); write(getde(mov, mem_index, SPARES_LOC)); write(get(not, mem_index, 0)); } else { write(getde(mov, mem_index, mem2_index)); write(get(not, mem_index, 0)); } } } else { uint32_t imm = scanvalue(); if (extra_op == '=') { imm_in_mem(mem_index, imm); } else if (extra_op == '+') { if (imm > UINT16_MAX) { write(getrev(mov, imm, SPARES_LOC)); write(get(add, mem_index, SPARES_LOC)); } else { write(get(add, mem_index, imm)); } } else if (extra_op == '-') { if (imm > UINT16_MAX) { write(getrev(mov, imm, SPARES_LOC)); write(get(sub, mem_index, SPARES_LOC)); } else { write(get(sub, mem_index, imm)); } } else if (extra_op == '&') { if (imm > UINT16_MAX) { write(getrev(mov, imm, SPARES_LOC)); write(get(and, mem_index, SPARES_LOC)); } else { write(get(and, mem_index, imm)); } } else if (extra_op == '!') { // @1 != 5; (@1 = !5;) imm_in_mem(mem_index, !imm); } } space; checkskip(';', "expected semicolon"); continue; } // unknown run_err(I, "unknown token '%c' (%i)", T, T); } return I; } ir2(string I, string filepath, pu3_inst_t *assembly_target) { const uint32_t macros_n = 1 << 9; const uint32_t labels_n = 1 << 10; const uint32_t vars_n = 1 << 11; const uint32_t fwd_n = 1 << 10; macro_t *macros = calloc(macros_n, sizeof(*macros)); label_t *labels = calloc(labels_n, sizeof(*labels)); label_t *variables = calloc(vars_n, sizeof(*variables)); label_delegated_t *forward_refs = calloc(fwd_n, sizeof(*forward_refs)); ir2_storage storage = { assembly_target, macros, labels, forward_refs, variables }; if (!macros || !labels || !variables || !forward_refs) { perror("calloc"); exit(1); } _ir2(I, filepath, storage); label_delegated_t *curf = forward_refs; // handle forward references! while (curf->name.ref != NULL) { label_delegated_t cur = *curf++; // find label uint32_t j; for (j = 0; j < labels_n; j++) { label_t clab = labels[ j ]; if (clab.name.len == cur.name.len && strncmp(clab.name.ref, cur.name.ref, cur.name.len) == 0) { // found it! set_dest_u32(*cur.replace_location, clab.location); goto REP_DONE; } } string init_I = cur.init_I; run_err(cur.name.ref, "could not find label %i %s", cur.name.len, cur.name.ref); REP_DONE:; } free(macros); free(labels); free(variables); free(forward_refs); } volatile long out; void disassemble_pu3_inst(pu3_inst_t inst) { byte op = get_scroll_byte(inst); if (op > 15) { printf("\033[1;31minvalid op\033[0m\n"); return; } byte spare = get_spare(inst); bool swap = (spare & 0x80); bool deref = (spare & 0x40); uint32_t dest = get_dest_u32(inst); uint16_t data = get_data_u16(inst); uint32_t lhs = swap ? data : (uint32_t) dest; uint32_t rhs = swap ? (uint32_t) dest : data; const char *name = op_names[ op ]; printf("\033[1;32m%-3s\033[0m ", name); switch (op) { case 0: // nop if (deref) printf("\033[1;33m[\033[0m"); printf("\033[1;34m%i\033[0m", data); if (deref) printf("\033[1;33m]\033[0m"); break; case 1: // hlt break; case 5: case 6: case 11: // inc, dec, not printf("\033[1;33m[\033[0m\033[1;34m%i\033[0m\033[1;33m]\033[0m", dest); break; case 12: // jmp printf("\033[1;34m%i\033[0m", dest); break; case 14: // dmv printf("\033[1;33m[\033[0m\033[1;34m%i\033[0m\033[1;33m]\033[0m", lhs); break; case 13: // jnz printf("\033[1;34m%i\033[0m, ", lhs); if (deref) printf("\033[1;33m[\033[0m"); printf("\033[1;34m%i\033[0m", rhs); if (deref) printf("\033[1;33m]\033[0m"); break; case 15: // ddm printf("\033[1;33m[[\033[0m\033[1;34m%i\033[0m\033[1;33m]]\033[0m, ", lhs); printf("\033[1;33m[\033[0m"); if (deref) printf("\033[1;33m[\033[0m"); printf("\033[1;34m%i\033[0m", rhs); if (deref) printf("\033[1;33m]\033[0m"); printf("\033[1;33m]\033[0m"); break; default: // mov, add, sub, mul, lsh, rsh, and printf("\033[1;33m[\033[0m\033[1;34m%i\033[0m\033[1;33m]\033[0m, ", lhs); if (deref) printf("\033[1;33m[\033[0m"); printf("\033[1;34m%i\033[0m", rhs); if (deref) printf("\033[1;33m]\033[0m"); break; } printf("\n"); } main(int argc, char **argv) { if (argc == 1) { printf( "\nusage:\n" " %s open and compile ir2-compliant code\n" " %s benchmark benchmark pu2 (two modes: time and rdtsc)\n\n", argv[ 0 ], argv[ 0 ]); return 1; } pu3_ctx *__ctx = init_pu3(); pu3_ctx ctx = *__ctx; if (strcmp(argv[ 1 ], "benchmark") == 0) { pu3_benchmark(); exit(0); } char *filename = argv[ 1 ]; uint32_t file_size; string contents = read_file(filename, &file_size); if (contents == NULL) { perror("unable to open file"); exit(1); } const uint32_t starting_code_ptr = init_ir2(ctx.memory.memory_u64, S); uint32_t o_size; long before = ntime(); // long cycles_bef = rdtsc(); ir2(contents, filename, ctx.memory.memory_u64); // long cycles_aft = rdtsc(); long after = ntime(); *ctx.pc = starting_code_ptr; // uint32_t j = 0; // for(j = 0; j < 32; j++) { // printf("%i: ", j); // disassemble_pu3_inst(ctx.memory.memory_u64[starting_code_ptr + j]); // } printf("assembly took %li milliseconds\n", (after - before) / 1000); free(contents); // uint32_t res_size = code - &m[ 4000 ]; // run the kernel (ring zero) forever (time slice -1) uint32_t mem_size = S; // pid zero - kernel processes[ 0 ] = (emu_process) { .context = ctx, .ring_level = 0, .permissions = { .make_syscall = true, .spawn_process = true } }; emu(-1, 0); }