/* Copyright (C) 3L Ltd. 1987-1991 */

/* I-code decoder.   ADC, 1-Jan-89  */

#include <stdio.h>

#define MAXINT  ((unsigned)(-1)<<1) >> 1 /* most positive 2's complement int */
#define MAXLINE 132  /* longest acceptable source line size */

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' */

setmap()
{
   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;

main(argc, argv)
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(MAXINT);     /* flush out rest of source file listing */
}

static int curline = 0;
static int depth /* of include file nesting */ = 0;

list(upto)
{
   char linebuf[MAXLINE];
   if (srcfile)
      while (curline < upto  &&  fgets(linebuf, MAXLINE, srcfile))
         printf("%6d: %s", ++curline, linebuf);
}

error(fmt, a, b, c, d, e)
char *fmt;
{
   fprintf(stderr, "idc: ");
   fprintf(stderr, fmt, a, b, c, d, e);
   fprintf(stderr, "\n");
   exit(1);
}

decode(code)
{
   struct iop *p;
   char *args;
   int line;

   if (code < 0 || code > 255) error("code out of range");
   p = imap[code];
   if (p == NULL)
      error("unknown I-code %d", code);

   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("]");
}

handle(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();  format = gettag();  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()
{
   int i = 0;
#  define DOBYTE    i = (i<<8) + getc(ifile)
   DOBYTE;  DOBYTE;  DOBYTE;  DOBYTE;
   return (i);
}

int gettag()
{
   int i = 0;
   DOBYTE;  DOBYTE;
   return i;
}

int getbyte()
{
   unsigned char c;
   c = getc(ifile);
   return c;
}

char *getstr(buf)
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(buf, terminator)
char *buf;
{
   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;
}
