diff options
| author | 2025-08-11 11:34:18 +0330 | |
|---|---|---|
| committer | 2025-08-11 11:34:18 +0330 | |
| commit | 2fc20ad0b11e60ecfacc653024f2b582acad6e86 (patch) | |
| tree | d7e3f793f32928e40a18c56d00875a5b8e74dc62 /pu2/pu2.c | |
| parent | vscode theme extension (diff) | |
ir2 initial release!
Diffstat (limited to 'pu2/pu2.c')
| -rw-r--r-- | pu2/pu2.c | 547 |
1 files changed, 547 insertions, 0 deletions
diff --git a/pu2/pu2.c b/pu2/pu2.c new file mode 100644 index 0000000..66794bc --- /dev/null +++ b/pu2/pu2.c @@ -0,0 +1,547 @@ +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#pragma clang diagnostic ignored "-Wparentheses" +#pragma clang diagnostic ignored "-Wreturn-type" +#pragma clang diagnostic ignored "-Wempty-body" + +// pu2! +// maybe compacter? idk +// last cpu: 167 chars + +// NOTE: if you want the cpu to +// crash or do something when an invalid opcode is met, +// replace the `8` in the code with whatever behavior you desire. +// you can use ({...}) to insert normal code, or use __builtin_trap() for a SIGTRAP + +// clang-format off +#define S 1<<20 +#define H !--c +#define J while +m[S],c,v,a,b,u;pu2(){J(c=m[m[7]++],v=m[m[7]++],c){H?a=v:H?a=m[b]:H?u?m[a]=b:0:H?v=a,a=b,b=v:H?a+=b:H?a-=b:H?a=a&&b:H?a=!a:H?u=1:8;u=v;}} +// clang-format on + +// there's just memory. no, really +// ok actually there's two registers called a and b +// 0 HLT: halt +// 1 IMM: a = imm +// 2 LOD: a = m[b] +// 3 STR: m[a] = b (only happens if acc != 0) +// 4 SWP: swap a and b +// 5 ADD: a += b +// 6 SUB: a -= b +// 7 AND: a &&= b +// 8 NOT: a = !a +// 9 ACC: u = imm + +// NOTE: Memory indexes less than 1024 should be considered as temporary/compute memory, and +// programs should be written at index 1024 and upwards. +// +// NOTE: Memory index 7 is the program +// counter, in which the current instruction is read from m[m[7]] at each cycle + +// ------------------------- + +// yeehaw baby it's time for as2 +// E is the error code loc. zero it before calling as2 +// Y is macro storage. extend as needed +// set O for output before running. keep in mind, it gets modified whilst as2 is running. +// therefore, i recommend you store a copy somewhere! + +// clang-format off +char*C="hltimmlodstrswpaddsubandnotacc",*Y[1<<10],*E,*L,i;*O;as2(char*I){J(c=*I++){if(c==35){J(*++I-10);c=*I++;}L=C;(!isspace(c)?c-39?c-125?c-123?c-64?({i=0,I--;J(i++,*L)strncmp(L,I,3)?L+=3:({I+=3;*O++=i-1;*O++=i-2?0:strtol(I,&I,10);goto Q;});E=I;return 1;Q:;}):as2(Y[strtol(I,&I,10)]):({Y[strtol(I,&I,10)]=I+1;J(*I++-125);}):({break;}):({*O++=1;*O++=*I++;}):0);}} +// clang-format on + +#undef S +#undef H +#undef J + +#ifndef __WRAPPER + +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"); +} + +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; + + for (; i < size; i++) putchar(m[ ptr++ ]); + while (size--) putchar(m[ ptr++ ]); + } + + // 1: input character + else if (int_code == 1) { + m[ 2 ] = getchar(); + } + + 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; int imm; int lod; int str; int swp; int add; int sub; int and; int not; int acc; +} opcode = { + 0, 1, 2, 3, 4, 5, 6, 7, 8,9 +}; + +#define emit(OP, VAL) ({ *(*O)++ = (OP); *(*O)++ = (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)) VAR.len++, I++; VAR; }) +#define memslice slice __attribute__((cleanup(clean_slice_ref))) +#define readd() ({ if(used == new.len) new.ref = realloc(new.ref, new.len *= 2); }) +// 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); } + +T; +string ERR; +string ERR_code; +_ir2(string I, int **O, 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++; + } + + // handle assignments! + if (T == '@') { + I++; + int mem_index = strtol(I, &I, 10); + skip_space(&I); + checkskip('=', "expected assigment after memory blob"); + skip_space(&I); + + if (*I == '@') { + I++; + if (!isdigit(*I)) { + ERR = "expected a digit after @"; + run_err(I); + } + + int mem2_index = strtol(I, &I, 10); + + emit(opcode.imm, mem2_index); + emit(opcode.swp, 0); + emit(opcode.lod, 0); + emit(opcode.swp, 0); + emit(opcode.imm, mem_index); + store(); + } else if (isdigit(*I) || *I == '\'') { + int imm; + if (*I == '\'') { + I++; + imm = *I++; + checkskip('\'', "couldn't find closing quote on character"); + } else { + imm = strtol(I, &I, 10); + } + + emit(opcode.imm, imm); + emit(opcode.swp, 0); + emit(opcode.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)) { + 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"); + + _ir2("@0 = 2; exit();", O, macros); + + 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, O, macros); + + continue; + } + } +} + +ir2(string I, int **O) { + entry macros[ 1 << 9 ]; + _ir2(I, O, macros); +} + +main() { + int *out = &m[ 1024 ]; + size_t o_size; + ir2(read_file("code.i2", &o_size), &out); + m[ 7 ] = 1024; + + // size_t res_size = out - &m[ 1024 ]; + // for(i = 0; i < res_size; i++) { + // printf("%i ", m[1024+i]); + // } + // fflush(stdout); + // printf("\n\n"); + + emu(); +} + +main2(int argc, char **argv) { + if (argc == 1) { + return printf( + "\nusage:\n" + " %s <file_name>\n\n", + argv[ 0 ]); + } + + char *filename = argv[ 1 ]; + size_t file_size; + char *contents = read_file(filename, &file_size); + + E = 0; + O = &m[ 1024 ]; + as2(contents); + + if (E != 0) { + error_debug(E, contents, file_size); + exit(1); + } + + m[ 7 ] = 1024; + emu(); +} + +#endif
\ No newline at end of file |
