#include #include #include #include #pragma clang diagnostic ignored "-Wreturn-type" #pragma clang diagnostic ignored "-Wc23-extensions" // clang-format off #define S 65536 #define H !--c? #define J while m[S],c,a;pu(){J((c=m[m[7]++]))H*m=m[m[7]++]:c--<9?!a?m[c]=*m:0:8>(c-=8)?*m=m[c]:!(c-=8)?m[m[1]]=*m:H*m=a:H a+=*m:H a-=*m:H a=!a:H a=0:0;} // clang-format on // a is acc // first 8 memory bytes are registers actually // c is pc, read from m[7] // --- opcodes --- // 0 = halt DONE // 1 = const in r0 DONE // 2...9 = r0 in rX (only if acc is zero) DONE // 10...17 = rX in r0 DONE // 18 = r0 in m[r1] DONE // 19 = acc in r0 DONE // 20 = acc += r0 DONE // 21 = acc -= r0 DONE // 22 = acc = !acc DONE // 23 = acc = 0 DONE // for ops 2..9 and 10..17 it basically works like this: 2 handles r0, 3 r1, 4 r2, etc. 10 handles // r0, 11 r1, 12 r2 and so on. // compiler! // the switch skips whitespace. then, the beautiful loop thingymajigy emits bytecode. // clang-format off #define W case #define Q goto M; char R[]={ #embed"t" };K;char*as(char*I,int*O){char*V=R;J((K=*I)){V=R;switch(K){W 32:W 10:W 9:I++;Q;W 35:J(*++I!=10);Q}strncmp("con ",I,4)?:({*O++=1,*O++=atoi(I+=4);J(*I>47&*I<58)I++;Q});J(*V)V+=3,(strncmp(V-3,I,3)?:({*O++=*V;I+=3;Q})),V+=2;return I;M:}return 0;} // clang-format on #undef Q #undef H #undef W #undef S #undef J #ifndef __WRAPPER // runs pu until exit code != 2, // that exit code is an interrupt for talking to the emulated hardware int emu() { while (1) { pu(); 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++ ]); write(STDOUT_FILENO, &m[ ptr ], size); } // 1: input character else if (int_code == 1) { m[ 2 ] = getchar(); } else { // unknown interrupt. what to do? } m[ 0 ] = 0; m[ 7 ]++; } } char R_backup[] = "hlt\0 ar1\2 ar2\3 ar3\4 ar4\5 ar5\6 ar6\7 ar7\x8 ar8\x9 r1a\xA r2a\xB r3a\xC " "r4a\xD r5a\xE r6a\xF r7a\20 r8a\21 mem\22 aca\23 add\24 sub\25 not\26 clr\27"; int main(int argc, char **argv) { if (access("t", F_OK)) { // patch the instruction table incase you removed it. printf("you've deleted t! i'll write it again for you.\n\n"); FILE *fp = fopen("t", "w+"); fwrite(R_backup, 1, sizeof(R_backup), fp); fclose(fp); memcpy(R, R_backup, sizeof(R_backup)); } if (argc == 1) { printf("Usage:\n %s ", argv[ 0 ]); exit(1); } // read code FILE *fp = fopen(argv[ 1 ], "r"); fseek(fp, 0, SEEK_END); int sz = ftell(fp); rewind(fp); char code[ sz + 1 ]; fread(code, 1, sz, fp); code[ sz ] = 0; fclose(fp); // set pc m[ 7 ] = 1024; // assemble char *fail_point = as(code, &m[ 1024 ]); if (fail_point != 0) { 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, "Assembly failed at line %d, column %d (position %d):\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, " "); for (i = line_start; i < line_end; i++) { fputc(code[ i ], stderr); } fprintf(stderr, "\n"); fprintf(stderr, " "); for (i = line_start; i < error_pos; i++) { fputc(' ', stderr); } fprintf(stderr, "^ parsing stopped here\n"); exit(1); } // run emulation emu(); } #endif