You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
163 lines
3.1 KiB
163 lines
3.1 KiB
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
|
|
#include <attrs.h>
|
|
#include <panic.h>
|
|
|
|
const char* const input[] = {
|
|
#ifdef TEST
|
|
#include "input-test.h"
|
|
#else
|
|
#include "input.h"
|
|
#endif
|
|
};
|
|
|
|
#define input_sz (sizeof(input)/sizeof(char*))
|
|
|
|
enum op {
|
|
OP_NOP=0,
|
|
OP_ACC,
|
|
OP_JMP,
|
|
OP_HLT,
|
|
};
|
|
|
|
inline static const char* strop(enum op op)
|
|
{
|
|
switch(op) {
|
|
case OP_NOP: return "nop";
|
|
case OP_ACC: return "acc";
|
|
case OP_JMP: return "jmp";
|
|
case OP_HLT: return "hlt";
|
|
}
|
|
panic("Unknown op %d", op);
|
|
}
|
|
|
|
enum exec_stat {
|
|
END = 0,
|
|
HALT,
|
|
};
|
|
|
|
typedef struct instr {
|
|
enum op operator;
|
|
int64_t operand;
|
|
} instruction_t;
|
|
|
|
struct reg {
|
|
ssize_t pc;
|
|
int64_t acc;
|
|
int pjmp[input_sz];
|
|
};
|
|
|
|
|
|
static pure instruction_t parse_single(const char* str)
|
|
{
|
|
register instruction_t op;
|
|
switch(str[0]) {
|
|
case 'n': op.operator = OP_NOP; break;
|
|
case 'a': op.operator = OP_ACC; break;
|
|
case 'j': op.operator = OP_JMP; break;
|
|
default: panic("Unknown instr op: %s", str);
|
|
}
|
|
op.operand = (int64_t)atoll(str+4);
|
|
return op;
|
|
}
|
|
|
|
enum exec_stat execute_destructive(instruction_t* restrict prog, struct reg* restrict reg, size_t len);
|
|
|
|
enum exec_stat execute(const instruction_t* prog, struct reg* restrict reg)
|
|
{
|
|
instruction_t dest[input_sz];
|
|
memcpy(dest, prog, input_sz * sizeof(prog[0]));
|
|
reg->pc = 0;
|
|
reg->acc =0;
|
|
return execute_destructive(dest, reg, input_sz);
|
|
}
|
|
enum exec_stat execute_destructive(instruction_t* restrict prog, struct reg* restrict reg, size_t len)
|
|
{
|
|
for(;(size_t)reg->pc<len;)
|
|
{
|
|
if(reg->pc < 0) panic("Invalid pc %ld", reg->pc);
|
|
|
|
instruction_t* restrict current = &prog[reg->pc];
|
|
enum op op = current->operator;
|
|
current->operator = OP_HLT;
|
|
switch(op)
|
|
{
|
|
case OP_HLT: return HALT;
|
|
case OP_JMP:
|
|
reg->pc += (ssize_t)current->operand;
|
|
continue;
|
|
case OP_ACC: reg->acc += current->operand; break;
|
|
default: break;
|
|
}
|
|
reg->pc += 1;
|
|
}
|
|
|
|
return END;
|
|
}
|
|
|
|
void swap(size_t* restrict s1, size_t* restrict s2)
|
|
{
|
|
*s1 = *s1 ^ *s2;
|
|
*s2 = *s1 ^ *s2;
|
|
*s1 = *s1 ^ *s2;
|
|
}
|
|
|
|
void reverse(size_t* restrict ar, size_t len)
|
|
{
|
|
size_t i=0,j=len-1;
|
|
if(j==1) return;
|
|
while(i<j) {
|
|
|
|
swap(ar+i, ar+j);
|
|
i++; j--;
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
instruction_t instr[input_sz];
|
|
size_t jumps[input_sz]; size_t nj =0;
|
|
size_t nops[input_sz]; size_t ni=0;
|
|
for (size_t i=0;i<input_sz;i++) {
|
|
switch((instr[i] = parse_single(input[i])).operator)
|
|
{
|
|
case OP_NOP: nops[ni++] = i; break;
|
|
case OP_JMP: jumps[nj++] = i; break;
|
|
default: break;
|
|
}
|
|
}
|
|
reverse(jumps, nj);
|
|
reverse(nops, ni);
|
|
|
|
struct reg state = {0};
|
|
#ifdef PART2
|
|
register int seti=0,setj=0;
|
|
while (execute(instr, &state)) {
|
|
if(seti)instr[nops [ni]].operator = OP_NOP;
|
|
if(setj)instr[jumps[nj]].operator = OP_JMP;
|
|
|
|
seti=setj=0;
|
|
if(ni) {
|
|
instr[nops [--ni]].operator = OP_JMP;
|
|
seti=1;
|
|
}
|
|
else if(nj) {
|
|
instr[jumps[--nj]].operator = OP_NOP;
|
|
setj=1;
|
|
}
|
|
else panic("All inputs halt: %ld", state.acc);
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "Retry: %ld %ld\n", state.pc, state.acc);
|
|
#endif
|
|
}
|
|
#else
|
|
enum exec_stat res = execute_destructive(instr, &state, input_sz);
|
|
if(!res) panic("No halt");
|
|
#endif
|
|
printf("%ld\n", state.acc);
|
|
return 0;
|
|
}
|
|
|