#include <stdint.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>

uint32_t finger = 0;
uint32_t regs[8] = {0, 0, 0, 0, 0, 0, 0, 0};

uint32_t *code;

#define ARR(x) (x ? (uint32_t *)x : code)

inline void execOne () {
    uint32_t errand = code[finger++];
    
    int a = (errand >> 6) & 7;
    int b = (errand >> 3) & 7;
    int c = (errand >> 0) & 7;

    switch ((errand >> 28) & 0xF) {

    case 0: // conditional move
	if (regs[c])
	    regs[a] = regs[b];
	break;
	
	
    case 1: // array index
	regs[a] = ARR(regs[b])[regs[c]];
	break;
	
	
    case 2: // array amendmnet
	ARR(regs[a])[regs[b]] = regs[c];
	break;

    case 3: // addition
	regs[a] = regs[b] + regs[c];
	break;

    case 4: // multiplication
	regs[a] = regs[b] * regs[c];
	break;

    case 5: // division
	regs[a] = regs[b] / regs[c];
	break;
	
    case 6: // not-and
	regs[a] = ~(regs[b] & regs[c]);
	break;

    case 7: // halt
	exit(0);
	break;
	
    case 8: // allocation
    {
	uint32_t *p = calloc(regs[c]+1, sizeof(uint32_t));
	*p = regs[c];
	regs[b] = (uint32_t)(p+1);
	break;
    }

    case 9: // abandonment
	free(ARR(regs[c])-1);
	break;

    case 10: // output
	putchar(regs[c]);
	break;
	
    case 11: // input
	regs[c] = getchar();
	break;
	
    case 12: // load program
	if (regs[b]) {
	    uint32_t size = ARR(regs[b])[-1];
	    uint32_t *p = malloc((size+1)*sizeof(uint32_t));
	    *p = size;
	    free(code-1);
	    code = p+1;
	    memcpy(code, ARR(regs[b]), size*sizeof(uint32_t));
	}
	finger = regs[c];
	break;

    case 13:
    {
	int a   = (errand >> 25) & 7;
	int val = errand & ((1 << 25) - 1);
	regs[a] = val;
	break;
    }

    default:
	fprintf(stderr, "Ouch! bad opcode in errand %08x at %08x\n",
		errand, finger);
    }
}

uint32_t *read_scroll(char *path) {
    uint32_t size;
    FILE *fp = fopen(path, "rb");
    fseek(fp, 0, SEEK_END);
    size = ftell(fp);

    uint32_t *ret = malloc(size+sizeof(uint32_t));
    *ret = size/sizeof(uint32_t);
    
    fseek(fp, 0, SEEK_SET);
    fread(ret+1, 1, size, fp);
    int i;
    for (i = 1; i <= size/sizeof(uint32_t); i++) {
	ret[i] = ntohl(ret[i]); 
    }

    return ret;
}

main (int argc, char *argv[argc]) {
    code = 1+read_scroll(argv[1]);
    for(;;) execOne();
}
