Source file: /~heha/hs/gputils64-210929.zip/gputils/gpdasm.cpp

/* Disassembles ".HEX" files
   Copyright 2001-2005	Craig Franklin
   Copyright 2014-2016	Molnár Károly
*/

#include "stdhdr.h"

#include "libgputils.h"
#include "gpcfg.h"
#include "gpdasm.h"
#include "labelset.h"
#include "parse.h"
#include "scan.h"

#ifdef STDC_HEADERS
#include <stdarg.h>
#endif

#define USER_LABEL_ALIGN        15

extern int yyparse(void);

struct gpdasm_state state;

static const char *processor_name;

static gp_cfg_addr_pack_t addr_pack;

static struct {
  bool   is_print;
  unsigned number;
  int          words[PIC16E_IDLOCS_SIZE];
} idlocs_pack = { 0, 0, { -1, } };

static bool prev_empty_line = false;

static char border[] = "===============================================================================";

static char out_buffer[BUFSIZ];

static const char longopts[] =
  "cmnemonics\0"
  "hhelp\0"
  "ihex-info\0"
  "jmov-fsrn\0"
  "klabel-list\1FILE\0"
  "llist-chips\0"
  "mdump\0"
  "nshow-names\0"
  "oshow-config\0"
  "pprocessor\1PROC\0"
  "sshort\0"
  "\1strict-options\0"
  "tuse-tab\0"
  "vversion\0"
  "yextended\0"
  "\2strict\0";

static const char longdesc[]=
  "Decode the special mnemonics.\0"
  "Show this usage message.\0"
  "Show the informations of the input hex file.\0"
  "In the MOVIW or MOVWI instructions show as base"
	" the FSRn register instead of the INDFn. [INDFn]\0"
  "A file which lists the names and addresses of"
	" the labels in the disassembled program code."
	" (With the -n, -o and -s options.)\0"
  "List the supported processors.\0"
  "Memory dump of the input hex file.\0"
  "For some case of SFR, shows the name of"
	" instead of the address. In addition shows"
	" the labels also.\0"
  "Show the CONFIG and IDLOCS - or __idlocs - directives.\0"
  "Select the microcontroller.\0"
  "Print short format output. (Creates a compilable"
	" source. See also the -k, -n and -o options.)\0"
  "Uses tabulator character in the written disassembled"
	" text.\0"
  "Print the gpdasm version information and exit.\0"
  "Enable 18xx extended mode.\0"
  "Disassemble only opcodes generated by gpasm"
	" in case of instructions with several opcodes.\0"
  "If this is set, then an option may not be parameter"
	" of an another option. For example: -p --dump\0";


/*------------------------------------------------------------------------------------------------*/

static void _show_usage(const char*argv0) {
  gp_usage(argv0,longopts,longdesc,32,79,
   "For example:  gpdasm -nos -k program.ulist -p12f1822 program.hex > program.dis\n");
  exit(0);
}

/*------------------------------------------------------------------------------------------------*/

static size_t _unexpand(char *Dst, size_t Size, const char *Src) {
  size_t in_read_idx;
  size_t in_read_virt_idx;
  size_t out_write_idx;
  size_t first_tab;
  size_t last_char;
  size_t space_num;

  if (Size <= 1) {
    return 0;
  }

  --Size;

  in_read_idx      = 0;
  in_read_virt_idx = 0;
  out_write_idx    = 0;
  first_tab        = 0;
  last_char        = 0;
  space_num        = 0;

  while ((Src[in_read_idx] != '\0') && (out_write_idx < Size)) {
    if (Src[in_read_idx] == ' ') {
      ++space_num;
    }
    else if (Src[in_read_idx] == '\n') {
      /* This a newline character. */
      Dst[out_write_idx++] = Src[in_read_idx];
      in_read_virt_idx = 0;
      last_char        = 0;
      space_num        = 0;
      ++in_read_idx;
      continue;
    }
    else {
      /* This a not-space character. */
      if (space_num > 1) {
        first_tab = ((last_char + TABULATOR_SIZE) / TABULATOR_SIZE) * TABULATOR_SIZE;

        if (first_tab > in_read_virt_idx) {
          first_tab = 0;
        }

        if ((last_char + 1) < first_tab) {
          if (first_tab <= in_read_virt_idx) {
            Dst[out_write_idx++] = '\t';

            if (out_write_idx >= Size) {
              goto _exit;
            }

            space_num = in_read_virt_idx - first_tab;
          }

          while (space_num >= TABULATOR_SIZE) {
            Dst[out_write_idx++] = '\t';

            if (out_write_idx >= Size) {
              goto _exit;
            }

            space_num -= TABULATOR_SIZE;
          }
        }
      }

      while (space_num > 0) {
        Dst[out_write_idx++] = ' ';

        if (out_write_idx >= Size) {
          goto _exit;
        }

        --space_num;
      }

      Dst[out_write_idx++] = Src[in_read_idx];
      last_char = in_read_idx;
    }

    ++in_read_idx;
    ++in_read_virt_idx;
  } /* while ((Src[in_read_idx] != '\0') && */

  while (space_num >= TABULATOR_SIZE) {
    Dst[out_write_idx++] = '\t';
    if (out_write_idx >= Size) {
      goto _exit;
    }

    space_num -= TABULATOR_SIZE;
  }

_exit:

  Dst[out_write_idx] = '\0';
  return out_write_idx;
}

/*------------------------------------------------------------------------------------------------*/

static void _cdecl _ux_print(bool New_line, const char *Format, ...)
{
  va_list     ap;
  char        buffer[BUFSIZ];
  const char *bptr;
  size_t      len;

  va_start(ap, Format);
  len = (size_t)vsnprintf(buffer, sizeof(buffer), Format, ap);
  va_end(ap);

  if ((int)len < 0) {
    return;
  }

  bptr = buffer;

  if (New_line) {
    if (state.use_tab) {
      _unexpand(out_buffer, sizeof(out_buffer), buffer);
      bptr = out_buffer;
    }

    puts(bptr);
  }
  else {
    if (state.use_tab) {
      len  = _unexpand(out_buffer, sizeof(out_buffer), buffer);
      bptr = out_buffer;
    }

    printf("%s", bptr);
  }
}

/*------------------------------------------------------------------------------------------------*/

static void _select_processor() {
  pic_processor_t found = NULL;

  if (processor_name == NULL) {
    fprintf(stderr, "Error: Must select the processor.\n");
    exit(1);
  }

  found = gp_find_processor(processor_name);

  if (found) state.processor = found;
  else {
    fprintf(stderr, "Error: Didn't find any processor named: %s\n", processor_name);
    printf("Here are the supported processors:\n");
    gp_dump_processor_list(true, PROC_CLASS_UNKNOWN, PROC_CLASS_UNKNOWN, PROC_CLASS_UNKNOWN);
    exit(1);
  }

  state.pclass = gp_processor_class(state.processor);

  if (!state.pclass->instructions) {
    fprintf(stderr, "Error: Unsupported processor class.\n");
    exit(1);
  }

  state.proc_regs = gp_register_table_t::find_mcu(state.processor->names[1]);
}

/*------------------------------------------------------------------------------------------------*/

static void _write_header() {
  if (!state.format) {
    _ux_print(true, "        processor %s\n"
                    "        radix dec", state.processor->names[1]);
    if (state.processor->header) {
      _ux_print(true, "\n"
                      "        include %s", state.processor->header);
    }
  }
}

/*------------------------------------------------------------------------------------------------*/

static void _write_core_sfr_list() {
  if (state.format) return;
  if (!state.pclass->core_sfr_table) return;
  if (!state.pclass->core_sfr_number) return;

  _ux_print(true, "\n"
                  "F\tequ\t1\n"
                  "W\tequ\t0");

  if (state.pclass == PROC_CLASS_PIC16E) {
    _ux_print(true, "A\tequ\t0\n"
                    "B\tequ\t1");
  }

  _ux_print(true, "");

  const core_sfr_t *table = state.pclass->core_sfr_table;
  for (int i = state.pclass->core_sfr_number; i > 0; ++table, --i) {
    if ((state.pclass == PROC_CLASS_PIC14E) || (state.pclass == PROC_CLASS_PIC14EX)) {
      if (!strcmp(table->name, "FSR0L")) {
        _ux_print(true, "FSR0\tequ\t0x%03x", table->address);
      }
      else if (!strcmp(table->name, "FSR1L")) {
        _ux_print(true, "FSR1\tequ\t0x%03x", table->address);
      }
    }
    _ux_print(true, "%s\tequ\t0x%03x", table->name, table->address);
  }
}

/*------------------------------------------------------------------------------------------------*/

static void _end_asm() {
  if (!state.format) {
    _ux_print(true, "\n"
                    "\tend");
  }
}

/*------------------------------------------------------------------------------------------------*/

static void _write_org(int Org, int Addr_digits, const char *Title,
	const char *Address_name, int Offset) {
  size_t length;
  char   buffer[BUFSIZ];

  if (!state.format) {
    _ux_print(true, "");

    if (Title) {
      _ux_print(true, "\t; %s", Title);
    }

    if (Address_name) {
      if (Offset > 0) {
        length = snprintf(buffer, sizeof(buffer), "\torg\t(%s + 0x%0*x)", Address_name,
                          Addr_digits, Offset);
      }
      else {
        length = snprintf(buffer, sizeof(buffer), "\torg\t%s", Address_name);
      }

      gp_exclamation(buffer, sizeof(buffer), length, "; address: 0x%0*x", Addr_digits, Org);
    }else{
      snprintf(buffer, sizeof(buffer), "\torg\t0x%0*x", Addr_digits, Org);
    }

    _ux_print(true, "\n%s\n", buffer);
    prev_empty_line = true;
  }
}

/*------------------------------------------------------------------------------------------------*/

static void _mark_false_addresses(MemBlock_t *Memory) {
  MemBlock_t    *m;
  int            i;
  int            maximum;
  int            org;
  int            insn_size;
  int            num_words;
  uint16_t data;

  m = Memory;
  while (m) {
    i       = IMemAddrFromBase(m->base);
    maximum = i + I_MEM_MAX;

    insn_size = 2;
    while (i < maximum) {
      org = gp_processor_insn_from_byte_p(state.processor, i);

      if (gp_processor_is_idlocs_org(state.processor, org) >= 0) {
        insn_size = (state.pclass == PROC_CLASS_PIC16E) ? 1 : 2;
      }
      else if (gp_processor_is_config_org(state.processor, org) >= 0) {
        insn_size = (state.pclass == PROC_CLASS_PIC16E) ? 1 : 2;
      }
      else if (gp_processor_is_eeprom_org(state.processor, org) >= 0) {
        insn_size = 1;
      }
      else {
        if (state.pclass->i_memory_get(m, i, &data, NULL, NULL) == W_USED_ALL) {
          num_words = gp_disassemble_mark_false_addresses(m, i, state.processor);
          insn_size = (num_words != 1) ? 4 : 2;
        }
      }

      i += insn_size;
    }

    m = m->next;
  }
}

/*------------------------------------------------------------------------------------------------*/

static void _recognize_labels_and_spec_words(MemBlock_t *Memory) {
  MemBlock_t            *m;
  int                    i;
  int                    maximum;
  int                    index;
  int                    org;
  int                    offset;
  int                    insn_size;
  int                    num_words;
  uint8_t                byte;
  uint16_t               data;
  const vector_t        *vector;
  gpdasm_fstate_t        fstate;
  const gp_cfg_device_t *dev;
  gp_cfg_addr_hit_t     *hit;
  unsigned           max_width;
  unsigned           type;
  lset_symbol_t         *sym;

  if (state.show_config) {
    dev = gp_cfg_find_pic_multi_name(state.processor->names, ARRAY_SIZE(state.processor->names));
    if (dev == NULL) {
      fprintf(stderr, "Warning: The %s processor has no entries in the config db.", state.processor->names[2]);
    }
  }
  else {
    dev = NULL;
  }

  m                    = Memory;
  fstate.wreg          = 0;
  fstate.pclath        = 0;
  fstate.pclath_valid  = 0xff;
  addr_pack.hit_count  = 0;
  max_width            = 0;
  idlocs_pack.number   = 0;
  idlocs_pack.is_print = true;
  while (m) {
    i       = IMemAddrFromBase(m->base);
    maximum = i + I_MEM_MAX;

    insn_size = 2;
    while (i < maximum) {
      org = gp_processor_insn_from_byte_p(state.processor, i);

      if ((index = gp_processor_is_idlocs_org(state.processor, org)) >= 0) {
        insn_size = (state.pclass == PROC_CLASS_PIC16E) ? 1 : 2;

        if (state.pclass == PROC_CLASS_PIC16E) {
          if (gp_mem_b_get(m, i, &byte, NULL, NULL)) {
            idlocs_pack.words[index] = byte;

            if (!isprint(byte)) {
              idlocs_pack.is_print = false;
            }

            ++idlocs_pack.number;
          }
          else {
            idlocs_pack.words[index] = -1;
          }
        }
        else {
          if (state.pclass->i_memory_get(m, i, &data, NULL, NULL) == W_USED_ALL) {
            sym = lset_symbol_find_addr(state.lset_root.sections[SECT_SPEC_CODE], org, -1, true);

            if ((sym) && !(sym->attr & CSYM_ORG)) {
              if (sym->start == (long)org) {
                gp_mem_b_set_addr_name(m, i, sym->name);
                sym->attr |= CSYM_USED;
              }
            }

            idlocs_pack.words[index] = data;
            ++idlocs_pack.number;
          }
          else {
            idlocs_pack.words[index] = -1;
          }
        }
      }
      else if (gp_processor_is_config_org(state.processor, org) >= 0) {
        insn_size = (state.pclass == PROC_CLASS_PIC16E) ? 1 : 2;

        if (dev) {
          if (addr_pack.hit_count < GP_CFG_ADDR_PACK_MAX) {
            if (state.pclass == PROC_CLASS_PIC16E) {
              if (gp_mem_b_get(m, i, &byte, NULL, NULL)) {
                hit = &addr_pack.hits[addr_pack.hit_count];

                if (gp_cfg_decode_directive(dev, org, byte, hit) > 0) {
                  if (max_width < hit->max_dir_width) {
                    max_width = hit->max_dir_width;
                  }

                  ++addr_pack.hit_count;
                }
              }
            }
            else {
              if (state.pclass->i_memory_get(m, i, &data, NULL, NULL) == W_USED_ALL) {
                hit = &addr_pack.hits[addr_pack.hit_count];

                if (gp_cfg_decode_directive(dev, org, data, hit) > 0) {
                  if (max_width < hit->max_dir_width) {
                    max_width = hit->max_dir_width;
                  }

                  ++addr_pack.hit_count;
                }
              }
            }
          }
          else {
            fprintf(stderr, "Warning: The value of GP_CFG_ADDR_PACK_MAX too little: %u",
                    GP_CFG_ADDR_PACK_MAX);
          }
        } /* if (dev) */
      } /* else if (gp_processor_is_config_org(state.processor, org) >= 0) */
      else if ((offset = gp_processor_is_eeprom_org(state.processor, org)) >= 0) {
        if (gp_mem_b_get(m, i, &byte, NULL, NULL)) {
          sym = lset_symbol_find_addr(state.lset_root.sections[SECT_SPEC_EEDATA], offset, -1, true);

          if ((sym) && !(sym->attr & CSYM_ORG)) {
            if (sym->start == (long)offset) {
              gp_mem_b_set_addr_name(m, i, sym->name);
              sym->attr |= CSYM_USED;
            }
          }
        }

        insn_size = 1;
      }
      else {
        if (state.pclass->i_memory_get(m, i, &data, NULL, NULL) == W_USED_ALL) {
          sym = lset_symbol_find_addr(state.lset_root.sections[SECT_SPEC_CODE], org, -1, true);

          if ((sym) && !(sym->attr & CSYM_ORG)) {
            type = (sym->attr & CSYM_DATA) ? W_CONST_DATA : 0;

            if (sym->start == (long)org) {
              type |= W_ADDR_T_LABEL;
              gp_mem_b_set_addr_name(m, i, sym->name);
            }

            gp_mem_b_set_type(m, i, type);
            insn_size = 2;
          }
          else {
            if (state.pclass == PROC_CLASS_SX) {
            /* Unlike the others, the address of reset vector located the top of program memory. */
              org = (org == state.processor->maxrom) ? -1 : org;
            }

            vector = gp_processor_find_vector(state.pclass, org);

            if (vector) {
              gp_mem_b_set_addr_type(m, i, W_ADDR_T_LABEL, 0);
              gp_mem_b_set_addr_name(m, i, vector->name);
            }

            num_words = gp_disassemble_find_labels(m, i, state.processor, &fstate);
            insn_size = (num_words != 1) ? 4 : 2;
          }
        }
      }

      i += insn_size;
    }

    m = m->next;
  }

  addr_pack.max_dir_width = max_width;
}

/*------------------------------------------------------------------------------------------------*/

static void _stdcall _user_data_finder(MemArg_t *Argument) {
  lset_symbol_t *sym;

  if (Argument->arg) {
    return;
  }

  sym = lset_symbol_find_addr(state.lset_root.sections[SECT_SPEC_DATA], (long)Argument->val, -1, true);

  if (sym) {
    Argument->arg  = sym->name;
    Argument->offs = (int)((long)Argument->val - sym->start);
    /* This will be necessary for the later listing. */
    sym->attr |= CSYM_USED;
  }
}

/*------------------------------------------------------------------------------------------------*/

static void _recognize_registers(MemBlock_t *Memory) {
  MemBlock_t      *m;
  int              i;
  int              maximum;
  int              org;
  int              insn_size;
  int              num_words;
  uint16_t   data;
  gpdasm_fstate_t  fstate;

  if (state.pclass == PROC_CLASS_SX) {
    return;
  }

  m                   = Memory;
  fstate.wreg         = 0;
  fstate.bank         = 0;
  fstate.bank_valid   = 0xff;
  fstate.proc_regs    = state.proc_regs;
  fstate.bsr_boundary = gp_processor_bsr_boundary(state.processor);
  fstate.need_sfr_equ = false;
  while (m) {
    i       = IMemAddrFromBase(m->base);
    maximum = i + I_MEM_MAX;

    insn_size = 2;
    while (i < maximum) {
      org = gp_processor_insn_from_byte_p(state.processor, i);

      if (gp_processor_is_idlocs_org(state.processor, org) >= 0) {
        insn_size = (state.pclass == PROC_CLASS_PIC16E) ? 1 : 2;
      }
      else if (gp_processor_is_config_org(state.processor, org) >= 0) {
        insn_size = (state.pclass == PROC_CLASS_PIC16E) ? 1 : 2;
      }
      else if (gp_processor_is_eeprom_org(state.processor, org) >= 0) {
        insn_size = 1;
      }
      else {
        if (state.pclass->i_memory_get(m, i, &data, NULL, NULL) == W_USED_ALL) {
          num_words = gp_disassemble_find_registers(m, i, state.processor, &fstate, _user_data_finder);
          insn_size = (num_words != 1) ? 4 : 2;
        }
      }

      i += insn_size;
    }

    m = m->next;
  }

  state.need_sfr_equ = fstate.need_sfr_equ;
}

/*------------------------------------------------------------------------------------------------*/

static void
_denominate_labels(MemBlock_t *Memory)
{
  MemBlock_t   *m;
  int           i;
  int           maximum;
  unsigned  type;
  unsigned  func_idx;
  unsigned  label_idx;
  char          buffer[BUFSIZ];

  m         = Memory;
  func_idx  = 0;
  label_idx = 0;
  while (m) {
    i       = IMemAddrFromBase(m->base);
    maximum = i + I_MEM_MAX;

    while (i < maximum) {
      type = gp_mem_b_get_addr_type(m, i, NULL, NULL);

      if (type & W_ADDR_T_FUNC) {
        snprintf(buffer, sizeof(buffer), "function_%03u", func_idx);
        gp_mem_b_set_addr_name(m, i, buffer);
        ++func_idx;
      }
      else if (type & W_ADDR_T_LABEL) {
        snprintf(buffer, sizeof(buffer), "label_%03u", label_idx);
        gp_mem_b_set_addr_name(m, i, buffer);
        ++label_idx;
      }

      i += 2;
    }

    m = m->next;
  }
}

/*------------------------------------------------------------------------------------------------*/

static size_t
_byte_exclamation(char *Buffer, size_t Buffer_length, size_t Current_length, uint8_t Byte)
{
  int    l;
  size_t length;

  l = snprintf(&Buffer[Current_length], Buffer_length - Current_length, "%-*s0x%02x",
               TABULATOR_SIZE, "db", (unsigned)Byte);

  if (l <= 0) {
    return Current_length;
  }

  length = Current_length + l;

  if (isprint(Byte)) {
    gp_exclamation(Buffer, Buffer_length, length, "; '%c'", Byte);
  }

  return length;
}

/*------------------------------------------------------------------------------------------------*/

static void
_show_config(void)
{
  gp_cfg_addr_hit_t *hit;
  unsigned       m;
  unsigned       n;

  if (addr_pack.hit_count == 0) {
    return;
  }

  _ux_print(true, "");
  for (m = 0; m < addr_pack.hit_count; ++m) {
    hit = &addr_pack.hits[m];

    for (n = 0; n < hit->pair_count; ++n) {
      _ux_print(true, "        CONFIG  %-*s = %s", addr_pack.max_dir_width,
                hit->pairs[n].directive->name, hit->pairs[n].option->name);
    }
  }
}

/*------------------------------------------------------------------------------------------------*/

static void
_show_idlocs(void)
{
  unsigned i;
  unsigned j;
  int          word;
  bool   prev_exist;
  bool   act_exist;
  bool   aligned;
  char         buffer[PIC16E_IDLOCS_SIZE * 2];

  if (idlocs_pack.number == 0) {
    return;
  }

  if (state.pclass == PROC_CLASS_PIC16E) {
    prev_exist = false;
    act_exist  = false;
    aligned    = false;
    for (i = 0, word = 0; i < PIC16E_IDLOCS_SIZE; ++i) {
      act_exist = (idlocs_pack.words[i] >= 0) ? true : false;

      if (i == 0) {
        /* Start at the beginning. */
        aligned = act_exist;
      }

      if ((!prev_exist) && act_exist) {
        ++word;

        if (word > 1) {
          /* There are several separate words or characters. */
          break;
        }
      }

      prev_exist = act_exist;
    }

    if (aligned && (word == 1)) {
      /* One word and start at the beginning. */
      if (idlocs_pack.is_print) {
        /* Only printable characters are there in it. */
        for (i = 0, j = 0; i < PIC16E_IDLOCS_SIZE; ++i) {
          if ((word = idlocs_pack.words[i]) > 0) {
            buffer[j++] = (char)word;
          }
        }

        buffer[j] = '\0';
        _ux_print(true, "\n"
                        "        IDLOCS  \"%s\"", buffer);
      }
      else {
        /* Not only printable characters are there in it. */
        _ux_print(true, "");
        for (i = 0; i < PIC16E_IDLOCS_SIZE; ++i) {
          if ((word = idlocs_pack.words[i]) >= 0) {
            if (isalnum(word)) {
              _ux_print(true, "        __idlocs _IDLOC%u, '%c'", i, word);
            }
            else {
              _ux_print(true, "        __idlocs _IDLOC%u, 0x%02X", i, word);
            }
          }
        }
      }
    } /* if (aligned && (word == 1)) */
    else {
      /* There are several separate words or characters, or do not start at the beginning. */
      prev_exist = false;
      act_exist  = false;
      _ux_print(true, "");
      for (i = 0; i < PIC16E_IDLOCS_SIZE; ++i) {
        if ((word = idlocs_pack.words[i]) >= 0) {
          act_exist = true;

          if (isalnum(word)) {
            _ux_print(true, "        __idlocs _IDLOC%u, '%c'", i, word);
          }
          else {
            _ux_print(true, "        __idlocs _IDLOC%u, 0x%02X", i, word);
          }
        }
        else {
          act_exist = false;
        }

        if (prev_exist && (!act_exist)) {
          /* Indicates the break. */
          _ux_print(true, "");
        }

        prev_exist = act_exist;
      }
    }
  } /* if (state.pclass == PROC_CLASS_PIC16E) */
  else {
    if (idlocs_pack.number != PIC12_IDLOCS_SIZE) {
      fprintf(stderr, "The IDLOCS size is not %u words!\n", PIC12_IDLOCS_SIZE);
    }

    word  = (idlocs_pack.words[0] & 0x000F) << 12;
    word |= (idlocs_pack.words[1] & 0x000F) << 8;
    word |= (idlocs_pack.words[2] & 0x000F) << 4;
    word |= (idlocs_pack.words[3] & 0x000F);
    _ux_print(true, "\n"
                    "        __idlocs 0x%04X", word);
  }
}

/*------------------------------------------------------------------------------------------------*/

static bool
_need_section_print(const lset_section_t *Section)
{
  unsigned i;

  for (i = 0; i < Section->symbol_number; ++ i) {
    if (Section->symbol_table[i]->attr & CSYM_USED) {
      return true;
    }
  }

  return false;
}

/*------------------------------------------------------------------------------------------------*/

static void
_list_user_labels(int Addr_digits)
{
  unsigned          i;
  const lset_section_t *sect;
  const lset_symbol_t  *sym;
  size_t                length;
  char                  buffer[BUFSIZ];

  if (((sect = state.lset_root.sections[SECT_SPEC_DATA])) && (sect->symbol_table)) {
    if (_need_section_print(sect)) {
      printf("\n;%s\n; DATA address definitions\n\n", border);
      for (i = 0; i < sect->symbol_number; ++i) {
        sym = sect->symbol_table[i];

        if (sym->attr & CSYM_USED) {
          length = snprintf(buffer, sizeof(buffer), "%-*s equ     0x%0*lX",
                            USER_LABEL_ALIGN, sym->name, Addr_digits, sym->start);

          if (sym->attr & CSYM_END) {
            gp_exclamation(buffer, sizeof(buffer), length, "; size: %li bytes", sym->end - sym->start + 1);
          }

          _ux_print(true, "%s", buffer);
        }
      }
    }
  }

  if (((sect = state.lset_root.sections[SECT_SPEC_EEDATA])) && (sect->symbol_table)) {
    if (_need_section_print(sect)) {
      _ux_print(true, "\n"
                      ";%s\n"
                      "; EEDATA address definitions\n", border);
      for (i = 0; i < sect->symbol_number; ++i) {
        sym = sect->symbol_table[i];

        if (sym->attr & CSYM_USED) {
          length = snprintf(buffer, sizeof(buffer), "%-*s equ     (__EEPROM_START + 0x%0*lX)",
                            USER_LABEL_ALIGN, sym->name, Addr_digits, sym->start);

          if (sym->attr & CSYM_END) {
            gp_exclamation(buffer, sizeof(buffer), length, "; size: %li bytes", sym->end - sym->start + 1);
          }

          _ux_print(true, "%s", buffer);
        }
      }
    }
  }
}

/*------------------------------------------------------------------------------------------------*/

static void
_add_constant(lset_section_t *Section, const char *Name, long Start, unsigned Attr)
{
  if (lset_symbol_find_addr(Section, Start, -1, false)) {
    return;
  }

  lset_symbol_new(Section, Name, Start, -1, CSYM_START | Attr, 0);
}

/*------------------------------------------------------------------------------------------------*/

static void
_load_processor_constants(void)
{
  pic_processor_t  processor;
  lset_section_t  *sect;
  const int       *pair;
  int              addr;
  const vector_t  *vec;
  unsigned     num;
  char             buf[BUFSIZ];

  processor = state.processor;

  /* The code section. */

  if ((sect = state.lset_root.sections[SECT_SPEC_CODE]) == NULL) {
    sect = lset_section_new(&state.lset_root, "CODE", 0);
  }

  if (sect) {
    addr = processor->prog_mem_size;

    if (addr > 0) {
      _add_constant(sect, "__CODE_START",        0, CSYM_ORG);
      _add_constant(sect, "__CODE_END",   addr - 1, CSYM_ORG);
    }

    if ((pair = gp_processor_idlocs_exist(processor))) {
      lset_symbol_new(sect, "__IDLOCS_START", pair[0], -1, CSYM_START, 0);
      lset_symbol_new(sect, "__IDLOCS_END",   pair[1], -1, CSYM_END,   0);
    }

    if ((pair = gp_processor_config_exist(processor))) {
      lset_symbol_new(sect, "__CONFIG_START", pair[0], -1, CSYM_START, 0);
      lset_symbol_new(sect, "__CONFIG_END",   pair[1], -1, CSYM_END,   0);
    }

    if ((processor->pclass->vector_table) || (processor->pclass->vector_number > 0)) {
      vec = processor->pclass->vector_table;
      num = processor->pclass->vector_number;

      for (; num; ++vec, --num) {
        buf[0] = '_';
        buf[1] = '_';
        gp_stptoupper(&buf[2], vec->name, sizeof(buf) - 2);

        if (vec->address < 0) {
          /* This a SX type processor. */
          addr = processor->prog_mem_size;

          if (addr > 0) {
            --addr;
          }
        }
        else {
          addr = vec->address;
        }

        _add_constant(sect, buf, addr, CSYM_ORG);
      }
    }
  } /* if (sect) */

  /* The data section. */

  if ((sect = state.lset_root.sections[SECT_SPEC_DATA]) == NULL) {
    sect = lset_section_new(&state.lset_root, "DATA", 0);
  }

  if (sect) {
    if ((pair = gp_processor_common_ram_exist(processor))) {
      if (lset_symbol_find_addr(sect, pair[0], pair[1], false) == NULL) {
        lset_symbol_new(sect, "Common_RAM", pair[0], pair[1], CSYM_START | CSYM_END, 0);
      }
    }

    if ((pair = gp_processor_linear_ram_exist(processor))) {
      if (lset_symbol_find_addr(sect, pair[0], pair[1], false) == NULL) {
        lset_symbol_new(sect, "Linear_RAM", pair[0], pair[1], CSYM_START | CSYM_END, 0);
      }
    }
  }

  /* The eedata section. */

  if ((sect = state.lset_root.sections[SECT_SPEC_EEDATA]) == NULL) {
    sect = lset_section_new(&state.lset_root, "EEDATA", 0);
  }

  if (sect) {
    if ((pair = gp_processor_eeprom_exist(processor))) {
      _add_constant(sect, "__EEPROM_START", pair[0], CSYM_ORG);
      _add_constant(sect, "__EEPROM_END",   pair[1], CSYM_ORG);
    }
  }

  lset_sections_choose(&state.lset_root);
  lset_section_make_symbol_tables(&state.lset_root);
  lset_section_check_bounds(&state.lset_root);
}

/*------------------------------------------------------------------------------------------------*/

static void
_dasm(MemBlock_t *Memory)
{
  MemBlock_t          *m;
  int                  i;
  int                  maximum;
  int                  org;
  int                  offset;
  int                  insn_size;
  int                  last_loc;
  int                  num_words;
  int                  behavior;
  int                  bsr_boundary;
  uint16_t             data;
  uint8_t              byte;
  unsigned         type;
  const char          *label_name;
  int                  addr_digits;
  int                  word_digits;
  size_t               length;
  const lset_symbol_t *sym;
  char                 buffer[BUFSIZ];

  if (state.show_names && ((state.pclass == PROC_CLASS_PIC12)   ||
                           (state.pclass == PROC_CLASS_PIC12E)  ||
                           (state.pclass == PROC_CLASS_PIC12I)  ||
                           (state.pclass == PROC_CLASS_SX)      ||
                           (state.pclass == PROC_CLASS_PIC14)   ||
                           (state.pclass == PROC_CLASS_PIC14E)  ||
                           (state.pclass == PROC_CLASS_PIC14EX) ||
                           (state.pclass == PROC_CLASS_PIC16)   ||
                           (state.pclass == PROC_CLASS_PIC16E))) {
    if (state.pclass == PROC_CLASS_PIC16E) {
      _mark_false_addresses(Memory);
    }

    _recognize_labels_and_spec_words(Memory);
    _recognize_registers(Memory);
    _denominate_labels(Memory);
  }

  bsr_boundary = gp_processor_bsr_boundary(state.processor);
  addr_digits  = state.pclass->addr_digits;
  word_digits  = state.pclass->word_digits;

  _write_header();

  if (state.show_names) {
    _ux_print(true, "\n"
                    "; The recognition of labels and registers is not always good, therefore\n"
                    "; be treated cautiously the results.");
    if (state.need_sfr_equ) {
      _write_core_sfr_list();
    }
  }

  if (!state.format && state.show_config) {
    _show_config();
    _show_idlocs();
  }

  if (state.show_names) {
    behavior = GPDIS_SHOW_NAMES | GPDIS_SHOW_BYTES | GPDIS_SHOW_EXCLAMATION;

    if (state.show_fsrn) {
      behavior |= GPDIS_SHOW_FSRN;
    }

    _list_user_labels(addr_digits);
  }
  else {
    behavior = GPDIS_SHOW_NOTHING;
  }

  m = Memory;
  last_loc = 0;
  while (m) {
    i = IMemAddrFromBase(m->base);
    maximum = i + I_MEM_MAX;

    insn_size = 2;
    while (i < maximum) {
      org = gp_processor_insn_from_byte_p(state.processor, i);

      if ((offset = gp_processor_is_idlocs_org(state.processor, org)) >= 0) {
        /* This is idlocs word/bytes. Not need disassemble. */
        if (state.pclass == PROC_CLASS_PIC16E) {
          if (!state.show_config) {
            if (gp_mem_b_get(m, i, &byte, NULL, NULL)) {
              if (last_loc != (i - insn_size)) {
                if (state.show_names && (offset == 0)) {
                  _ux_print(true, "\n"
                                  ";%s\n"
                                  "; IDLOCS area", border);
                }

                _write_org(org, addr_digits, "idlocs", NULL, 0);
              }

              last_loc = i;

              if (state.format) {
                length = snprintf(buffer, sizeof(buffer), "%0*x:  %02x  ",
                                  addr_digits, org, (unsigned)byte);
              }
              else {
                length = snprintf(buffer, sizeof(buffer), "        ");
              }

              _byte_exclamation(buffer, sizeof(buffer), length, byte);
              _ux_print(true, "%s", buffer);
            }
            else {
              last_loc = 0;
            }
          }

          insn_size = 1;
        } /* if (state.pclass == PROC_CLASS_PIC16E) */
        else {
          if (!state.show_config) {
            if (state.pclass->i_memory_get(m, i, &data, NULL, NULL)) {
              if (last_loc != (i - insn_size)) {
                sym = lset_symbol_find_addr(state.lset_root.sections[SECT_SPEC_CODE], org, -1, true);

                if ((sym) && (sym->attr & CSYM_ORG)) {
                  _write_org(org, addr_digits, "idlocs", sym->name, 0);
                }
                else {
                  _write_org(org, addr_digits, "idlocs", NULL, 0);
                }
              }

              last_loc = i;

              if (state.format) {
                length = snprintf(buffer, sizeof(buffer), "%0*x:  %0*x  ",
                                  addr_digits, org, word_digits, (unsigned)data);
              }
              else {
                length = snprintf(buffer, sizeof(buffer), "        ");
              }

              length += snprintf(buffer + length, sizeof(buffer) - length, "%-*s0x%0*x",
                                 TABULATOR_SIZE, "dw", word_digits, (unsigned)data);

              if (state.processor->idlocs_mask != 0) {
                unsigned tmp = (~state.processor->idlocs_mask) & data;

                gp_exclamation(buffer, sizeof(buffer), length, "; in fact: 0x%0*x", word_digits, tmp);
              }

              _ux_print(true, "%s", buffer);
            }
            else {
              last_loc = 0;
            }
          }

          insn_size = 2;
        }
      } /* if (gp_processor_is_idlocs_org(state.processor, org) >= 0) */
      else if ((offset = gp_processor_is_config_org(state.processor, org)) >= 0) {
        /* This is config word/bytes. Not need disassemble. */
        if (state.pclass == PROC_CLASS_PIC16E) {
          if (!state.show_config) {
            if (gp_mem_b_get(m, i, &byte, NULL, NULL)) {
              if (last_loc != (i - insn_size)) {
                if (state.show_names && (offset == 0)) {
                  _ux_print(true, "\n"
                                  ";%s\n"
                                  "; CONFIG Bits area", border);
                }

                _write_org(org, addr_digits, "config", NULL, 0);
              }

              last_loc = i;

              if (state.format) {
                _ux_print(false, "%0*x:  %02x  ", addr_digits, org, (unsigned)byte);
              }
              else {
                _ux_print(false, "        ");
              }

              _ux_print(true, "%-*s0x%02x", TABULATOR_SIZE, "db", (unsigned)byte);
            }
            else {
              last_loc = 0;
            }
          }

          insn_size = 1;
        } /* if (state.pclass == PROC_CLASS_PIC16E) */
        else {
          if (!state.show_config) {
            if (state.pclass->i_memory_get(m, i, &data, NULL, NULL)) {
              if (last_loc != (i - insn_size)) {
                if (state.show_names && (offset == 0)) {
                  _ux_print(true, "\n"
                                  ";%s\n"
                                  "; CONFIG Bits area", border);
                }

                _write_org(org, addr_digits, "config", NULL, 0);
              }

              last_loc = i;

              if (state.format) {
                _ux_print(false, "%0*x:  %0*x  ", addr_digits, org, word_digits, (unsigned)data);
              }
              else {
                _ux_print(false, "        ");
              }

              _ux_print(true, "%-*s0x%0*x", TABULATOR_SIZE, "dw", word_digits, (unsigned)data);
            }
            else {
              last_loc = 0;
            }
          }

          insn_size = 2;
        }
      } /* else if (gp_processor_is_config_org(state.processor, org) >= 0) */
      else if ((offset = gp_processor_is_eeprom_org(state.processor, org)) >= 0) {
        if (gp_mem_b_get(m, i, &byte, NULL, &label_name)) {
          if (last_loc != (i - insn_size)) {
            sym = lset_symbol_find_addr(state.lset_root.sections[SECT_SPEC_EEDATA], org, -1, true);

            if (state.show_names && (offset == 0)) {
              _ux_print(true, "\n"
                              ";%s\n"
                              "; EEDATA area", border);
            }

            if ((sym) && (sym->attr & CSYM_ORG)) {
              _write_org(org, addr_digits, "eeprom", sym->name, 0);
            }
            else {
              sym = lset_symbol_find_addr(state.lset_root.sections[SECT_SPEC_EEDATA],
                                          state.processor->eeprom_addrs[0],
                                          state.processor->eeprom_addrs[1], true);

              if ((sym) && (sym->attr & CSYM_ORG)) {
                _write_org(org, addr_digits, "eeprom", sym->name, offset);
              }
              else {
                _write_org(org, addr_digits, "eeprom", NULL, offset);
              }
            }
          }

          last_loc = i;

          if (state.show_names && (label_name)) {
            length = snprintf(buffer, sizeof(buffer), "%s", label_name);
            gp_exclamation(buffer, sizeof(buffer), length, "; address: 0x%0*x", addr_digits, org);

            if (! prev_empty_line) {
              _ux_print(true, "");
            }

            _ux_print(true, "%s\n", buffer);
            prev_empty_line = true;
          }

          if (state.format) {
            length = snprintf(buffer, sizeof(buffer), "%0*x:  %02x    ",
                              addr_digits, org, (unsigned)byte);
          }
          else {
            length = snprintf(buffer, sizeof(buffer), "        ");
          }

          _byte_exclamation(buffer, sizeof(buffer), length, byte);
          _ux_print(true, "%s", buffer);
          prev_empty_line = false;
        }
        else {
          last_loc = 0;
        }

        insn_size = 1;
      } /* else if (gp_processor_is_eeprom_org(state.processor, org) >= 0) */
      else {
        /* This is program word. */
        if (state.pclass->i_memory_get(m, i, &data, NULL, &label_name) == W_USED_ALL) {
          if (last_loc != (i - insn_size)) {
            sym = lset_symbol_find_addr(state.lset_root.sections[SECT_SPEC_CODE], org, -1, true);

            if (state.show_names && (org == 0)) {
              _ux_print(true, "\n"
                              ";%s\n"
                              "; CODE area", border);
            }

            if ((sym) && (sym->attr & CSYM_ORG)) {
              _write_org(org, addr_digits, "code", sym->name, 0);
            }
            else {
              _write_org(org, addr_digits, "code", NULL, 0);
            }
          }

          last_loc = i;

          if (state.show_names && (label_name)) {
            length = snprintf(buffer, sizeof(buffer), "%s:", label_name);
            gp_exclamation(buffer, sizeof(buffer), length, "; address: 0x%0*x", addr_digits, org);

            if (! prev_empty_line) {
              _ux_print(true, "");
            }

            _ux_print(true, "%s\n", buffer);
            prev_empty_line = true;
          }

          if (state.format) {
            length = snprintf(buffer, sizeof(buffer), "%0*x:  %0*x  ",
                              addr_digits, org, word_digits, (unsigned)data);
          }
          else {
            length = snprintf(buffer, sizeof(buffer), "        ");
          }

          type = gp_mem_b_get_type(m, i);

          if (type & W_CONST_DATA) {
            gp_disassemble_show_data(m, i, state.pclass, behavior, buffer, sizeof(buffer), length);
            _ux_print(true, "%s", buffer);
            prev_empty_line = false;
            num_words = 1;
          }
          else {
            num_words = gp_disassemble(m, i, state.pclass, bsr_boundary, state.processor->prog_mem_size,
                                       behavior, buffer, sizeof(buffer), length);
            _ux_print(true, "%s", buffer);
            prev_empty_line = false;

            if (num_words != 1) {
              /* Some 18xx instructions use two words. */
              if (state.format) {
                state.pclass->i_memory_get(m, i + 2, &data, NULL, NULL);
                _ux_print(true, "%0*x:  %0*x", addr_digits, gp_processor_insn_from_byte_p(state.processor, i + 2),
                          word_digits, (unsigned)data);
                prev_empty_line = false;
              }

              insn_size = 4;
            }
            else {
              insn_size = 2;
            }
          }
        }
      }

      i += insn_size;
    }

    m = m->next;
  }

  _end_asm();
}

/*------------------------------------------------------------------------------------------------*/
static bool
  strict_options,
  print_hex_info,
  memory_dump,
  strict,
  usage;
static const char*label_list_name;
static const char*filename;

static void _stdcall onOption(void*, char c, const char*arg) {
  if (strict_options) gp_checkarg(arg,longopts);
  switch (c) {
    case '?':
    case 'h': usage = true;			break;
    case 'c': gp_decode_mnemonics = true;	break;
    case 'i': print_hex_info = true;		break;
    case 'j': state.show_fsrn = true;		break;
    case 'k': label_list_name = arg;		break;
    case 'l':
      gp_dump_processor_list(true, PROC_CLASS_UNKNOWN, PROC_CLASS_UNKNOWN, PROC_CLASS_UNKNOWN);
      exit(0);
    case 'm': memory_dump = true;		break;
    case 'n': state.show_names = true;		break;
    case 'o': state.show_config = true;		break;
    case 'p': processor_name = arg;		break;
    case 's': state.format = 0;			break;
    case 't': state.use_tab = true;		break;
    case 'v':
      fprintf(stderr, "%s\n", GPDASM_VERSION_STRING);
      exit(0);
    case 'y': gp_decode_extended = true;	break;
    case 2: strict = true;			break;
    case 1: gp_checkarg(label_list_name,longopts);
	    gp_checkarg(processor_name,longopts);
	    gp_checkarg(filename,longopts);
            strict_options = true;		break;
    case 0: filename = arg;			break;
  }
}

int _cdecl main(int argc, char *argv[]) {
  const int  *pair;

  gp_init();

  state.pclass=PROC_CLASS_GENERIC;	// 12 bit device
  state.format=1;			// output format
  state.i_memory = gp_mem_i_create();

  gp_getopt(argv,longopts,onOption,0);
  if (!filename || usage) _show_usage(*argv);

  _select_processor();

  state.hex_info = gp_readhex(filename, state.i_memory);

  if (state.hex_info->error) state.num.errors++;

  if (strict && (state.pclass) && (state.pclass->patch_strict)) {
    state.pclass->patch_strict();
  }

  if (print_hex_info) {
    printf("hex file name:   %s\n", filename);
    printf("hex file format: ");

    if (state.hex_info->hex_format == INHX8M) {
      printf("inhx8m\n");
    }
    else if (state.hex_info->hex_format == INHX16) {
      printf("inhx16\n");
    }
    else if (state.hex_info->hex_format == INHX32) {
      printf("inhx32\n");
    }
    else {
      printf("UNKNOWN\n");
    }

    printf("number of bytes: %i\n\n", state.hex_info->size);
  }

  if (label_list_name) {
    open_label_source(label_list_name);
    lset_init(&state.lset_root, label_list_name);
    yyparse();
    close_label_source();
    lset_sections_choose(&state.lset_root);
    lset_section_make_symbol_tables(&state.lset_root);
    lset_section_check_bounds(&state.lset_root);

    lset_symbol_check_absolute_limits(state.lset_root.sections[SECT_SPEC_CODE],
                                      0, state.processor->prog_mem_size - 1);

    if (state.pclass == PROC_CLASS_PIC16E) {
      lset_symbol_check_align(state.lset_root.sections[SECT_SPEC_CODE], 2);
    }

    if (state.lset_root.sections[SECT_SPEC_EEDATA]) {
      if ((pair = gp_processor_eeprom_exist(state.processor))) {
        lset_symbol_check_absolute_limits(state.lset_root.sections[SECT_SPEC_EEDATA],
                                          0, pair[1] - pair[0]);
      }
      else {
        fprintf(stderr, "Error: The processor does not have EEPROM.\n");
        exit(1);
      }
    }
  }
  else {
    lset_init(&state.lset_root, NULL);
  }

  if (state.num.errors == 0) {
    if (memory_dump) {
      gp_mem_i_print(state.i_memory, state.processor);
    }
    else {
      _load_processor_constants();
      _dasm(state.i_memory);
    }
  }

  lset_delete(&state.lset_root);
  gp_mem_i_free(state.i_memory);

  return ((state.num.errors > 0) ? EXIT_FAILURE : EXIT_SUCCESS);
}
Detected encoding: UTF-80