#include "pu2.out.c" #include #include typedef char *string; string read_file(string path, size_t *out_size) { FILE *f = fopen(path, "rb"); string buf; size_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; } error_debug(string fail_point, string code, int sz) { int error_pos = (int) (fail_point - code); int i; int line = 1, col = 1; for (i = 0; i < error_pos && i < sz; i++) { if (code[ i ] == '\n') { line++; col = 1; } else { col++; } } 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); int line_start = error_pos; while (line_start > 0 && code[ line_start - 1 ] != '\n') { line_start--; } int line_end = error_pos; while (line_end < sz && code[ line_end ] != '\n' && code[ line_end ] != '\0') { line_end++; } fprintf(stderr, " \x1b[1m"); for (i = line_start; i < line_end; i++) { fputc(code[ i ], stderr); } fprintf(stderr, "\x1b[0m\n"); fprintf(stderr, " "); 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 r; } emu() { while (1) { pu2(); int e_code = m[ 0 ]; if (e_code != 2) return e_code; // handle hardware interrupts int int_code = m[ 1 ]; // 0: output string if (int_code == 0) { int ptr = m[ 2 ]; int 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) { int op = m[ 2 ]; int val1 = m[ 3 ]; int 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 ] = val1 << val2; else { m[ 3 ] = powi(val1, val2); } } else { // unknown op, set m[2] m[ 2 ] = 1; } } else { // unknown interrupt. what to do? } m[ 0 ] = 0; } } skip_space(string *I) { while ((**I == '\n' || isspace(**I)) && (**I != 0)) (*I)++; } // IR parser for pu2's as2 // clang-format off struct { int hlt, imm, lod, str, swp, add, sub, and, not, acc, a2u, u2a, b2u, u2b; } opcode = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; // clang-format on typedef unsigned char byte; typedef byte bool; const bool true = 1; const bool false = 0; // use this instead of strdup'ing and allocating extra storage! typedef struct { size_t len; string ref; } slice; typedef struct { slice key; slice value; } entry; clean_slice_ref(slice *val) { free(val->ref); } #define static_len(G) (sizeof(G) / sizeof((G)[ 0 ])) #define emit(OP, VAL) \ ({ \ *Code++ = (OP); \ *Code++ = (VAL); \ }) #define store() \ ({ \ emit(opcode.acc, 1); \ emit(opcode.str, 0); \ }) #define run_err(result) \ ({ \ printf("\n"); \ error_debug((result), ERR_code, strlen(ERR_code)); \ printf("\nir2 error message: %s\n", ERR); \ printf("\n"); \ exit(1); \ }) #define checkskip(char, message) \ ({ \ if (!*I) { \ ERR = (message); \ run_err(I); \ } \ if (*I++ != (char) ) { \ ERR = (message); \ ERR_code = init_I; \ run_err(I - 1); \ } \ }) #define scanword() \ ({ \ slice VAR = { 0, I }; \ while (isalnum(*I) || *I == '_') VAR.len++, I++; \ VAR; \ }) #define memslice slice __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. */ \ int 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; \ size_t str_len = 0; \ while (*I != '"' || T) { \ if (*I == '\\') { \ T = !T; \ if (!T && I[ 1 ] == '"') str_len--; \ } \ str_len++; \ I++; \ } \ I++; \ str_len++; \ int *dest_start = Data -= str_len; \ int y; \ for (y = 0; y < str_len - 1; y++) { dest_start[ y ] = str_start[ y ]; } \ Data[ -1 ] = 0; \ value = Data - m; \ } else { \ ERR = "expected value (number, character, string)"; \ run_err(I); \ } \ value; \ }) #define op(mode) emit(opcode.mode, 0) #define swap() emit(opcode.swp, 0) #define imm(value) emit(opcode.imm, value) #define load() emit(opcode.lod, 0) #define imm_in_mem(__mem, __imm) \ ({ \ imm(__imm); \ swap(); \ imm(__mem); \ store(); \ }) #define mov_mem_mem(__mem1, __mem2) \ ({ \ imm(__mem2); \ swap(); \ load(); \ swap(); \ imm(__mem1); \ store(); \ }) #define mem_in_a(__mem) \ ({ \ op(b2u); \ imm(__mem); \ swap(); \ load(); \ op(u2b); \ }) #define perform_op(operation) \ ({ \ mem_in_a(mem2_index); \ swap(); \ mem_in_a(mem_index); \ emit(opcode.operation, 0); \ swap(); \ imm(mem_index); \ store(); \ }) char T; string ERR; string ERR_code; int *Code = &m[ 4000 ]; int *Data = &m[ static_len(m) - 1 ]; // grows downwards _ir2(string I, entry macros[ 1 << 9 ]) { string init_I = I; ERR = "all good"; while (T = *I) { skip_space(&I); if (T == '/' && I[ 1 ] == '/') { // comment I += 2; while (*I != '\n' && *I != '\0') I++; } // handle assignments! if (T == '@') { I++; int mem_index = strtol(I, &I, 10); skip_space(&I); char extra_op = '='; if (*I == '+' || *I == '-' || *I == '&' || *I == '!') extra_op = *I++; checkskip('=', "expected assigment after memory blob (hint: only +-&! are supported)"); skip_space(&I); if (*I == '@') { I++; if (!isdigit(*I)) { ERR = "expected a digit after @"; run_err(I); } int mem2_index = strtol(I, &I, 10); if (extra_op == '=') { mov_mem_mem(mem_index, mem2_index); } else if (extra_op == '+') { perform_op(add); } else if (extra_op == '-') { perform_op(sub); } else if (extra_op == '&') { perform_op(and); } else if (extra_op == '!') { // @1 != @2; mem_in_a(mem2_index); emit(opcode.not, 0); swap(); imm(mem_index); store(); } } else { int imm = scanvalue(); if (extra_op == '=') { imm_in_mem(mem_index, imm); } else if (extra_op == '+') { imm(imm); swap(); mem_in_a(mem_index); emit(opcode.add, 0); store(); } else if (extra_op == '-') { imm(imm); swap(); mem_in_a(mem_index); emit(opcode.sub, 0); store(); } else if (extra_op == '&') { imm(imm); swap(); mem_in_a(mem_index); emit(opcode.and, 0); store(); } else if (extra_op == '!') { // @1 != 5; imm(imm); emit(opcode.not, 0); swap(); imm(mem_index); store(); } } skip_space(&I); checkskip(';', "expected semicolon"); continue; } // register macros if (strncmp("macro", I, 5) == 0 && isspace(I[ 5 ])) { I += 6; skip_space(&I); // parse macro stuff if (!isalpha(*I) && *I != '_') { ERR = "expected letter at macro name"; run_err(I - 1); } slice name = scanword(); skip_space(&I); checkskip('(', "expected open paren in macro defintion"); skip_space(&I); checkskip( ')', "expected close paren in macro definition (hint: macros do not take named " "arguments)"); skip_space(&I); checkskip('{', "expected open curly bracket in macro body"); // find open space in macros int x; for (x = 0; x < (1 << 9); x++) { entry g = macros[ x ]; if (g.key.ref == NULL) { goto DONE; } } ERR = "macro count exceeded 1<<9"; run_err(I - 1); DONE:; skip_space(&I); // might aswell skip whitespace for funsies. entry *g = ¯os[ x ]; g->key = name; g->value.ref = I; g->value.len = 0; int dep = 0; while (dep != 0 || *I != '}') { if (*I == '{') dep++; if (*I == '}') dep--; g->value.len++; I++; if (*I == 0) { checkskip('}', "expected close curly bracket in macro body (met EOF instead)"); } } I++; continue; } // emit exit calls if (strncmp("exit", I, 4) == 0) { I += 4; checkskip('(', "expected open paren on exit blob"); string old = I; int value = strtol(I, &I, 10); if (old == I && *I != ')') { ERR = "invalid input to exit blob, expected number"; run_err(I - 1); } skip_space(&I); checkskip(')', "expected open paren on exit blob"); skip_space(&I); checkskip(';', "expected semicolon after call"); // actually emit stuff if (old != I) emit(opcode.imm, value); emit(opcode.hlt, 0); continue; } // emit system if (strncmp("system", I, 6) == 0 && !isalnum(I[ 7 ])) { I += 6; skip_space(&I); checkskip('(', "expected open paren on system blob"); skip_space(&I); checkskip( ')', "expected closing paren on system blob (hint: system does not take any arguments)"); skip_space(&I); checkskip(';', "expected semicolon after system blob"); emit(opcode.imm, 2); emit(opcode.swp, 0); emit(opcode.imm, 0); store(); emit(opcode.hlt, 0); continue; } // macro call, maybe? if (isalpha(*I)) { slice name = scanword(); skip_space(&I); checkskip('(', "expected open paren after alphanumeric word (assuming this is a blob)"); skip_space(&I); slice arguments[ 1 << 7 ]; size_t arg_count = 0; while (*I != ')') { if (!*I) { ERR = "reached EOF whilst looking for closing paren on blob"; run_err(I); } checkskip('[', "expected bracket in blob argument (macro-type)"); arguments[ arg_count ].len = 0; arguments[ arg_count ].ref = I; int 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)"); } } skip_space(&I); checkskip(']', "expected closing bracket in macro arguments"); skip_space(&I); if (*I != ',' && *I != ')') { ERR = "expected comma or closing paren in blob"; run_err(I); } if (*I == ',') { I++; skip_space(&I); } arg_count++; } checkskip(')', "expected closing paren after blob arguments"); skip_space(&I); checkskip(';', "expected semicolon after blob"); // done reading blob! check it and parse macro :) // find index size_t i; for (i = 0; i < (1 << 9); i++) { entry cur = macros[ i ]; if (cur.key.len != name.len) continue; if (strncmp(name.ref, cur.key.ref, name.len) == 0) goto FOUND; } ERR = "could not find a macro by that name"; run_err(I); FOUND:; slice target = macros[ i ].value; string N = target.ref; string oldN = N; memslice new = { target.len, malloc(target.len) }; size_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; size_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 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, macros); continue; } } } ir2(string I) { entry macros[ 1 << 9 ]; _ir2(I, macros); } string op_names[] = { "hlt", "imm", "lod", "str", "swp", "add", "sub", "and", "not", "acc" }; unsigned long long rdtsc(void) { unsigned hi, lo; __asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi)); return ((unsigned long long) hi << 32) | lo; } double bench_cycle(int instr) { int i; for (i = 0; i < S - 2; i += 2) { m[ i ] = instr; m[ i + 1 ] = 5; } m[ S - 2 ] = 0; m[ S - 1 ] = 0; m[ 7 ] = 0; c = 0; clock_t start = clock(); pu2(); clock_t end = clock(); double seconds = (double) (end - start) / CLOCKS_PER_SEC; printf("time: %.6f s\n", seconds); return (double) m[ 7 ] / seconds / 1e6; } benchmark() { double results[ 13 ]; double sum = 0; for (i = 0; i < 13; i++) { results[ i ] = bench_cycle(i); sum += results[ i ]; } sum /= 13; unsigned long long start, end; start = rdtsc(); sleep(1); end = rdtsc(); float clock_speed = (end - start) / 1e6; printf("clock speed ~ %.6f MHz\n", clock_speed); printf("results sum: %f\n", sum); printf("instructions per cycle: %f\n", clock_speed / sum); } main() { size_t o_size; ir2(read_file("code.i2", &o_size)); m[ 7 ] = 4000; size_t res_size = Code - &m[ 4000 ]; // --- BYTES PRINTING --- // for(i = 0; i < res_size; i++) { // printf("%X%X ", m[4000+i]>>4, m[4000+i]&0b1111); // if(i % 8 == 7) printf("\n"); // } // fflush(stdout); // printf("\n\n"); // --- OPCODES PRINTING --- // for (i = 0; i < res_size; i++) { // int op = m[ 4000 + i++ ]; // int data = m[ 4000 + i ]; // printf("%s(%i);\n", op_names[ op ], data); // } // printf("\n\n"); emu(); printf("result: %i\n", m[ 10 ]); } main2(int argc, char **argv) { if (argc == 1) { return printf( "\nusage:\n" " %s \n\n", argv[ 0 ]); } char *filename = argv[ 1 ]; size_t file_size; char *contents = read_file(filename, &file_size); E = 0; K = &m[ 1024 ]; as2(contents); if (E != 0) { error_debug(E, contents, file_size); exit(1); } m[ 7 ] = 1024; emu(); }