#include #include #include #define OC 0 #define OPS \ OP(addr) OP(addi) OP(mulr) OP(muli) \ OP(banr) OP(bani) OP(borr) OP(bori) \ OP(setr) OP(seti) OP(gtir) OP(gtri) \ OP(gtrr) OP(eqir) OP(eqri) OP(eqrr) #define OP(name) name, enum ops {OPS num_ops}; #undef OP #define OP(name) #name, static char *opnames[] = {OPS}; #undef OP union register_file { uint64_t all; int16_t r[4]; }; static inline union register_file execute(int op, int a, int b, int c, union register_file f) { switch (op) { case addr: f.r[c] = f.r[a] + f.r[b]; break; case addi: f.r[c] = f.r[a] + b; break; case mulr: f.r[c] = f.r[a] * f.r[b]; break; case muli: f.r[c] = f.r[a] * b; break; case banr: f.r[c] = f.r[a] & f.r[b]; break; case bani: f.r[c] = f.r[a] & b; break; case borr: f.r[c] = f.r[a] | f.r[b]; break; case bori: f.r[c] = f.r[a] | b; break; case setr: f.r[c] = f.r[a]; break; case seti: f.r[c] = a; break; case gtir: f.r[c] = a > f.r[b]; break; case gtri: f.r[c] = f.r[a] > b; break; case gtrr: f.r[c] = f.r[a] > f.r[b]; break; case eqir: f.r[c] = a == f.r[b]; break; case eqri: f.r[c] = f.r[a] == b; break; case eqrr: f.r[c] = f.r[a] == f.r[b]; break; default: ; } return f; } #define IO_ERROR 3 #define NEED_MORE_SAMPLES 2 #define OPCODE_TOO_BIG 1 int main(int argc, char *argv[]) { static char decode[16]; static int possibilities[sizeof decode]; FILE *input; int error = IO_ERROR; for (int i = 0; i < sizeof decode; i++) { possibilities[i] = (1 << sizeof decode) - 1; } if (input = fopen(argc > 1? argv[1]: "input.txt", "r")) { int i, j, k, l, m, n, o, p, q, r, s, t; error = 0; while (fscanf(input, "Before: [%d, %d, %d, %d] " "%d %d %d %d " "After: [%d, %d, %d, %d] ", &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t) == 12 ) if (m < 0 || m >= sizeof decode) { error = OPCODE_TOO_BIG; break; } else { union register_file b, a; int opbit = 1; b.r[0] = i; b.r[1] = j; b.r[2] = k; b.r[3] = l; a.r[0] = q; a.r[1] = r; a.r[2] = s; a.r[3] = t; if (OC) printf( "Before: [%d, %d, %d, %d]\n" "After: [%d, %d, %d, %d]\n", i, j, k, l, q, r, s, t ); for (int op = 0, opbit = 1; op < num_ops; op++, opbit <<= 1) { union register_file d = execute(op, n, o, p, b); if (d.all != a.all) possibilities[m] &= ~opbit; if (OC) printf( "%s %d %d %d: [%d, %d, %d, %d]%s\n", opnames[op], n, o, p, d.r[0], d.r[1], d.r[2], d.r[3], " *" + 2*(d.all != a.all) ); } if (OC) putchar('\n'); } if (!error) { int mask = (1 << sizeof decode) - 1; goto enter; do { /* Comb through possibilities, extracting opcodes * that have just one possibility bit set. */ error = NEED_MORE_SAMPLES; for (int i = 0; i < sizeof decode; i++) { int entry = possibilities[i] & mask; if (entry && !(entry & entry - 1)) { /* Exactly one bit was set. Claim * this possibility and exclude it. */ decode[i] = __builtin_ctz(entry); mask &= ~entry; error = 0; } } enter: if (0) { printf("%08x\n\n", mask); for (int i = 0; i < sizeof decode; i++) { printf("%08x %s\n", possibilities[i] & mask, possibilities[i] & mask? "": opnames[decode[i]] ); } putchar('\n'); } } while (mask && !error); } if (!error) { int opcode, a, b, c; union register_file f = {0}; while (fscanf(input, "%d %d %d %d ", &opcode, &a, &b, &c) == 4) { int op; f = execute(op = decode[opcode], a, b, c, f); if (OC) printf( "%s %d %d %d: [%3d, %3d, %3d, %3d]\n", opnames[op], a, b, c, f.r[0], f.r[1], f.r[2], f.r[3] ); } printf("%d\n", f.r[0]); } fclose(input); } return error; }