Created
April 7, 2015 16:00
-
-
Save clausecker/3fc4dfcdd7892bc184a1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* http://codegolf.stackexchange.com/q/48528/134 */ | |
| /* by Robert Clausecker (FUZxxl) */ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <limits.h> | |
| #include <stdint.h> | |
| enum opcode { HALT, PRINTX, INCX, INCY, JMP, DJZX, DJZY }; | |
| const char *mnemonics[7] = { | |
| "HALT", "PRINTX", "INCX", "INCY", "JMP", "DJZX", "DJZY" | |
| }; | |
| struct instr { | |
| unsigned int opcode : 3; | |
| size_t operand : sizeof (size_t) * CHAR_BIT - 3; | |
| }; | |
| static size_t labels[UCHAR_MAX]; | |
| static struct instr program[16384 / sizeof (struct instr)]; | |
| static int debug; | |
| /* | |
| * code format: | |
| * one column label (space if no label) | |
| * one column opcode: H (HALT), X (INCX / DJZX), Y (INCY / DJZY), | |
| * J (JMP), P (PRINTX). | |
| * optional one column operand (distinguishing INCX from DJZX, etc). | |
| * newline | |
| */ | |
| /* returns program length in instructions */ | |
| static size_t | |
| assemble(void) | |
| { | |
| int label, opchar; | |
| size_t ip = 0, i; | |
| /* catch unreferences labels */ | |
| for (i = 0; i < UCHAR_MAX; i++) | |
| labels[i] = -1; | |
| for (;; ip++) { | |
| /* read label */ | |
| if ((label = getchar()) == EOF) | |
| break; | |
| if (label != ' ') | |
| labels[(unsigned char)label] = ip; | |
| /* read opcode */ | |
| switch (opchar = getchar()) { | |
| case EOF: | |
| fprintf(stderr, "syntax error on line %zu: opcode expected\n", ip); | |
| exit(EXIT_FAILURE); | |
| case 'H': program[ip].opcode = HALT; break; | |
| case 'X': program[ip].opcode = INCX; break; | |
| case 'Y': program[ip].opcode = INCY; break; | |
| case 'J': program[ip].opcode = JMP; break; | |
| case 'P': program[ip].opcode = PRINTX; break; | |
| default: | |
| fprintf(stderr, "syntax error on line %zu: invalid opcode %c\n", ip, opchar); | |
| exit(EXIT_FAILURE); | |
| } | |
| /* read operand if present */ | |
| if ((opchar = getchar()) == EOF) { | |
| fprintf(stderr, "syntax error on line %zu: unexpected end of file\n", ip); | |
| exit(EXIT_FAILURE); | |
| } | |
| if (opchar == '\n') { | |
| if (program[ip].opcode <= INCY) | |
| continue; | |
| fprintf(stderr, "syntax error on line %zu: operand expected\n", ip); | |
| exit(EXIT_FAILURE); | |
| } | |
| if (program[ip].opcode <= PRINTX) { | |
| fprintf(stderr, "syntax error on line %zu: unexpected operand\n", ip); | |
| exit(EXIT_FAILURE); | |
| } | |
| /* adjust INCX and INCY */ | |
| if (program[ip].opcode <= INCY) | |
| program[ip].opcode += 3; | |
| program[ip].operand = (size_t)opchar; | |
| /* read newline */ | |
| if ((opchar = getchar()) == EOF) { | |
| fprintf(stderr, "syntax error on line %zu: unexpected end of file\n", ip); | |
| exit(EXIT_FAILURE); | |
| } | |
| if (opchar != '\n') { | |
| fprintf(stderr, "syntax error on line %zu: newline expected\n", ip); | |
| } | |
| } | |
| /* adjust labels */ | |
| for (i = 0; i < ip; i++) { | |
| if (program[i].operand == '\0') | |
| continue; | |
| if (labels[program[i].operand] == (size_t)-1) { | |
| fprintf(stderr, "reference to undefined label %c on line %zu\n", (int)program[i].operand, i); | |
| exit(EXIT_FAILURE); | |
| } | |
| program[i].operand = labels[program[i].operand]; | |
| } | |
| return ip; | |
| } | |
| /* print out a program listing for debugging */ | |
| static void | |
| disassemble(size_t ip, struct instr instr) | |
| { | |
| fprintf(stderr, "%6zu ", ip); | |
| if (instr.opcode <= INCY) | |
| fprintf(stderr, "%s\n", mnemonics[instr.opcode]); | |
| else | |
| fprintf(stderr, "%-6s %6zu\n", mnemonics[instr.opcode], (size_t)instr.operand); | |
| } | |
| /* interprete the program */ | |
| static void | |
| interpret(uintmax_t x, uintmax_t y) | |
| { | |
| size_t ip = 0; | |
| for (;; ip++) { | |
| decode: | |
| if (debug) { | |
| fprintf(stderr, "%6jd %6jd ", x, y); | |
| disassemble(ip, program[ip]); | |
| } | |
| switch (program[ip].opcode) { | |
| case HALT: | |
| return; | |
| case PRINTX: | |
| printf("%ju\n", x); | |
| break; | |
| case INCX: | |
| x++; | |
| break; | |
| case INCY: | |
| y++; | |
| break; | |
| case JMP: | |
| ip = program[ip].operand; | |
| goto decode; | |
| case DJZX: | |
| if (x == 0) { | |
| ip = program[ip].operand; | |
| goto decode; | |
| } | |
| x--; | |
| break; | |
| case DJZY: | |
| if (y == 0) { | |
| ip = program[ip].operand; | |
| goto decode; | |
| } | |
| y--; | |
| break; | |
| default: | |
| fputs("decoding error", stderr); | |
| exit(EXIT_FAILURE); | |
| } | |
| } | |
| } | |
| extern int | |
| main(int argc, char *argv[]) | |
| { | |
| uintmax_t x, y = 0; | |
| size_t plen, i; | |
| if (argc != 2) { | |
| fprintf(stderr, "Usage: %s x\n", argv[0]); | |
| return EXIT_FAILURE; | |
| } | |
| debug = getenv("DEBUG") != NULL; | |
| plen = assemble(); | |
| x = (uintmax_t)atol(argv[1]); | |
| if (debug) { | |
| fprintf(stderr, "%zu instructions loaded\n", plen); | |
| for (i = 0; i < plen; i++) | |
| disassemble(i, program[i]); | |
| } | |
| interpret(x, y); | |
| return EXIT_SUCCESS; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment