#include "icode2ast.h"
#include "icode2ast_data.h"
#include "showicode.h"

static char *next_blockname (int lev); 
static void push_blockname (char *s); 
static void pop_blockname (void); 

static char *TagLookup[0x10000];
static int unique_block_no = 0;
static char current_blockname[1024] = { '\0' };
static int IN_START_FINISH_BLOCK = 0;
static int blocklevel = 0;
static int POP_BLOCKNAME_ON_FINISH = FALSE;

static int opt_recode_wanted = 0;

static char *next_blockname(int lev) {
  static char tmp[128];
  if (lev == 0) {
    sprintf(tmp, "main");
  } else {
    sprintf(tmp, "lev%did%d", lev, unique_block_no);
  }
  unique_block_no += 1;
  return tmp;
}

static void push_blockname(char *s) {
  if (*current_blockname == '\0') {
    sprintf(current_blockname, "%s", s);
  } else {
    sprintf(current_blockname+strlen(current_blockname), "_%s", s);
  }
}

static void pop_blockname(void) {
  char *p = strrchr(current_blockname, '_');
  if (p) *p = '\0'; else *current_blockname = '\0';
}

static char *indent(int lev) {
  static char s[256];
  char *sp = s;
  *sp = '\0';
  lev += 1;
  if (lev > 64) lev = 64;
  while (lev --> 0) {
    *sp++ = ' '; *sp++ = ' ';
  }
  *sp = '\0';
  return s;
}

void show_icode(IMPAST *this, int display, FILE *out) {

  if (this == NULL) return;
  
  int opcode = this->opcode;

  if (opcode >= 256 && opcode < LAST_OP) {
    switch (opcode) {
      case ASTOP_PERMS:
      if (display) fprintf(out, "%s! %s <perm code list>", indent(blocklevel), icode_name[opcode]);
      break;
    default:
      if (display) fprintf(out, "%s! %s", indent(blocklevel), icode_name[opcode]);
      break;
    }
    return;
  }

  if (opcode >= LAST_OP) {
    fprintf(stderr, "* showicode.c: Bad opcode: %x(0x%09x)\n", opcode, opcode);
    exit(1);
  }
  
  switch(opcode) {

  case '\n': break; // end of file

  case ';': /* END */
            blocklevel -= 1;
            if (display) fprintf(out, "%s%c %s %s {lev=%d --> %d}", indent(blocklevel), opcode, icode_name[opcode], current_blockname, blocklevel+1, blocklevel);
            pop_blockname();
            break;

  case '~':
            {
              int pending = this->altalt.alttype;

              if ((' ' + 1 <= pending) && (pending <= '~')) {
                if (pending == 'A') {
                  if (display) fprintf(out, "%s%c %s ~A %s", indent(blocklevel), opcode, icode_name[opcode], "ALTBEG");
                } else if (pending == 'B') {
                  if (display) fprintf(out, "%s%c %s ~B %s", indent(blocklevel), opcode, icode_name[opcode], "ALTEND");
                } else if (pending == 'C') {
                  if (display) fprintf(out, "%s%c %s ~C %s", indent(blocklevel), opcode, icode_name[opcode], "ALT");
                } else {
                  if (display) fprintf(out, "%s%c %s ??? %s", indent(blocklevel), opcode, icode_name[opcode], "ALTALT1");
                }
              } else {
                if (display) fprintf(out, "%s%c %s pending=%d %s", indent(blocklevel), opcode, icode_name[opcode], pending, "ALTALT2");
              }
            }
            break;

  case 'H': /* BEGIN */

            push_blockname(next_blockname(blocklevel));
            if (display) fprintf(out, "%s%c %s %s {lev=%d --> %d}", indent(blocklevel), opcode, icode_name[opcode], current_blockname, blocklevel, blocklevel+1);
            blocklevel += 1;
            break;

  case '{': /* START */
            {
            int form;

            if (display) fprintf(out, "%s%c %s", indent(blocklevel), opcode, icode_name[opcode]);
                                 //blocklevel, forms[next_free_form-1]);

            push_form(form = pop_form()); // form was pushed by '$'
            if (display) {
              if (form == F_RECORDFORMAT) {
                fprintf(out, " RECORDFORMAT");
              } else {
                fprintf(out, " FORMAL PARAMETERS  {lev=%d --> %d, nested=%d --> %d}", blocklevel, blocklevel+1, IN_START_FINISH_BLOCK, IN_START_FINISH_BLOCK+1);
              }
            }
            IN_START_FINISH_BLOCK += 1;
            blocklevel += 1;
            }
            break;

  case '|': /* ALT */
            // Only relevant to record definitions, not parameter lists.
            blocklevel -= 1;

            if (display) fprintf(out, "%s%c %s", indent(blocklevel), opcode, icode_name[opcode]);
            blocklevel += 1;
            break;

  case '}': /* FINISH */
            {
            int form;
            blocklevel -= 1;
            push_form(form = pop_form());

            if (display) fprintf(out, "%s%c %s ", indent(blocklevel), opcode, icode_name[opcode]);
            if (form != F_RECORDFORMAT) if (display) fprintf(out, "{lev=%d}", blocklevel);
            IN_START_FINISH_BLOCK -= 1;
            int is_spec = pop_spec();
            push_form(form = pop_form());
            if (form == F_RECORDFORMAT) {
            } else {
              if (POP_BLOCKNAME_ON_FINISH) {
                pop_blockname();
                POP_BLOCKNAME_ON_FINISH = FALSE;
              }
              if (is_spec) {
                blocklevel -= 1; // extra level due to spec                    DOESN'T QUITE WORK WITH REWRITTEN ICODE FILE YET **************************
                                                                               if (blocklevel < 0) blocklevel = 0;
                if (display) fprintf(out, " {lev=%d (due to %%spec)}", blocklevel);
              }
            }
            }
            break;

  case '$': case 'g': /* {RE}DEF TAG TEXT TYPE FORM SIZE SPEC PREFIX */
            {
            int tf, format, ostate;
            int linkage, type, form;
            int special;

/*

       Instruction:  Define <tag> [id] <a> <b> <c>

       Effect:       A new tag value is created.

                     <tag> defines the tag index which  will  be  used  to
                           select the value.

                     [id]  specifies the actual identifier associated with
                           the described object.  It is a sequence of zero
                           or more characters terminated by a comma.  This
                           identifier will be used for external linkage if
                           necessary  unless  overridden by an Alias. [id]
                           will  also  be  used  for  run-time  diagnostic
                           information.

                     <a>   A two-byte value: <a> = T<<4+F where:
                           T = 0 : void
                               1 : integer            {qualified by <b>}
                               2 : real               {qualified by <b>}
                               3 : string             {maximum length <b>}
                               4 : record             {format <b>}
                               5 : boolean
                               6 : set
                               7 : 8-bit-enumerated   {format <b>}
                               8 : 16-bit-enumerated  {format <b>}
                               9 : pointer
                              10 : char
                           11-15 : undefined          {error}

                           F = 0 : void
                               1 : simple                  {byte}
                               2 : indirect                {bytename}
                               3 : general label
                               4 : recordformat
                               5 : undefined               {error}
                               6 : switch
                               7 : routine
                               8 : function
                               9 : map
                              10 : predicate
                              11 : array                   {array}
                              12 : array indirect          {arrayname}
                              13 : indirect array          {namearray}
                              14 : indirect array indirect {namearrayname}
                              15 : undefined               {error}

                     <b>   If   T  is  INTEGER  <b>  takes  the  following
                           meanings:

                           <b> =       1, full range
                                       2, range 0..255
                                       3, range -32768..32767

                           If T is REAL <b> takes the following meanings:

                           <b> =       1, normal precision
                                       4, double precision

                           If T is STRING <b> gives the maximum length  of
                           the string.
                           If  T  is  RECORD  <b>  gives  the  tag  of the
                           corresponding recordformat.
                           If T is enumerated <b> gives  the  tag  of  the
                           dummy  format  used  to identify the enumerated
                           value identifiers.

                     <c>   is a two-byte value: U<<5+I<<4+S<<3+X where:
                              U is 1 check the object for unassigned
                                   0 otherwise
                              I is 1 if the object is an indirect object,
                                   0 otherwise
                              S is 1 if this is a spec,
                                   0 otherwise
                              X  = 0 :: automatic (stack) allocation
                                   1 :: own
                                   2 :: constant
                                   3 :: external
                                   4 :: system
                                   5 :: dynamic
                                   6 :: primitive
                                   7 :: permanent

                           An indirect object (I=1) differs  from  F=2  in
                           that F=2 implies that the actual object created
                           will  be  a  pointer  and  will be dereferenced
                           whenever used unless explicit action  is  taken
                           (e.g.  use  of  Assign-Reference).   If  I=1  a
                           pointer will be created (usually as an integer)
                           and will be treated as an integer (or  address)
                           with no automatic dereferencing taking place.

       Notes:        The  tag  values  within  a block should be dense and
                     preferably consecutive.   All  tag  values  within  a
                     block  must  have values greater than the maximum tag
                     value yet defined within the enclosing block.
                     Tag definitions remain valid until  the  end  of  the
                     enclosing block.
                     The  tag values used within a recordformat definition
                     must all be zero; the fields of a record are selected
                     by their position in the  format,  numbered  starting
                     from one.

                             x = illegal combination

                                               1 1 1 1 1
                       F = 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4
                          *-----------------------------*
                  T = 0   | |x| | |x|x| | |x| | |x| |x| |
                          |-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
                      1   |x| | |x|x|x|x|x| | |x| | | | |
                          |-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
                      2   |x| | |x|x|x|x|x| | |x| | | | |
                          |-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
                      3   |x| | |x|x|x|x|x| | |x| | | | |
                          |-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
                      4   |x| | |x| |x|x|x| | |x| | | | |
                          |-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
                      5   |x| | |x|x|x|x|x| | |x| | | | |
                          |-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
                      6   |x| | |x|x|x|x|x| | |x| | | | |
                          |-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
                      7   |x| | |x|x|x|x|x| | |x| | | | |
                          |-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
                      8   |x| | |x|x|x|x|x| | |x| | | | |
                          |-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
                      9   |x| | |x|x|x|x|x| | |x| | | | |
                          |-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
                     10   |x| | |x|x|x|x|x| | |x| | | | |
                          *-----------------------------*

*/
            TagLookup[this->def.tag] = strdup(this->def.id);

            tf = this->def.a; // a
            format = this->def.b; // b
            ostate = this->def.c; // c

            linkage = ostate&7;
            type = (tf >> 4) & 0xF;
            form = tf & 0xF;

/*
                     <b>   If   T  is  INTEGER  <b>  takes  the  following
                           meanings:

                           <b> =       1, full range
                                       2, range 0..255
                                       3, range -32768..32767

                           If T is REAL <b> takes the following meanings:

                           <b> =       1, normal precision
                                       4, double precision

                           If T is STRING <b> gives the maximum length  of
                           the string.
                           If  T  is  RECORD  <b>  gives  the  tag  of the
                           corresponding recordformat.
                           If T is enumerated <b> gives  the  tag  of  the
                           dummy  format  used  to identify the enumerated
                           value identifiers.
*/

            /* handle bizarre type encoding scheme!: */
            special = SPECIAL_DEFAULT;
            if (type == T_INTEGER) {
              if (format == 2)
                special = SPECIAL_BYTE_INT;
              else if (format == 3)
                special = SPECIAL_SHORT_INT;
            } else if (type == T_REAL /* BUG? was T_STRING? */ && format == 4) {
              special = SPECIAL_LONG_REAL;
            } else if (type == T_STRING) {
            } else if (type == T_RECORD) {
            }


            if (display) fprintf(out, "%s%c ", indent(blocklevel), opcode);

            if (IS_PROCEDURE || form == F_RECORDFORMAT) push_form(form);
              // things that will be followed by START/FINISH blocks

/*XXX*/       if (/*!*/ IN_START_FINISH_BLOCK && /*IN_ENCLOSING_PROCEDURE &&*/ IS_PROCEDURE /*&& !IS_SPEC_ONLY*/) {
              push_spec(IS_SPEC_ONLY);
            } else if (!IN_START_FINISH_BLOCK && IS_PROCEDURE && !IS_SPEC_ONLY) {
              blocklevel += 1;
            }
            if (IS_PROCEDURE /*&& !IS_SPEC_ONLY*/) push_blockname(this->def.id);


            // DEF    FN p1=V_0080 a/tf=18 b/format=1 c/ostate=10  type=1 (integer) form=8 (function) special=0 (<special:0>) linkage=0 (auto) spec=0 indirect=1 unass=0
            char extra[1024];
            extra[0] = '\0';
            if (IN_START_FINISH_BLOCK && /*IN_ENCLOSING_PROCEDURE &&*/ IS_PROCEDURE) {
              sprintf(extra, "(PROCEDURE PARAMETER %s) ", current_blockname);
              if (opt_recode_wanted) strcat(extra, "***** REPLACEMENT CONTENDER ***** ");
              //* icode_needs_replacing[icode_idx] = strdup(current_blockname);
            } else if (IS_PROCEDURE) {
              sprintf(extra, "(%s) ", current_blockname);
              if (0 /*flagged_for_replacement(current_blockname)*/) {
                //* save_fn(icode_idx, current_blockname);
                //* replacement[icode[icode_idx]] = 'g'; // 'REDEF'
                if (opt_recode_wanted) strcat(extra, "***** MASTER ****** ");
              }
            }

            if (display) fprintf(out,
                    "%s %s %stag=V_%04x a/tf=%x b/format=%x c/ostate=%x"
                    "  type=%d (%s) form=%d (%s) special=%d (%s)"
                    " linkage=%d (%s) spec=%d indirect=%d unass=%d",
                    icode_name[opcode],
                    this->def.id, extra, this->def.tag,
                    this->def.a, this->def.b, this->def.c,
                    type, typedesc[type],
                    form, formdesc[form],
                    special, specialdesc[special],
                    linkage, linkagedesc[ostate&7],
                    IS_SPEC_ONLY, IS_INDIRECT, IS_UNASS);

            //if (IN_START_FINISH_BLOCK && !IS_PROCEDURE) if (display) fprintf(out, " ]");

            if (IS_PROCEDURE && IS_SPEC_ONLY) {
              // pop_blockname();
              POP_BLOCKNAME_ON_FINISH = TRUE;
            } else if ((IN_START_FINISH_BLOCK && /*IN_ENCLOSING_PROCEDURE &&*/ IS_PROCEDURE)) {
              pop_blockname();
            }
            // The "&& IS_SPEC_ONLY" part means that the function name
            // in the function parameter doesn't include the name of
            // the function that takes that parameter.  This means that
            // when trying to match the incomplete spec with the
            // full spec that follows later, we can't match based on the
            // fully-scoped function name.  So I'll have to remove
            // the "&& IS_SPEC_ONLY" and pop the block name at the FINISH
            // of the START/FINISH block. (TO DO)

            extra[0] = '\0';

            }
            break;

  case '"': /* JUMPIFD cond label */

            if (display) fprintf(out, "%s%c %s %c L_%04x", indent(blocklevel), (char)opcode, icode_name[opcode], this->jumpifd.cond, this->jumpifd.label);
            break;
  case 'C': /* JUMPIFA cond label */

            if (display) fprintf(out, "%s%c %s %c L_%04x", indent(blocklevel), (char)opcode, icode_name[opcode], this->jumpifa.cond, this->jumpifa.label);
            break;
  case '?': /* JUMPIF cond label */

            if (display) fprintf(out, "%s%c %s %c L_%04x", indent(blocklevel), (char)opcode, icode_name[opcode], this->jumpif.cond, this->jumpif.label);
            break;

  case 'k': /* BF label */

            if (display) fprintf(out, "%s%c %s L_%04x", indent(blocklevel), opcode, icode_name[opcode], this->bf.label);
            break;
  case 't': /* BT label */

            if (display) fprintf(out, "%s%c %s L_%04x", indent(blocklevel), opcode, icode_name[opcode], this->bt.label);
            break;

  case '\'': /* PUSHS sconst */

            if (display) {
              fprintf(out, "%s%c %s ", indent(blocklevel), opcode, icode_name[opcode]);
              char *s = this->pushs.sconst;
              fprintf(out, "\"");
              for (;;) {
                int c = *s++;
                if (c == '\0') break;
                // there are a lot more C escape sequences but this will do for now.
                if (c == '\n') {
                  fprintf(out, "\\n");
                } else if (c == '"') {
                  fprintf(out, "\\\"");
                } else {
                  fprintf(out, "%c", c);
                }
              }
              fprintf(out, "\"");
            }
            break;

  case 'G': /* ALIAS */

            if (display) fprintf(out, "%s%c %s %s", indent(blocklevel), opcode, icode_name[opcode], this->alias.alias);
            break;

  case 'D': /* PUSHR rconst */

            if (display) fprintf(out, "%s%c %s ", indent(blocklevel), opcode, icode_name[opcode]);
            if (display) fprintf(out, "code=%d \"%s", this->pushr.code, this->pushr.str);
            if (this->pushr.exponent != 0) {
              if (display) fprintf(out, " @ (signed short)0x%04d", this->pushr.exponent);
            }
            if (display) fprintf(out, "\"");
            break;

  case 'N': /* PUSHI iconst */

            if (display) fprintf(out, "%s%c %s %d", indent(blocklevel), opcode, icode_name[opcode], this->pushi.word32);
            break;

  case ':': /* LOCATE label */

            if (display) fprintf(out, "%s%c %s L_%04x", indent(blocklevel), opcode, icode_name[opcode], this->locate.label);
            break;
  case 'B': /* REPEAT label */

            if (display) fprintf(out, "%s%c %s L_%04x", indent(blocklevel), opcode, icode_name[opcode], this->repeat.label);
            break;
  case 'F': /* GOTO label */

            if (display) fprintf(out, "%s%c %s L_%04x", indent(blocklevel), opcode, icode_name[opcode], this->igoto.label);
            break;
  case 'J': /* JUMP label */

            if (display) fprintf(out, "%s%c %s L_%04x", indent(blocklevel), opcode, icode_name[opcode], this->jump.label);
            break;
  case 'L': /* LABEL label */

            if (display) fprintf(out, "%s%c %s L_%04x", indent(blocklevel), opcode, icode_name[opcode], this->label.label);
            break;
  case 'f': /* FOR label (label was missing from thesis description) */

            if (display) fprintf(out, "%s%c %s L_%04x", indent(blocklevel), opcode, icode_name[opcode], this->ifor.label);
            break;

  case 'O': /* LINE decimal */

            //suppress_perms = FALSE;
            // EXTENSION FOR C-PASS1:
            /*if (opt_extended_line) fname = this->line.filename; */
            if (display) fprintf(out, "%s%c %s %d %s", indent(blocklevel), opcode, icode_name[opcode], this->line.lineno, this->line.filename);
            break;

  case '@': /* PUSH tag */

            if (display) fprintf(out, "%s%c %s tag=0x%04x", indent(blocklevel), opcode, icode_name[opcode], this->push.tag);
            break;
  case '^': /* PROC tag */

            if (display) fprintf(out, "%s%c %s tag=0x%04x", indent(blocklevel), opcode, icode_name[opcode], this->proc.tag);
            break;

  case 'n': /* SELECT tag */

            if (display) fprintf(out, "%s%c %s tag=0x%04x", indent(blocklevel), opcode, icode_name[opcode], this->select.tag);
            break;

  case '`': /* defsw */         // EXTENSION FOR C-PASS1: default switch label

            if (display) fprintf(out, "%s%c %s L_%04x", indent(blocklevel), opcode, icode_name[opcode], this->defsw.label);
            break;
  case 'W': /* SJUMP sd */

            if (display) fprintf(out, "%s%c %s L_%04x", indent(blocklevel), opcode, icode_name[opcode], this->sjump.label);
            break;
  case '_': /* SLABEL sd */

            if (display) fprintf(out, "%s%c %s L_%04x", indent(blocklevel), opcode, icode_name[opcode], this->slabel.label);
            break;

  case 'd': /* DIM short,short */

            if (display) fprintf(out, "%s%c %s dims %d #arrays %d", indent(blocklevel), opcode, icode_name[opcode], this->dim.dimens, this->dim.arrays);
            break;

  case 'Y': /* DEFAULT short */

            if (display) fprintf(out, "%s%c %s %d", indent(blocklevel), opcode, icode_name[opcode], this->idefault.data);
            break;
  case 'A': /* INIT short */

            if (display) fprintf(out, "%s%c %s %d", indent(blocklevel), opcode, icode_name[opcode], this->init.data);
            break;

  case 'y': /* DIAG short */

            if (display) fprintf(out, "%s%c %s 0x%04X", indent(blocklevel), opcode, icode_name[opcode], this->diag.data);
            break;
  case 'z': /* CONTROL short */

            if (display) fprintf(out, "%s%c %s 0x%04X", indent(blocklevel), opcode, icode_name[opcode], this->control.data);
            break;
  case 'e': /* EVENT short */

            if (display) fprintf(out, "%s%c %s 0x%04X", indent(blocklevel), opcode, icode_name[opcode], this->event.data);
            break;
  case 'l': /* LANG short */

            if (display) fprintf(out, "%s%c %s 0x%04X", indent(blocklevel), opcode, icode_name[opcode], this->lang.language);
            break;

  case 'o': /* ON byte short label */ /* BUG! wrong data */

            if (display) fprintf(out, "%s%c %s MASK=0x%04X L_%04x", indent(blocklevel), opcode, icode_name[opcode], this->on.mask, this->on.label);
            break;

  case 'r': /* RESOLVE m */

      /*
r flag   String Resolution.  Flag is a three bit number, the
       bits representing:
            4    - conditional resolution.
            2    - first destination present.
            1    - second destination present.

       The resolution sets the condition code to true for
       successful resolution, and false for failure.
       The various operands are on the top of the stack.

       e.g.      S -> A.(B).C        @s@a@b@c r3

                 S -> A.(B)          @s@a@b r1

                 stop if S -> (B).C  @s@b@c r5k4s:4

               */
              // result is tested by BF or BT!
            if (display) fprintf(out, "%s%c %s flags=%04x", indent(blocklevel), opcode, icode_name[opcode], this->resolve.flags);
            break;

  case 'w': /* SUBA - documentation failure??? file uses 'w'  */
            opcode = 'c';

            // ? break;

  case 'c': /* MCODE */

#ifdef NEVER
            {
              int i1;
              short int h1;
              if (display) fprintf(out, "%s%c %s ", indent(blocklevel), opcode, icode_name[opcode]);
              for (;;) {
                p1 = getmcstring(&i1);
                if (display) fprintf(out, " %s", p1);
                if (i1 == ';') break;
                p2 = getshort();
                if (display) fprintf(out, " tag_%s", p2);
              }
            }
#endif
            break;

  case '!': /* OR */
  case '#': /* BNE */
  case '%': /* XOR */
  case '&': /* AND */
  case '*': /* MUL */
  case '+': /* ADD */
  case '-': /* SUB */
  case '.': /* CONCAT */
  case '/': /* QUOT */
  case 'E': /* CALL */
  case 'K': /* FALSE */
  case 'M': /* MAP */
  case 'P': /* PLANT */
  case 'Q': /* DIVIDE */
  case 'R': /* RETURN */
  case 'S': /* ASSVAL */
  case 'T': /* TRUE */
  case 'U': /* NEGATE */
  case 'V': /* RESULT */
  case 'X': /* IEXP */
  case 'Z': /* ASSREF */
  case '[': /* LSH */
  case '\\': /* NOT */
  case ']': /* RSH */
  case 'a': /* ACCESS */
  case 'b': /* BOUNDS */
  case 'h': /* ALTBEG */
  case 'i': /* INDEX */
  case 'j': /* JAM */
  case 'm': /* MONITOR */
  case 'p': /* ASSPAR */
  case 'q': /* SUBA */
  case 's': /* STOP */
  case 'u': /* ADDA */
  case 'v': /* MOD */
  case 'x': /* REXP */

            if (display) fprintf(out, "%s%c %s", indent(blocklevel), opcode, icode_name[opcode]);
            break;

  default:

    if (opcode < 0 || opcode > 255 || icode_name[opcode] == NULL) {
      if (display) fprintf(out, "? showicode.c: %sOPCODE %d\n", indent(blocklevel)+1, opcode);
    } else {
      if (display) fprintf(out, "? showicode.c: %s%c %s\n", indent(blocklevel)+1, opcode, icode_name[opcode]);
    }
    break;
  }

}
