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

int is_LE(){
  union{struct{uint8_t a;uint8_t c;uint8_t b;uint8_t d;} _8; uint32_t _32;} x;
  x._32 = 0x1234567;
  return x._8.a == 0x67;
}

int main(int argc, char **argv) {
  FILE *f= fopen(argv[1], "r");
  uint32_t i,sz,*PC,I,*Z,R[8]={0,0,0,0,0,0,0,0};
  fseek(f,0,SEEK_END),sz = ftell(f), fseek(f,0,SEEK_SET);
  PC = (Z = (uint32_t*) malloc(sz));
  fread(Z, 1, sz, f);
  fclose(f);
  if(is_LE())
    for(i = 0;i < sz/4;i++)
      Z[i]=(Z[i]&0xFF)<<24|(Z[i]&0xFF00)<<8
        |(Z[i]&0xFF0000)>>8|(Z[i]&0xFF000000)>>24;
  while(1){
    uint32_t A,B,C;
    I = *PC++;
    A = (I>>6)&7;
    B = (I>>3)&7;
    C = I&7;
    switch (I >> 28) {
    case 0:
      if(R[C])
	R[A] = R[B];
      break;
    case 1: /* Get */
      if(R[B])
	R[A] = ((uint32_t*)R[B])[R[C]];
      else
	R[A] = Z[R[C]];
      break;
    case 2: /* Set */
      if(R[A])
	((uint32_t*)R[A])[R[B]] = R[C];
      else
	Z[R[B]] = R[C];
      break;
    case 3: /* + */
      R[A] = R[B] + R[C];
      break;
    case 4: /* * */
      R[A] = R[B] * R[C];
      break;
    case 5: /* / */
      R[A] = R[B] / R[C];
      break;
    case 6: /* nand */
      R[A] = ~(R[B] & R[C]);
      break;
    case 7: /* Stop */
      return 0;
    case 8: /* Alloc */ {
      uint32_t *ar = (uint32_t*)calloc((R[C]+1),sizeof(uint32_t));
      ar[0] = R[C];
      R[B] = (uint32_t) &ar[1];
      break; }
    case 9: /* Free */
      free(&((uint32_t*)R[C])[-1]);
      break;
    case 10: /* Output */
      putchar(R[C]);
      break;
    case 11: /* Input */
      if (EOF == (R[C] = getchar()))
	R[C] = 0xFFFFFFFF;
      break;
    case 12: /* Load */ {
      if(R[B] != 0) {
	uint32_t *ar = (uint32_t*)R[B],*new;
	new = (uint32_t*)malloc((ar[-1]+1)*sizeof(uint32_t));
	memcpy(new,&ar[-1],(ar[-1]+1)*sizeof(uint32_t));
	Z = &new[1]; }
      PC = Z + R[C];
      break; }
    case 13: /* Imm */
      R[(I>>25)&7] = I&((1<<25)-1);
      break;
    }
  }
}
