Skip to content

Instantly share code, notes, and snippets.

@clausecker
Created April 7, 2015 16:00
Show Gist options
  • Select an option

  • Save clausecker/3fc4dfcdd7892bc184a1 to your computer and use it in GitHub Desktop.

Select an option

Save clausecker/3fc4dfcdd7892bc184a1 to your computer and use it in GitHub Desktop.
/* 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