/* Copyright (C) 3L Ltd. 1987-1991 */

/* I-code decoder.   ADC, 1-Jan-89  */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

/* Headers for idc.c */
extern void setmap(void);
extern int main(int argc, char **argv);
extern void list(int upto);
// extern void error(char *fmt, int a, int b, int c, int d, int e);
extern void error(char *fmt, ...);
extern void decode(int code);
extern void hex_bytes(size_t b);
extern void handle(int argument);
extern int getword(void);
extern int gettag(void);
extern int getbyte(void);
extern char *getstr(char *buf);
extern char *getstring(char *buf, int terminator);
/* End of headers for idc.c */

#include <limits.h>
//#define MAXINT  ((int)(((unsigned)(-1)<<1) >> 1))) /* most positive 2's complement int */
#define MAXLINE 132 /* longest acceptable source line size */

static long last_start = 0L;

struct iop {
  char code;
  char *name;
  char *args; /* args string lists argument types, or 0 for 'none'.
                        Valid types are:
                           'w':  32-bit value
                           'p':  32-bit hex pattern
                           't':  16-bit tag
                           'b':  8-bit value
                           's':  string, preceded by length count byte
                           ',':  string, terminated by ','
                           ';':  string, terminated by ';'
                           'z':  sizeof operand: handled specially
                           'D':  special for complicated 'define-var' args
                           '@':  real-constant arg
                           'x':  hex bytes, preceded by length count byte
                           'X':  hex bytes, preceded by length 32-bit word
                           'i':  an I-code (normally one of beq,bgt,bne etc.)
                           '?':  peculiar argument type handled specially
                    */
} icodes[] = {

    '$', "define-var", "t,D",
    'b', "constant-bounds", 0,
    'd', "dimension", "tt",
    '@', "stack-var", "t",
    ';', "end", 0,
    '{', "{", 0,
    '}', "}", 0,
    '~', "~", "b",
128+',', "128+','", 0,
   '\'', "string", "s",
    'D', "real-constant", "@",
    '!', "or", 0,
    '%', "xor", 0,
    '&', "and", 0,
    '+', "add", 0,
    '-', "sub", 0,
    '*', "mul", 0,
    'Q', "rdiv", 0,
    '/', "div", 0,
    '[', "lsh", 0,
    ']', "rsh", 0,
    '.', "conc", 0,
    'X', "exp", 0,
    'x', "rexp", 0,
128+'|', "urem", 0,
128+'N', "rem", 0,
128+'/', "udiv", 0,
    'U', "neg", 0,
   '\\', "not", 0,
    'v', "abs", 0,
128+'X', "float", 0,
128+'T', "trunc", 0,
128+'?', "unsigned-compare-values", 0,
    '?', "compare-values", 0,
    'C', "compare-addresses", 0,
    '"', "compare-double", 0,
128+'"', "unsigned-stack-condition", "i",
128+'C', "stack-condition", "i",
    '=', "beq", "t",
    '#', "bne", "t",
    '>', "bgt", "t",
    '<', "blt", "t",
    '(', "ble", "t",
    ')', "bge", "t",
    ':', "label", "t",
    'B', "branch-back", "t",
    'F', "branch-forward", "t",
    'J', "user-jump", "t",
    'L', "user-label", "t",
    '_', "switch-label", "t",
    'W', "switch-jump", "t",
    'f', "for", "t",
    'e', "signal", "t",
    'o', "event-trap", "t",
    'm', "monitor", 0,
    'K', "return-false", 0,
    'M', "return-address", 0,
    'R', "return", 0,
    'T', "return-true", 0,
    'V', "return-value", 0,
    'a', "array-access", 0,
    'i', "array-index", 0,
    'S', "assign-value", 0,
    'Z', "assign-address", 0,
    'j', "jam-transfer", 0,
    'p', "assign-parameter", 0,
    'A', "init", "t",
    'E', "call", 0,
    'G', "alias", "s",
    'H', "begin", 0,
    'I', "icode-stream-select", 0,
    'N', "lit", "w",
128+'b', "byte", "b",
    'O', "line", "t",
    'l', "language-flags", "t",
    'n', "select", "t",
    'u', "adjust", 0,
    'w', "machine-code", ";",
    'q', "include", "s",
    'y', "diagnose", "t",
128+'o', "owntype", "t",
128+'A', "amap", 0,
128+'V', "vmap", "t",
128+'p', "fmap", "t",
128+'F', "format", "t",
128+'E', "eval", 0,
128+'e', "eval-address", 0,
128+'z', "sizeof", "z",
128+'S', "swap", 0,
128+'D', "dup", 0,
128+'P', "pop", 0,
128+'g', "C-call", 0,
    't', "bt", "t",
    'k', "bf", "t",
    'r', "resolve", "t",

/* Pascal-specific ones */

    'g', "test-nil", 0,
    '^', "set-format", 0,
128+'3', "set-variant-count", "t",
128+'4', "test-variant-count", "t",
128+'5', "check-not-long-new", 0,
128+'6', "check-dynamic-bounds", 0,
128+'R', "define-range", "t",
128+'f', "for-range", "t",
128+'r', "test-range", "t",
128+'s', "size-of", 0,
128+'t', "type-of", 0,
128+'{', "alternate", 0,
128+'}', "end-alternate", 0,
128+'[', "claim-with", "t",
128+']', "release-with", "t",
128+'U', "use-with", "t",
128+'B', "no-op", 0,
128+'c', "general-compare", 0,
128+'m', "general-move", 0,
128+'L', "localise", 0,
128+'O', "round", 0,
128+'M', "mod", 0,
128+'Z', "push-[]", 0,
128+'1', "add-member", 0,
128+'2', "add-range", 0,
128+'i', "in-set?", 0,
128+'I', "in-set", 0,

/* Fortran-specific ones */

128+'$', "define-at", "t,Dtw",
128+'a', "area-size", "tw",
128+'^', "position", "twt",
128+'u', "fixup", "t",
128+'x', "convert", "t",
128+'~', "promote", "t",
128+'=', "entry", "s",
    '0', "mark-stack-signed",   0,
    '1', "mark-stack-unsigned", 0,
    '2', "mark-desc-signed",    0,
    '3', "mark-desc-unsigned",  0,
    '4', "debug-v",             "tpsb",
    '5', "debug-t",             "tp",
    '6', "debug-x",             "x",

    '7', "constant-block",      "tX",
    '8', "inline-call",         "t",
    '9', "bss-alloc-next",      0,
};

/* Table below maps every character code to a pointer to relevant entry in 'iop'
   table above, or 0 for undefined I-codes */

struct iop *imap[256]; /* all zeros (null pointers) by default */

/* the mapping is set up at run-time, by the 'setmap' */

void setmap(void) {
  int i;
  struct iop *p;
  for (i = 0; i < sizeof(icodes) / sizeof(icodes[0]); i++) {
    p = &icodes[i];
    if (imap[p->code]) error("setmap: duplicate entry %d", p->code);
    imap[p->code] = p;
  }
}

FILE *ifile, *srcfile = NULL;

void skip_optional(char opt) {
  int c;
  long before;
  before = ftell(ifile);
  c = fgetc(ifile);
  if (c != opt) {
    fseek(ifile, SEEK_SET, before);
  }
}

int main(int argc, char **argv) {
  int c;

  if (argc < 2 || argc > 3) {
    if (argc == 1) fprintf(stderr, "I-code decoder V1.1\n");
    fprintf(stderr, "usage:  idc icodefile\n");
    fprintf(stderr, "   or:  idc icodefile sourcefile\n");
    exit(1);
  }

  ifile = fopen(argv[1], "rb");
  if (ifile == NULL) error("can't open I-code file %s", argv[1]);

  if (argc > 2) {
    srcfile = fopen(argv[2], "r");
    if (srcfile == NULL) error("can't open source file %s", argv[2]);
  }

  setmap();

  while ((c = getc(ifile)) != EOF) decode(c);

  list(INT_MAX); /* flush out rest of source file listing */
}

static int curline = 0;
static int depth /* of include file nesting */ = 0;

void list(int upto) {
  char linebuf[MAXLINE];
  if (srcfile)
    while (curline < upto && fgets(linebuf, MAXLINE, srcfile)) printf("%6d: %s", ++curline, linebuf);
}

void error(char *s, ...) {
  static char buff[256];
  va_list ap;
  fprintf(stderr, "idc: ");
  va_start(ap, s);
  vfprintf(stderr, s, ap);
  va_end(ap);
  fprintf(stderr, "\n\n");

  fprintf(stderr, "Recent icode (at %ld):\n", last_start);
  fseek(ifile, last_start, SEEK_SET);

  for (int i = 0; i < 32; i++) {
    int c = fgetc(ifile);
    if (c == EOF) break;
    fprintf(stderr, "%04x: %02x %c%c\n",
            i,
            c,
            c&128?'!':' ',
            ((' '+1 <= (c&127)) && ((c&127) < 127)) ? c&127 : '?');
    if ((i&4) == 3) fprintf(stderr, "\n");
  }
  fprintf(stderr, "\n");
  
  exit(EXIT_FAILURE);
}

void decode(int code) {
  struct iop *p;
  char *args;
  int line;
  
  if (code == '\n' || code == '\r') return;
  if (code < 0 || code > 255) error("code out of range");

  if (code == 'd') {
    int p1 = gettag(); skip_optional(',');
    int p2 = gettag();
    printf("\t\tCODE 'd' %d %d\n", p1, p2);
    return;
  }
  
  if (code == 'o') {
    /*
Yes, the transputer pass2 expects ‘o’ <tag><tag>, so this is simply a bug in idc.c.

 

Changing the entry for ‘o’ in the icodes[] table from ‘o’, “event-trap”, “t” to ‘o’, “event-trap”, “tt” ought to fix it.

 

Not too surprising that this never came to light before.  The main use cases for idc were: (a) dumping the latest Icode from the EPC Fortran front end to see what they were doing; (b) looking at the output from the C front end when it was doing something wrong.  Neither of these tended to have %on blocks.  :-)  Imp was just assumed to work!

      */
    int p1 = gettag(); skip_optional(',');
    int p2 = gettag();
    printf("\t\tevent-trap %d %d (bug-fixed)\n", p1, p2);
    return;
  }
  
  if (code == 's') {
    printf("\t\t%%stop\n");
    return;
  }
  
  p = imap[code];
  if (p == NULL) error("unknown I-code %d (%s'%c')", code, (code&128?"128+":""), code&127);

  last_start = ftell(ifile)-1L;
  
  //printf("Code %s'%c': ", (code&128?"128+":""), code&127);

  if (code == 'O') { /* line <n> */
    line = gettag();
    if (depth == 0) /* don't try and list contents of include files */
      list(line);
    printf("\t\t%s %d\n", p->name, line);

  } else if (code == 'q') { /* include */
    char *file = getstr(NULL);
    printf("\t\t%s %s\n", p->name, file);
    depth += (*file == '\0' ? -1 : 1);

  } else { /* normal codes */

    printf("\t\t%s", p->name);
    args = p->args;
    if (args) {
      while (*args) handle(*args++);
    }
    printf("\n");
  }
}

void hex_bytes(size_t b) {
  printf(" [");
  while (b--) printf(" %02x", getbyte());
  printf("]");
}

void handle(int argument) {
  switch (argument) {
    case 'w':
      printf(" %d", getword());
      break;

    case 'p':
      printf(" %08x", getword());
      break;

    case 't':
      printf(" %d", gettag());
      break;

    case 'b':
      printf(" %d", getbyte());
      break;

    case 's':
      printf(" \"%s\"", getstr(NULL));
      break;

    case ',':
    case ';':
      printf(" %s", getstring(NULL, argument));
      break;

    case 'z': {
      int b;
      printf(" %d", b = getbyte());
      if (b == 0) /* followed by <tag> (format) */
        printf(" %d", gettag());
      break;
    }

    case '@': {
      int j, c, count = gettag(), exp;
      putchar(' ');
      for (j = 0; j < count; j++) {
        c = getbyte();
        putchar(c);
        if (c == '@') {
          exp = gettag();
          if (exp & 0xFFFF8000) /* sign extend to 32 bits */
            exp |= 0xFFFF8000;
          printf("%d", exp);
          break;
        }
      }
      break;
    }

    case 'D': /* Special for 'define-var' args:
                    'type+form', 'format' and 'ostate' */
    {
      static char *translate[] = {
        /* translate 'special' codes below */
        /*  0: */ "",
        /*  1: */ "byte-integer",
        /*  2: */ "short-integer",
        /*  3: */ "long-real",
      };

      static char *types[16] = {
        /*  0: */ 0,
        /*  1: */ "integer",
        /*  2: */ "real",
        /*  3: */ "string",
        /*  4: */ "record",
        /*  5: */ "Boolean",
        /*  6: */ "set",
        /*  7: */ "byte-enumerated",
        /*  8: */ "short-enumerated",
        /*  9: */ "pointer",
        /* 10: */ "char",
        /* 11: */ 0,
        /* 12: */ 0,
        /* 13: */ 0,
        /* 14: */ 0,
        /* 15: */ "general-area"
      };

      static char *forms[16] = {
        /*  0: */ "0",
        /*  1: */ "simple",
        /*  2: */ "name",
        /*  3: */ "label",
        /*  4: */ "record-format",
        /*  5: */ "5",
        /*  6: */ "switch",
        /*  7: */ "routine",
        /*  8: */ "function",
        /*  9: */ "map",
        /* 10: */ "predicate",
        /* 11: */ "array",
        /* 12: */ "12",
        /* 13: */ "name-array",
        /* 14: */ "name-array-name",
        /* 15: */ "15",
      };

      static char *ostates[] = {
        /*  0: */ "0",
        /*  1: */ "own",
        /*  2: */ "constant",
        /*  3: */ "external",
        /*  4: */ "system",
        /*  5: */ "dynamic",
        /*  6: */ "prim",
        /*  7: */ "perm"
      };

      static struct { int mask; char *name; } oflags[] = {
          8,  "-spec",
          16, "-indirect",
      };

      int tf, type, form, format, ostate, special, i;

      tf = gettag();     skip_optional(','); // NOT SAFE!
      format = gettag(); skip_optional(','); //  "    "
      ostate = gettag();

      type = (tf >> 4) & 0xF;
      form = tf & 0xF;

      special = 0;     /* handle bizarre type encoding scheme */
      if (type == 1) { /* integer-oid */
        if (format == 2)
          special = 1;
        else if (format == 3)
          special = 2;
      } else if (type == 2 && format == 4)
        special = 3;

      printf(" ");
      if (special)
        printf("%s", translate[special]);
      else if (types[type])
        printf("%s", types[type]);
      else
        printf("type=%d", type);

      printf(", %s, format=%d", forms[form], format);

      printf(", %s", ostates[ostate & 7]);

      for (i = 0; i < sizeof(oflags) / sizeof(oflags[0]); i++)
        if (ostate & oflags[i].mask) printf("%s", oflags[i].name);

      break;
    }

    case 'x':
      hex_bytes(getbyte());
      break;

    case 'X':
      hex_bytes(getword());
      break;

    case 'i':
      printf(" [%s]", imap[getbyte()]->name);
      break;

    case '?':
    default:
      error("unknown argument type");
  }
}

int getword(void) {
  int i = 0;
#define DOBYTE i = (i << 8) + getc(ifile)
  DOBYTE;
  DOBYTE;
  DOBYTE;
  DOBYTE;
  return (i);
}

int gettag(void) {
  int i = 0;
  DOBYTE;
  DOBYTE;
  return i;
}

int getbyte(void) {
  unsigned char c;
  c = getc(ifile);
  return c;
}

char *getstr(char *buf) {
  static char sbuf[256];
  int i, len;
  if (buf == NULL) buf = sbuf;
  for (i = 0, len = getbyte(); i < len; i++) buf[i] = getc(ifile);
  buf[len] = '\0';
  return buf;
}

char *getstring(char *buf, int terminator) {
  char *p;
  int c;
  static char sbuf[256];
  if (buf == NULL) buf = sbuf;
  p = buf;
  for (;;) {
    c = getc(ifile);
    if (c == terminator) break;
    *p++ = c;
  }
  *p = '\0';
  return buf;
}
