/* Fast zoltraak interpretor. Reads from stdin and writes to stdout. This tool runs much faster than run_zoltraak.pl because it's written in C, and also because it uses a faster copy scheme to implement repeat codes. In exchange for the faster copy scheme, it doesn't check for stack overflow. So handwritten zoltraak code that are accepted by this tool might see runtime stack overflow errors when compiled. Code generated by zoltraak18.c and later are guaranteed not to overflow runtime stack. */ #include #include #include #include #define DATA_BUFFER_SIZE 0x10000 #define CODE_BUFFER_SIZE 0x200 #define XOR_KEY 0x20 /* Program states. */ typedef struct { /* Ring buffer of recently printed bytes, indexed by output_size. */ uint8_t data[DATA_BUFFER_SIZE]; /* Ring buffer of output sizes, indexed by code_size. This is used to translate from the code offsets used by the repeat commands into data offsets. */ int offset[CODE_BUFFER_SIZE]; /* Number of bytes printed. */ int output_size; /* Number of instruction units executed. Output counts as 1 unit, repeat counts as 2 units. */ int code_size; } ProgramState; /* Output a single byte. */ static void OutputByte(ProgramState *state, uint8_t byte) { putchar(byte); state->offset[state->code_size++ % CODE_BUFFER_SIZE] = state->output_size; state->data[state->output_size++ % DATA_BUFFER_SIZE] = byte; } /* Output repeated bytes. */ static void Repeat(ProgramState *state, int length, int offset) { int source_offset = state->offset[(state->code_size - offset) % CODE_BUFFER_SIZE]; state->offset[state->code_size % CODE_BUFFER_SIZE] = state->output_size; state->offset[(state->code_size + 1) % CODE_BUFFER_SIZE] = -1; for(; length > 0; length--) { const uint8_t byte = state->data[source_offset++ % DATA_BUFFER_SIZE]; putchar(byte); state->data[state->output_size++ % DATA_BUFFER_SIZE] = byte; } state->code_size += 2; } /* Interpret input lines. Returns 0 on success. */ static int Run(void) { char input_line[128]; int inside_header = 0; int bits = 0; int lines = 0; ProgramState *state = calloc(1, sizeof(ProgramState)); if( state == NULL ) { fputs("Out of memory\n", stderr); return 1; } while( fgets(input_line, sizeof(input_line), stdin) != NULL ) { if( inside_header ) { if( strstr(input_line, "#endif") != NULL ) inside_header = 0; continue; } else { if( strstr(input_line, "#ifndef") != NULL ) { inside_header = 1; continue; } } /* Load a single instruction bit. */ if( input_line[0] == 'z' || input_line[0] == 's' ) bits |= 1 << lines; lines++; /* Execute instruction once we got enough bits. */ if( lines == 9 ) { if( (bits & 0x100) == 0 ) continue; OutputByte(state, (bits & 0xff) ^ XOR_KEY); lines = bits = 0; } else if( lines == 18 ) { if( (bits & 0x20000) == 0 ) { fputs("Incomplete branch instruction\n", stderr); free(state); return 1; } const int length = (bits & 0xff) + 3; const int offset = ((bits >> 9) & 0xff) + 1; if( state->code_size - offset < 0 ) { fputs("Branch out of bounds\n", stderr); return 1; } if( state->offset[(state->code_size - offset) % CODE_BUFFER_SIZE] < 0 ) { fputs("Branch into incomplete instruction\n", stderr); free(state); return 1; } Repeat(state, length, offset); lines = bits = 0; } } free(state); return 0; } int main(int argc, char **argv) { return Run(); }