Source file: /~heha/hs/gputils64-210929.zip/gpasm/lst.cpp

/* ".LST" file output for gpasm
   Copyright 1998-2005	James Bowman, Craig Franklin
   Copyright 2012	Borut Ražem
   Copyright 2016	Molnár Károly
*/

#include "stdhdr.h"

#include "libgputils.h"
#include "directive.h"
#include "gpasm.h"
#include "cod.h"
#include "coff.h"
#include "lst.h"

#define STRINGIFY(s)                    _str(s)
#define _str(s)                         #s

#define IS_EEPROM                       ((IS_EEPROM8) || (IS_EEPROM16))
#define IS_BYTE                         ((IS_PIC16E_CORE) || (IS_EEPROM))

#define MEM_USED_CHAR                   'X'
#define MEM_UNUSED_CHAR                 '-'

#define MAC_PARM_STR_MAX_LENGTH         256

typedef struct {
  const symbol_t *sym;
  enum lst_sym_type_e {
    LST_SYMBOL,
    LST_DEFINITION,
    LST_MACRO
  } type;
} lst_symbol_t;

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

static void
_lst_check_page_start(void)
{
  if ((state.lst.lines_per_page != 0) &&
      ((state.lst.line_of_page == 0) || (state.lst.line_of_page > state.lst.lines_per_page))) {
    lst_page_start();
  }
}

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

static unsigned
_lst_spaces(unsigned Num)
{
  unsigned i = Num;

  _lst_check_page_start();
  while (i-- != 0) {
    putc(' ', state.lst.f);
  }

  return Num;
}

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

static void
_lst_eol(void)
{
  if (state.lst.f) {
    putc('\n', state.lst.f);
    state.lst.line_number++;
    state.lst.line_of_page++;
    cod_lst_line(COD_NORMAL_LST_LINE);
  }
}

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

/* Print part of a line. Output must not contain newline. Needs call to lst_eol at end of line. */

static unsigned _cdecl _lst_printf(const char *Format, ...) {
  int     r = 0;
  va_list args;

  if (state.lst.f) {
    _lst_check_page_start();
    va_start(args, Format);
    r = vfprintf(state.lst.f, Format, args);
    va_end(args);
  }

  assert(r >= 0);
  return (unsigned)r;
}

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

static void
_lst_putc(char Ch)
{
  if (state.lst.f) {
    putc(Ch, state.lst.f);
  }
}

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

/* find relocation by address */

static gp_reloc_t * _find_reloc_by_address(unsigned Address) {
  gp_reloc_t *p;

  for (p = state.obj.section->relocation_list.first; p; p = p->next) {
    if (p->address == Address) {
      break;
    }
  }

  return p;
}

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

/* get previous relocation type */

static uint16_t
_prev_reloc_type(void)
{
  gp_reloc_t *p;

  if (state.obj.section->relocation_list.first == state.obj.section->relocation_list.last) {
    return 0;
  }

  p = state.obj.section->relocation_list.last->prev;
  assert(p);
  return ((p->address == p->next->address) ? p->type : 0);
}

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

/* print word value with undefined nibbles replaced by "?" */
/* enable assertions
 * #define DO_ASSERT */
/* disable assertions */
#undef DO_ASSERT

#ifdef DO_ASSERT
#define ASSERT(expr)    assert(expr)
#else
#define ASSERT(expr)    ((void)0)
#endif

static unsigned
_print_reloc(uint16_t Type, uint16_t Current_value, unsigned Index)
{
  proc_class_t pclass = state.device.pclass;

  switch (Type) {
    case RELOC_CALL: {
      if ((pclass == PROC_CLASS_PIC12) || (pclass == PROC_CLASS_PIC12E) || (pclass == PROC_CLASS_PIC12I) ||
          (pclass == PROC_CLASS_SX)    || (pclass == PROC_CLASS_PIC16E)) {
        ASSERT((Current_value & PIC12_BMSK_CALL) == 0);
        return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8);
      }
      else if ((pclass == PROC_CLASS_PIC14) || (pclass == PROC_CLASS_PIC14E) || (pclass == PROC_CLASS_PIC14EX)) {
        ASSERT((Current_value & PIC14_BMSK_CALL) == 0);
        return _lst_printf("%X??? ", (Current_value & 0xf000) >> 12);
      }
      else if (pclass == PROC_CLASS_PIC16) {
        ASSERT((Current_value & PIC16_BMSK_CALL) == 0);
        return _lst_printf("???? ");
      }
      else if (pclass == PROC_CLASS_PIC16E) {
        ASSERT((Current_value & PIC16E_BMSK_CALL0) == 0);
        return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8);
      }
      else {
        ASSERT(0);
        return 0;
      }

      break;
    }

    /*..................................*/

    case RELOC_GOTO: {
      if ((pclass == PROC_CLASS_PIC12) || (pclass == PROC_CLASS_PIC12E) || (pclass == PROC_CLASS_PIC12I) ||
          (pclass == PROC_CLASS_SX)) {
        ASSERT((Current_value & PIC12_BMSK_GOTO) == 0);
        return _lst_printf("%X??? ", (Current_value & 0xf000) >> 12);
      }
      else if ((pclass == PROC_CLASS_PIC14) || (pclass == PROC_CLASS_PIC14E) || (pclass == PROC_CLASS_PIC14EX)) {
        ASSERT((Current_value & PIC14_BMSK_GOTO) == 0);
        return _lst_printf("%X??? ", (Current_value & 0xf000) >> 12);
      }
      else if (pclass == PROC_CLASS_PIC16) {
        ASSERT((Current_value & PIC16_BMSK_GOTO) == 0);
        return _lst_printf("???? ");
      }
      else if (pclass == PROC_CLASS_PIC16E) {
        ASSERT((Current_value & PIC16E_BMSK_GOTO0) == 0);
        return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8);
      }
      else {
        ASSERT(0);
        return 0;
      }

      break;
    }

    /*..................................*/

    case RELOC_LOW:
      /* On non-16bits devices DB directive generates values with
       * low byte != 0 if more then one byte is defined, for example
       * DB 0x12, 0x34 generates 0x1234, so the following assertion fails
       * in such cases.
       * TODO: This should be solved in DB directive handling function
       * do_db(), file directive.c.
       * ASSERT((Current_value & 0xff) == 0); */
      return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8);
      break;

    /*..................................*/

    case RELOC_HIGH:
    case RELOC_LFSR2:
    case RELOC_UPPER:
    case RELOC_CONDBRA:
      ASSERT((Current_value & 0xff) == 0);
      return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8);
      break;

    /*..................................*/

    case RELOC_P: {
      char buf[5] = "????";

      if (RELOC_F != _prev_reloc_type()) {
        sprintf(&buf[2], "%02X", Current_value & 0x00ff);
      }

      ASSERT((Current_value & 0x1f00) == 0);
      return _lst_printf("%s ", buf);
      break;
    }

    /*..................................*/

    case RELOC_BANKSEL: {
      if (pclass == PROC_CLASS_PIC12) {
        ASSERT((Current_value & PIC12_BMSK_BxF) == 0);  /* 04A4-04E4 or 05A4-05E4 */
        return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8);
      }
      else if ((pclass == PROC_CLASS_PIC12E) || (pclass == PROC_CLASS_PIC12I)) {
        ASSERT((Current_value & PIC12E_BMSK_MOVLB) == 0);
        return _lst_printf("%03X? ", (Current_value & 0x0fff0) >> 4);
      }
      else if (pclass == PROC_CLASS_PIC14) {
        ASSERT((Current_value & PIC14_BMSK_BxF) == 0);  /* 1283-1303 or 1683-1703 */
        return _lst_printf("%X??? ", (Current_value & 0xf000) >> 12);
      }
      else if (pclass == PROC_CLASS_PIC14E) {
        ASSERT((Current_value & PIC14E_BMSK_MOVLB) == 0);  /* 0020-003F */
        return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8);
      }
      else if (pclass == PROC_CLASS_PIC14EX) {
        ASSERT((Current_value & PIC14EX_BMSK_MOVLB) == 0);  /* 0140-017F */
        return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8);
      }
      else if (pclass == PROC_CLASS_PIC16) {
        ASSERT((Current_value & PIC16_BMSK_MOVLB) == 0);  /* 0100-010F */
        return _lst_printf("%03X? ", (Current_value & 0xfff0) >> 4);
      }
      else if (pclass == PROC_CLASS_PIC16E) {
        ASSERT((Current_value & PIC16E_BMSK_MOVLB) == 0);  /* 0100-010F */
        return _lst_printf("%03X? ", (Current_value & 0xfff0) >> 4);
      }
      return _lst_printf("???? ");
      break;
    }

    /*..................................*/

    case RELOC_ALL:
      return _lst_printf("???? ");
      break;

    /*..................................*/

    case RELOC_IBANKSEL: {
      if (pclass == PROC_CLASS_PIC14) {
        ASSERT((Current_value & PIC12_BMSK_BxF) == 0);
        return _lst_printf("1?83 ");  /* 1383 or 1783 */
      }
      else if ((pclass == PROC_CLASS_PIC14E) || (pclass == PROC_CLASS_PIC14EX)) {
        ASSERT((Current_value & PIC12_BMSK_BxF) == 0);
        return _lst_printf("%X??? ", (Current_value & 0xf000) >> 12);  /* 120A-118A or 160A-158A */
      }
      else if (pclass == PROC_CLASS_PIC16) {
        ASSERT((Current_value & 0x00ff) == 0);
        return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8);
      }
      else {
        ASSERT(0);
        return 0;
      }

      break;
    }

    /*..................................*/

    case RELOC_F: {
      if (pclass == PROC_CLASS_SX) {
        ASSERT((Current_value & 0x0007) == 0);
        return _lst_printf("%03X? ", (Current_value & 0xfff0) >> 4);
      }
      else if ((pclass == PROC_CLASS_PIC12) || (pclass == PROC_CLASS_PIC12E) || (pclass == PROC_CLASS_PIC12I)) {
        ASSERT((Current_value & MASK_PIC12_FILE) == 0);
      }
      else if ((pclass == PROC_CLASS_PIC14) || (pclass == PROC_CLASS_PIC14E) || (pclass == PROC_CLASS_PIC14EX)) {
        ASSERT((Current_value & MASK_PIC14_FILE) == 0);
      }
      else if ((pclass == PROC_CLASS_PIC16) || (pclass == PROC_CLASS_PIC16E)) {
        ASSERT((Current_value & MASK_PIC16_FILE) == 0);
      }
      else {
        ASSERT(0);
        return 0;
      }
      return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8);
      break;
    }

    /*..................................*/

    case RELOC_TRIS_3BIT:
      ASSERT((Current_value & PIC12_BMSK_TRIS) == 0);
      return _lst_printf("%03X? ", (Current_value & 0x00f0) >> 4);
      break;

    /*..................................*/

    case RELOC_TRIS:
      ASSERT((Current_value & 0x001f) == 0);
      return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8);
      break;

    /*..................................*/

    case RELOC_MOVLR: {
      if (pclass == PROC_CLASS_PIC16) {
        ASSERT((Current_value & PIC16_BMSK_MOVLR) == 0);
        return _lst_printf("%02X?%X ", (Current_value & 0xff00) >> 8, Current_value & 0x000f);  /* 010.-01F. */
      }
      else {
        ASSERT(0);
        return 0;
      }

      break;
    }

    /*..................................*/

    case RELOC_MOVLB: {
      if ((pclass == PROC_CLASS_PIC12E) || (pclass == PROC_CLASS_PIC12I)) {
        ASSERT((Current_value & PIC12E_BMSK_MOVLB) == 0);
        return _lst_printf("%03X? ", (Current_value & 0xfff0) >> 4);
      }
      else if (pclass == PROC_CLASS_PIC14E) {
        ASSERT((Current_value & PIC14E_BMSK_MOVLB) == 0);
        return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8);
      }
      else if (pclass == PROC_CLASS_PIC14EX) {
        ASSERT((Current_value & PIC14EX_BMSK_MOVLB) == 0);
        return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8);
      }
      else if (pclass == PROC_CLASS_PIC16) {
        ASSERT((Current_value & PIC16_BMSK_MOVLB) == 0);
        return _lst_printf("%03X? ", (Current_value & 0xfff0) >> 4);  /* 01.0-01.F */
      }
      else if (pclass == PROC_CLASS_PIC16E) {
        ASSERT((Current_value & PIC16E_BMSK_MOVLB) == 0);
        return _lst_printf("%03X? ", (Current_value & 0xfff0) >> 4);
      }
      else {
        ASSERT(0);
        return 0;
      }

      break;
    }

    /*..................................*/

    case RELOC_GOTO2:
      /* This is only used for PIC16E (pic18). */
    case RELOC_FF1:
    case RELOC_FF2:
      /* removed assertion since it fails during sdcc pic16 library
       * compilation: do_insn, case INSN_CLASS_FF
       * ASSERT((Current_value & 0x0fff) == 0); */
      return _lst_printf("%X??? ", (Current_value & 0xf000) >> 12);
      break;

    /*..................................*/

    case RELOC_LFSR1:
      return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8);
      break;

    /*..................................*/

    case RELOC_BRA: {
      if ((pclass == PROC_CLASS_PIC14E) || (pclass == PROC_CLASS_PIC14EX)) {
        ASSERT((Current_value & PIC14E_BMSK_RBRA9) == 0);
      }
      else if (pclass == PROC_CLASS_PIC16E) {
        ASSERT((Current_value & PIC16E_BMSK_RBRA11) == 0);
      }
      else {
        ASSERT(0);
        return 0;
      }
      return _lst_printf("%X??? ", (Current_value & 0xf000) >> 12);
      break;
    }

    /*..................................*/

    case RELOC_ACCESS: {
      if (pclass == PROC_CLASS_PIC16E) {
        ASSERT((Current_value & 0x00ff) == 0);
        return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8);
      }
      else {
        ASSERT(0);
        return 0;
      }
      break;
    }

    /*..................................*/

    case RELOC_PAGESEL_WREG: {
      if (Index == 0) {
        /* movlw PAGE_VALUE */
        if ((pclass == PROC_CLASS_PIC12) || (pclass == PROC_CLASS_PIC12E) || (pclass == PROC_CLASS_PIC12I)) {
          ASSERT((Current_value & PIC12_BMSK_MOVLW) == 0);
          return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8); /* 0C00 -- movlw ... */
        }
        else if (pclass == PROC_CLASS_PIC14) {
          ASSERT((Current_value & PIC14_BMSK_MOVLW) == 0);
          return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8); /* 3000 -- movlw ... */
        }
        else {
          ASSERT(0);
          return 0;
        }
      }
      else {
        /* movwf register */
        if ((pclass == PROC_CLASS_PIC12) || (pclass == PROC_CLASS_PIC12E) || (pclass == PROC_CLASS_PIC12I)) {
          ASSERT((Current_value & PIC12_BMSK_FILE) == 0);
          return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8); /* 0023 -- movwf STATUS */
        }
        else if (pclass == PROC_CLASS_PIC14) {
          ASSERT((Current_value & PIC14_BMSK_FILE) == 0);
          return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8); /* 008A -- movwf PCLATH */
        }
        else {
          ASSERT(0);
          return 0;
        }
      }

      break;
    }

    /*..................................*/

    case RELOC_PAGESEL_BITS: {
      if ((pclass == PROC_CLASS_PIC12) || (pclass == PROC_CLASS_PIC12E) || (pclass == PROC_CLASS_PIC12I)) {
        ASSERT((Current_value & PIC12_BMSK_BxF) == 0);
        return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8);  /* 04A3-04E3 or 05A3-05E3 */
      }
      else if (pclass == PROC_CLASS_PIC14) {
        ASSERT((Current_value & PIC14_BMSK_BxF) == 0);
        return _lst_printf("%X??? ", (Current_value & 0xf000) >> 12);  /* 120A-118A or 160A-158A */
      }
      else {
        ASSERT(0);
        return 0;
      }

      break;
    }

    /*..................................*/

    case RELOC_PAGESEL_MOVLP: {
      if ((pclass == PROC_CLASS_PIC14E) || (pclass == PROC_CLASS_PIC14EX)) {
        ASSERT((Current_value & PIC14E_BMSK_MOVLP) == 0);
        return _lst_printf("%02X?? ", (Current_value & 0xff00) >> 8);
      }
      else {
        ASSERT(0);
        return 0;
      }

      break;
    }

    /*..................................*/

    /* unimplemented relocations */
    case RELOC_PAGESEL:
    case RELOC_SCNSZ_LOW:
    case RELOC_SCNSZ_HIGH:
    case RELOC_SCNSZ_UPPER:
    case RELOC_SCNEND_LOW:
    case RELOC_SCNEND_HIGH:
    case RELOC_SCNEND_UPPER:
    case RELOC_SCNEND_LFSR1:
    case RELOC_SCNEND_LFSR2:
    default:
      return 0;
  }
}

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

static unsigned
_lst_data(unsigned Position, MemBlock_t *M, unsigned Byte_addr, unsigned Bytes_emitted,
          uint16_t Reloc_type)
{
  uint8_t      emit_byte;
  uint16_t     emit_word;
  unsigned start_addr;
  unsigned lst_bytes;
  bool   is_eeprom_area;
  unsigned n;

  lst_bytes      = 0;
  is_eeprom_area = (gp_processor_is_eeprom_byte_addr(state.processor, Byte_addr) >= 0) ? true : false;
  /* When in a idata or byte packed section or eeprom area, print byte by byte. */
  if (IS_EEPROM8 || is_eeprom_area || (state.obj.new_sect_flags & (STYP_DATA | STYP_BPACK))) {
    while ((Bytes_emitted > lst_bytes) && ((Position + 3) <= LST_LINENUM_POS)) {
      gp_mem_b_get(M, Byte_addr, &emit_byte, NULL, NULL);
      Position += _lst_printf("%02X ", emit_byte);
      ++Byte_addr;
      ++lst_bytes;
    }
  }
  else {    /* non-code pack section */
    /* list first byte on odd address */
    if ((Bytes_emitted != 0) && (Byte_addr & 1)) {
      gp_mem_b_get(M, Byte_addr, &emit_byte, NULL, NULL);
      Position += _lst_printf("%02X ", emit_byte);
      ++Byte_addr;
      ++lst_bytes;
    }
    /* list full words */
    start_addr = Byte_addr;
    while (((Bytes_emitted - lst_bytes) > 1) && ((Position + 5) <= LST_LINENUM_POS)) {
      state.device.pclass->i_memory_get(M, Byte_addr, &emit_word, NULL, NULL);

      /* Display '?' for undefined bytes if it is a relocatable code. */
      if (Reloc_type != 0) {
        n = _print_reloc(Reloc_type, emit_word, (Byte_addr - start_addr) / 2);

        Position += (n == 0) ? _lst_printf("%04X ", emit_word) : n;
      }
      else {
        Position += _lst_printf("%04X ", emit_word);
      }

      Byte_addr += 2;
      lst_bytes += 2;
    }

    if (((Bytes_emitted - lst_bytes) == 1) && ((Position + 3) <= LST_LINENUM_POS)) {
      gp_mem_b_get(M, Byte_addr, &emit_byte, NULL, NULL);
      Position += _lst_printf("%02X ", emit_byte);
      ++Byte_addr;
      ++lst_bytes;
    }
  }

  /* append appropriate spacing */
  _lst_spaces(LST_LINENUM_POS - Position);

  return lst_bytes;
}

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

static void
_cod_symbol_table(void)
{
  const symbol_t **lst;
  size_t           sym_count;

  sym_count = gp_sym_get_symbol_count(state.stGlobal);

  if (sym_count == 0) {
    return;
  }

  if (!state.mpasm_compatible) {
    lst = gp_sym_clone_symbol_array(state.stGlobal, gp_sym_version_compare_fn);
  }
  else {
    lst = gp_sym_clone_symbol_array(state.stGlobal, gp_sym_compare_fn);
  }

  assert(lst);

  cod_write_symbols(lst, sym_count);
  free((void*)lst);
}

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

void lst_init() {
  state.lst.line_of_page   = 0;
  state.lst.page           = 0;
  state.lst.lines_per_page = 59;
  state.lst.line_number    = 1;
  state.lst.memory_map     = true;
  state.lst.symbol_table   = true;
  state.lst.lst_state      = gpasm_state::Lst::LST_IN_MEM;

  /* Determine state.start_date. */
  gp_date_string(state.lst.start_date, sizeof(state.lst.start_date));

  if (!state.cmd_line.macro_expand){
    state.lst.expand = true;
  }

  state.lst.force            = state.cmd_line.lst_force;
  state.lst.config_address   = 0;
  state.lst.title_name[0]    = '\0';
  state.lst.subtitle_name[0] = '\0';
  state.lst.tabstop          = 8;   /* Default tabstop every 8. */
  state.lst.line_width       = 132; /* Default line width is 132. */

  if (state.lst_file != OUT_NAMED) {
    snprintf(state.lst_file_name, sizeof(state.lst_file_name), "%s.lst", state.base_file_name);
  }

  if (state.lst_file == OUT_SUPPRESS) {
    state.lst.f       = NULL;
    state.lst.enabled = false;
    unlink(state.lst_file_name);
  }
  else {
    state.lst.f = fopen(state.lst_file_name, "wt");

    if (state.lst.f == NULL) {
      perror(state.lst_file_name);
      exit(1);
    }
    state.lst.enabled = true;
  }

  cod_lst_line(COD_FIRST_LST_LINE);
}

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

void lst_close() {
  cod_lst_line(COD_LAST_LST_LINE);

  state.lst.lst_state = gpasm_state::Lst::LST_IN_NONE;

  if (state.lst.f) {
    lst_line(NULL);
    lst_line("Errors   : %5d", state.num.errors);
    lst_line("Warnings : %5d reported, %5d suppressed", state.num.warnings, state.num.warnings_suppressed);
    lst_line("Messages : %5d reported, %5d suppressed", state.num.messages, state.num.messages_suppressed);
    lst_line(NULL);
    putc('\f', state.lst.f);

    fclose(state.lst.f);
    state.lst.f = NULL;
  }
}

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

void
lst_throw(void)
{
  if (state.lst.f) {
    state.lst.page++;
    fprintf(state.lst.f,
            "%s%s %*.*s   %-28sPAGE %2d\n%s\n%s\n",
            (state.lst.page == 1) ? "" : "\f",
            GPASM_VERSION_STRING,
            (int)(45 - sizeof(GPASM_VERSION_STRING)),
            (int)(45 - sizeof(GPASM_VERSION_STRING)),
            state.src_file_name,
            state.lst.start_date,
            state.lst.page,
            state.lst.title_name,
            state.lst.subtitle_name);

    state.lst.line_of_page = 4;
    cod_lst_line(COD_NORMAL_LST_LINE);
    cod_lst_line(COD_NORMAL_LST_LINE);
    cod_lst_line(COD_NORMAL_LST_LINE);
    state.lst.line_number += 3;
  }
}

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

static void
_mem_header(void)
{
  lst_line("LOC    OBJECT CODE    LINE  SOURCE TEXT");
  lst_line("  VALUE");
}

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

static void
_symbol_table_header(void)
{
  lst_line("SYMBOL TABLE");

  if (!state.mpasm_compatible) {
    lst_line("%-32s  %-10s  %-8s    %-11s    %s", "  LABEL", "   TYPE", "   VALUE", "     VALUE", "     VALUE");
    lst_line("%-32s  %-10s  %-8s    %-11s    %s", "",        "",        "   (hex)", "     (dec)", "     (text)");
  }
  else {
    lst_line("%-32s  %-8s", "  LABEL", " VALUE");
  }
}

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

static void _memory_map_header() {
  lst_line("MEMORY USAGE MAP ('%c' = Used,  '%c' = Unused)", MEM_USED_CHAR, MEM_UNUSED_CHAR);
}

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

void lst_page_start() {
  lst_throw();
  switch (state.lst.lst_state) {
    case gpasm_state::Lst::LST_IN_MEM:
      _mem_header();
      break;

    case gpasm_state::Lst::LST_IN_SYMTAB:
      _symbol_table_header();
      break;

    case gpasm_state::Lst::LST_IN_MAP:
      _memory_map_header();
      break;

    default:
      lst_line(NULL);
      break;
  }

  lst_line(NULL);
}

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

void _cdecl lst_line(const char *Format, ...) {
  va_list args;

  if (state.lst.f) {
    if (Format) {
      _lst_check_page_start();
      va_start(args, Format);
      vfprintf(state.lst.f, Format, args);
      va_end(args);
    }
    _lst_eol();
  }
}

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

void
lst_err_line(const char *Type, unsigned Code, const char *Format, va_list Args)
{
  if (state.lst.f) {
    _lst_check_page_start();
    fprintf(state.lst.f, "%s[%03d]%s: ", Type, Code, (strcmp(Type, "Error") == 0) ? "  " : "");
    vfprintf(state.lst.f, Format, Args);
    _lst_eol();
  }
}

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

#define MEM_IS_USED(M, I)  \
        (((M)->memory) ? (IS_BYTE ? (gp_mem_b_offset_is_used(M, I)) : \
                                            (gp_mem_i_offset_is_used_le(M, (I) * 2) == W_USED_ALL)) : false)

#define NUM_PER_LINE            64
#define NUM_PER_BLOCK           16

void
lst_memory_map(MemBlock_t *M)
{
  unsigned max_mem;
  unsigned base;
  unsigned i;
  unsigned j;
  int          addr_digits;
  bool   row_is_used;
  char         row_map[NUM_PER_LINE];
  char         ch;
  unsigned used;

  state.lst.lst_state = gpasm_state::Lst::LST_IN_MAP;
  lst_line(NULL);
  lst_line(NULL);
  _memory_map_header();
  lst_line(NULL);

  addr_digits = (state.device.pclass) ? state.device.pclass->addr_digits : 4;

  while (M) {
    max_mem = I_MEM_MAX >> (uint8_t)!IS_BYTE;
    base    = IMemAddrFromBase(M->base) >> (uint8_t)!IS_BYTE;

    for (i = 0; i < max_mem; i += NUM_PER_LINE) {
      row_is_used = false;

      for (j = 0; j < NUM_PER_LINE; j++) {
        if (MEM_IS_USED(M, i + j)) {
          ch = MEM_USED_CHAR;
          row_is_used = true;
        }
        else {
          ch = MEM_UNUSED_CHAR;
        }

        row_map[j] = ch;
      }

      if (row_is_used) {
        if (state.show_full_addr && (IS_PIC16E_CORE)) {
          /* Gpasm mode: Print all address digits. */
          _lst_printf("%0*X :", addr_digits, (i + base));
        }
        else {
          /* MPASM(X) compatible: Print only lower 4 address digits. */
          _lst_printf("%04X :", (i + base) & 0xffff);
        }

        for (j = 0; j < NUM_PER_LINE; j++) {
          if ((j % NUM_PER_BLOCK) == 0) {
            _lst_putc(' ');
          }

          _lst_putc(row_map[j]);
        }

        _lst_eol();
        _lst_check_page_start();
      }
    }

    M = M->next;
  }

  lst_line(NULL);
  lst_line("All other memory blocks unused.");
  lst_line(NULL);

  /* it seems that MPASM includes config bytes into program memory usage
   * count for 16 bit cores. See gpasm testsuite:
   * gpasm/testsuite/gpasm.mchip/listfiles/configX.lst */
#define IS_PIC16        (IS_PIC16_CORE) || (IS_PIC16E_CORE)

  if (IS_EEPROM) {
    lst_line("Memory Bytes Used: %5i", gp_mem_b_used(state.i_memory));
  }
  else {
    used = gp_processor_insn_from_byte_p(state.processor, (!(IS_PIC16) && (state.processor)) ?
                        b_range_memory_used(state.i_memory, 0,
                                            gp_processor_byte_from_insn_c(state.device.pclass,
                                                                          state.processor->prog_mem_size)) :
                        gp_mem_b_used(state.i_memory));

    lst_line("Program Memory %s Used: %5i", IS_BYTE ? "Bytes" : "Words", used);

    if ((state.processor) && (state.processor->prog_mem_size >= 0)) {
      lst_line("Program Memory %s Free: %5u",
               IS_BYTE ? "Bytes" : "Words",
               ((int)used <= state.processor->prog_mem_size) ? (state.processor->prog_mem_size - used) : 0);
    }
  }

  lst_line(NULL);
}

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

#define ADDR_LEN                7

void
lst_format_line(const char *Src_line, unsigned Value)
{
  unsigned  emitted;
  unsigned  emitted_lines;
  unsigned  byte_addr;
  unsigned  bytes_emitted;
  unsigned  lst_bytes;
  const char   *addr_fmt;
  unsigned  pos;
  uint16_t      reloc_type;
  MemBlock_t   *m;
  uint32_t      addr;
  uint16_t      word;
  gp_reloc_t   *reloc;

  assert(Src_line);

  emitted       = 0;
  emitted_lines = 0;
  bytes_emitted = 0;
  addr_fmt      = (IS_PIC16E_CORE) ? "%06X " : (IS_EEPROM ? "%04X " : "%04X   ");
  pos           = 0;
  m             = state.i_memory;

  byte_addr = state.lst.line.was_byte_addr;

  if ((state.mode == MODE_RELOCATABLE) && (state.obj.section) &&
      (state.obj.new_sect_flags & STYP_TEXT) && (state.obj.section->relocation_list.last)) {
      addr = state.obj.section->address + state.obj.section->relocation_list.last->address;

      if (addr > byte_addr) {
        /* already passed it, go back to the history */
        reloc      = _find_reloc_by_address(byte_addr);
        reloc_type = (reloc) ? reloc->type : 0;
      }
      else if (addr == byte_addr) {
        reloc_type = state.obj.section->relocation_list.last->type;
      }
      else {
        reloc_type = 0;
      }
    }
  else {
    reloc_type = 0;
  }

  switch (state.lst.line.linetype) {
    case gpasm_state::Lst::Line::LTY_INSN:
      emitted_lines = emitted = state.byte_addr - byte_addr;
      break;

    case gpasm_state::Lst::Line::LTY_DATA: {
      emitted = state.byte_addr - byte_addr;

      if ((SECTION_FLAGS & (STYP_TEXT | (state.mpasm_compatible ? STYP_BPACK : 0))) == STYP_TEXT) {
        /* generate line numbers for data directives in program memory;
         * in mpasm compatibility mode code_pack doesn't generate line numbers */
        emitted_lines = emitted;
      }
      break;
    }

    case gpasm_state::Lst::Line::LTY_RES: {
      if (SECTION_FLAGS & STYP_DATA) {
        /* generate data listing for idata */
        emitted = state.byte_addr - byte_addr;
      }
      else if (!IS_RAM_ORG) {
        /* generate line numbers for res directives in program memory */
        emitted_lines = emitted = state.byte_addr - byte_addr;
      }
      break;
    }

    case gpasm_state::Lst::Line::LTY_IDLOCS:
      /* always 8 bytes (4 words) */
      emitted = 8;
      break;

    default:
      break;
  }

  coff_add_linenum(emitted_lines);

  /* Don't write to file if list is disabled with NOLIST directive */
  if (!state.lst.enabled) {
    return;
  }

  byte_addr = 0;
  switch (state.lst.line.linetype) {
    case gpasm_state::Lst::Line::LTY_EQU:
    case gpasm_state::Lst::Line::LTY_SET:
      pos += _lst_printf("  %08X", Value);
      _lst_spaces(LST_LINENUM_POS - pos);
      break;

    case gpasm_state::Lst::Line::LTY_SET4:
      pos += _lst_printf("  %04X", Value & 0xffff);
      _lst_spaces(LST_LINENUM_POS - pos);
      break;

    case gpasm_state::Lst::Line::LTY_ORG:
      pos += _lst_printf(addr_fmt, gp_processor_insn_from_byte_p(state.processor, state.byte_addr));
      _lst_spaces(LST_LINENUM_POS - pos);
      break;

    case gpasm_state::Lst::Line::LTY_IDLOCS:
      /* not used for 16 bit devices, config is used */
      m              = state.c_memory;
      pos           += _lst_printf(addr_fmt, gp_processor_insn_from_byte_p(state.processor, state.device.id_location));
      lst_bytes      = _lst_data(pos, m, state.device.id_location, emitted, reloc_type);
      byte_addr      = state.device.id_location + lst_bytes;
      bytes_emitted  = emitted - lst_bytes;
      break;

    case gpasm_state::Lst::Line::LTY_DATA:
    case gpasm_state::Lst::Line::LTY_RES:
      pos += _lst_printf(addr_fmt, state.lst.line.was_byte_addr);
      goto lst_data;

    case gpasm_state::Lst::Line::LTY_INSN:
      pos += _lst_printf(addr_fmt, gp_processor_insn_from_byte_p(state.processor, state.lst.line.was_byte_addr));

lst_data:

      lst_bytes     = _lst_data(pos, m, state.lst.line.was_byte_addr, emitted, reloc_type);
      byte_addr     = state.lst.line.was_byte_addr + lst_bytes;
      bytes_emitted = emitted - lst_bytes;
      break;

    case gpasm_state::Lst::Line::LTY_CONFIG: {
      if (IS_PIC16E_CORE) {
        /* The config data is byte addressable, but we only want to print words in the list file. */
        if (state.lst.config_address == CONFIG4L) {
          /* Special case */
          state.device.pclass->i_memory_get(state.c_memory, state.lst.config_address, &word, NULL, NULL);
          pos += _lst_printf(addr_fmt, state.lst.config_address);
          pos += _lst_printf("%04X", word);
          _lst_spaces(LST_LINENUM_POS - pos);
        }
        else if ((state.lst.config_address & 1) == 0) {
          /* if it is an even address don't print anything */
          _lst_spaces(LST_LINENUM_POS);
        }
        else {
          state.device.pclass->i_memory_get(state.c_memory, state.lst.config_address - 1, &word, NULL, NULL);
          pos += _lst_printf(addr_fmt, state.lst.config_address - 1);
          pos += _lst_printf("%04X", word);
          _lst_spaces(LST_LINENUM_POS - pos);
        }
      }
      else {
        state.device.pclass->i_memory_get(state.c_memory, state.lst.config_address, &word, NULL, NULL);
        pos += _lst_printf(addr_fmt, gp_processor_insn_from_byte_p(state.processor, state.lst.config_address));
        pos += _lst_printf("%04X", word);
        _lst_spaces(LST_LINENUM_POS - pos);
      }
      break;
    }

    case gpasm_state::Lst::Line::LTY_SEC:
    case gpasm_state::Lst::Line::LTY_DIR:
    case gpasm_state::Lst::Line::LTY_NONE:
    default:
      _lst_spaces(LST_LINENUM_POS);
      break;
  }

  if (state.stGlobal == state.stTop) {
    _lst_printf("%05d ", state.src_list.last->line_number);
  }
  else {
    _lst_printf("    M ");
  }

  /* Now copy source line to listing, expanding tabs as required. */
  {
    int         src_column = 0;           /* current source line column */
    int         lst_column = LST_SRC_POS; /* current listing column after the SRC_POS */
    const char *p = Src_line;

    while (*p != '\0') {
      if (*p == '\t') {
        int len = state.lst.tabstop - (src_column % state.lst.tabstop);

        while (len-- > 0) {
          if (lst_column >= state.lst.line_width) {
            _lst_eol();
            _lst_spaces(LST_SRC_POS);
            lst_column = LST_SRC_POS;
          }
          ++lst_column;
          ++src_column;
          putc(' ', state.lst.f);
        }
      }
      else {
        if (lst_column >= state.lst.line_width) {
          _lst_eol();
          _lst_spaces(LST_SRC_POS);
          lst_column = LST_SRC_POS;
        }

        ++lst_column;
        ++src_column;
        putc(*p, state.lst.f);
      }
      ++p;
    }
  }

  /* Tell the .cod file that the next line(s) has an opcode(s) */
  state.cod.emitting = emitted;

  _lst_eol();

  if (bytes_emitted > 0) {
    while (bytes_emitted > 0) {
      /* data left to print on separate lines */

      pos            = _lst_spaces(ADDR_LEN);
      lst_bytes      = _lst_data(pos, m, byte_addr, bytes_emitted, reloc_type);
      byte_addr     += lst_bytes;
      bytes_emitted -= lst_bytes;
      _lst_eol();
    }
    state.cod.emitting = 0;
  }
}

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

static int _cdecl _lst_symbol_verscmp(const void *P0, const void *P1) {
  return strverscmp(gp_sym_get_symbol_name(((const lst_symbol_t *)P0)->sym),
                    gp_sym_get_symbol_name(((const lst_symbol_t *)P1)->sym));
}

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

static int _cdecl _lst_symbol_cmp(const void *P0, const void *P1) {
  return strcmp(gp_sym_get_symbol_name(((const lst_symbol_t *)P0)->sym),
                gp_sym_get_symbol_name(((const lst_symbol_t *)P1)->sym));
}

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

/* append the symbol table to the .lst file */

void lst_symbol_table(void) {
  const pnode_t      *list;
  const pnode_t      *head;
  const char         *string;
  const symbol_t    **clone;
  size_t              sym_count;
  lst_symbol_t       *lst;
  lst_symbol_t       *ps;
  size_t              count;
  size_t              i;
  const char         *name;
  const void         *ptr;
  const variable_t   *var;
  numstring_t         num_type;
  long                val;
  const macro_head_t *hd;
  char               *mac_parms;
  size_t              mac_parm_length;

  state.lst.lst_state = gpasm_state::Lst::LST_IN_SYMTAB;
  _symbol_table_header();
  lst_line(NULL);

  _cod_symbol_table();

  count = gp_sym_get_symbol_count(state.stGlobal) + gp_sym_get_symbol_count(state.stDefines) +
	  gp_sym_get_symbol_count(state.stMacros);
  if (count == 0) {
    return;
  }

  ps = lst = new lst_symbol_t[count];

  clone = gp_sym_clone_symbol_array(state.stGlobal, NULL);
  if (clone) {
    sym_count = gp_sym_get_symbol_count(state.stGlobal);
    for (i = 0; i < sym_count; i++) {
      ps->sym  = clone[i];
      ps->type = lst_symbol_t::LST_SYMBOL;
      ++ps;
    }

    free((void*)clone);
  }

  clone = gp_sym_clone_symbol_array(state.stDefines, NULL);
  if (clone) {
    sym_count = gp_sym_get_symbol_count(state.stDefines);
    for (i = 0; i < sym_count; i++) {
      ps->sym  = clone[i];
      ps->type = lst_symbol_t::LST_DEFINITION;
      ++ps;
    }

    free((void*)clone);
  }

  clone = gp_sym_clone_symbol_array(state.stMacros, NULL);
  if (clone) {
    sym_count = gp_sym_get_symbol_count(state.stMacros);
    for (i = 0; i < sym_count; i++) {
      ps->sym  = clone[i];
      ps->type = lst_symbol_t::LST_MACRO;
      ++ps;
    }

    free((void*)clone);
  }

  assert(ps == &lst[count]);

  if (!state.mpasm_compatible) {
    qsort(lst, count, sizeof(lst_symbol_t), _lst_symbol_verscmp);
  }
  else {
    qsort(lst, count, sizeof(lst_symbol_t), _lst_symbol_cmp);
  }

  for (i = 0; i < count; i++) {
    name = gp_sym_get_symbol_name(lst[i].sym);
    ptr  = gp_sym_get_symbol_annotation(lst[i].sym);

    switch (lst[i].type) {
      case lst_symbol_t::LST_SYMBOL: {
        /* symbol */
        var = (const variable_t *)ptr;

        if (!state.mpasm_compatible) {
          if (var) {
            if (FlagIsClr(var->flags, VATRR_HAS_NO_VALUE)) {
              lst_line("%-32s  %-10s    %08X    %11d", name, value_type_to_str(var, false), var->value, var->value);
            }
            else {
              lst_line("%-32s  %-10s    %8s    %-11s", name, value_type_to_str(var, false), "HAS NO", "HAS NO");
            }
          }
          else {
            lst_line("%-32s", name);
          }
        }
        else {
          lst_line("%-32s  %08X", name, (var) ? var->value : 0);
        }
        break;
      }

      case lst_symbol_t::LST_DEFINITION: {
        /* define */
        list = (const pnode_t *)ptr;

        if (list) {
          assert(PnIsList(list));
          head = PnListHead(list);

          assert(PnIsString(head));
          string = PnString(head);
        }
        else {
          string = NULL;
        }

        if (!state.mpasm_compatible) {
          if (string) {
            val = gp_strtol(string, &num_type);

            if (num_type) {
              /* This definition contains is just a decimal number. */
              lst_line("%-32s  %-10s    %08lX    %11ld    %s", name, "DEFINITION", val, val, string);
            }
            else {
              /* This definition contains a general text (not just number). */
              lst_line("%-32s  %-10s    %8s    %11s    %s", name, "DEFINITION", "", "", string);
            }
          }
          else {
            lst_line("%-32s  %-10s", name, "DEFINITION");
          }
        } /* if (!state.mpasm_compatible) */
        else {
          lst_line("%-32s  %s", name, (string) ? string : "");
        }
        break;
      }

      case lst_symbol_t::LST_MACRO: {
        /* define */
        if (!state.mpasm_compatible) {
          hd = (const macro_head_t *)ptr;

          if (hd) {
            mac_parm_length = 0;
            mac_parms       = macro_params_to_string(NULL, MAC_PARM_STR_MAX_LENGTH, &mac_parm_length, hd->parms);

            if (mac_parms) {
              if (mac_parm_length > 0) {
                lst_line("%-32s  %-10s    %8s    %11s    %s", name, "MACRO", "", "", mac_parms);
              }
              else {
                lst_line("%-32s  %-10s", name, "MACRO");
              }

              free(mac_parms);
            }
            else {
              lst_line("%-32s  %-10s", name, "MACRO");
            }
          }
          else {
            lst_line("%-32s  %-10s", name, "MACRO");
          }
        }
        else {
          lst_line("%-32s", name);
        }
        break;
      }
    }
  }

  free(lst);
}


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

/*
 * Preprocessed file generator.
 */

void
preproc_init(void)
{
  const char *name;

  name = state.preproc.preproc_file_name;
  if (name) {
    if ((name[0] == '-') && (name[1] == '\0')) {
      state.preproc.f = stdout;
    }
    else if ((state.preproc.f = fopen(name, "wt")) == NULL) {
      perror(name);
      exit(1);
    }
  }
}

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

void
preproc_emit(void)
{
  if (state.preproc.f) {
    if (state.preproc.do_emit && asm_enabled()) {
      fprintf(state.preproc.f, "%s\n", state.preproc.curr_src_line.line);
    }
    else {
      state.preproc.do_emit = true;
    }
  }
}
Detected encoding: UTF-80