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

/* ".LST" file output for gplink
   Copyright 2004, 2005
   Craig Franklin

    Copyright 2016 Molnár Károly
*/

#include "stdhdr.h"

#include "libgputils.h"
#include "gplink.h"
#include "cod.h"

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

#define LINESIZ     520

static bool          list_enabled;
static const gp_section_t *line_section;

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

static void _open_source(const char *Name, gp_symbol_t *Symbol) {
  bool name_allocated=false;
  const char     *p;

  assert(Name);

  list_context_t *pnew = new list_context_t;
  pnew->f = fopen(Name, "rt");
  if (!pnew->f) {
    /* Try searching include pathes. */
    for (int i = 0; i < state.num_paths; i++) {
      unsigned len = strlen(state.paths[i])
		   + strlen(PATH_SEPARATOR_STR)
		   + strlen(Name) +1;
      char*full_name = new char[len];
      snprintf(full_name, len, "%s" PATH_SEPARATOR_STR "%s", state.paths[i], Name);

      pnew->f = fopen(full_name, "rb");
      if (pnew->f) {
        Name = full_name;
	name_allocated=true;
        break;
      }else free(full_name);
    }

    if (!pnew->f) {
      /* The path may belong to a build procedure other than this. */
      p = strrchr(Name, PATH_SEPARATOR_CHAR);

      if (p) {
        for (int i = 0; i < state.num_paths; i++) {
          unsigned len = strlen(state.paths[i])
		       + strlen(p) + 1;
          char*full_name = new char[len];
          snprintf(full_name, len, "%s%s", state.paths[i], p);
          pnew->f = fopen(full_name, "rb");
          if (pnew->f) {
	    if (name_allocated) free((char*)Name);
            Name = full_name;
	    name_allocated=true;
            break;
          }
        }
      }
    }
  }

  if (pnew->f) {
    pnew->name           = name_allocated ? (char*)Name : GP_Strdup(Name);
    pnew->missing_source = false;
  }
  else {
    pnew->missing_source = true;

    if (getenv("GPUTILS_WARN_MISSING_SRC")) {
      gp_warning("Cannot find source file: \"%s\"", Name);
    }
  }

  pnew->symbol      = Symbol;
  pnew->line_number = 1;
  pnew->prev        = state.lst.src;

  state.lst.src = pnew;
}

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

static void _close_source() {
  list_context_t *old;

  assert(state.lst.src);

  old = state.lst.src;
  state.lst.src = state.lst.src->prev;
  free(old);
}

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

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

  if (state.lst.f) {
    va_start(args, Format);
    vfprintf(state.lst.f, Format, args);
    va_end(args);
    fputc('\n', state.lst.f);
  }
}

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

static const gp_linenum_t* _find_line_number(const gp_symbol_t *Symbol, unsigned Line_number) {
  const gp_section_t *section;
  const gp_linenum_t *linenum;

  section = state.object->section_list.first;
  while (section) {
    linenum = gp_coffgen_find_linenum(section, Symbol, Line_number);

    if (linenum) {
      if (section != line_section) {
        /* Switching sections, so update was_org with the pnew address. */
        state.lst.was_byte_addr = linenum->address;
        line_section            = section;
      }

      return linenum;
    }

    section = section->next;
  }

  return NULL;
}

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

static char* _expand_tabs(const char *Buffer) {
  static char  dest[LINESIZ];

  unsigned is;
  unsigned id;
  char         c;
  unsigned n;

  for (is = 0, id = 0; ((c = Buffer[is]) != '\0') && (id < (sizeof(dest) - 2)); ++is) {
    if (c == '\t') {
      n = TABULATOR_SIZE - (id % TABULATOR_SIZE);

      while ((n-- > 0) && (id < (sizeof(dest) - 2))) {
        dest[id++] = ' ';
      }
    }
    else {
      dest[id++] = c;
    }
  }

  dest[id] = '\0';
  return dest;
}

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

static void _write_source(bool Last_line) {
  char                linebuf[LINESIZ];
  char                dasmbuf[LINESIZ];
  char               *pc;
  const gp_linenum_t *line;
  bool          first_time;
  unsigned        org;
  unsigned        len;
  uint8_t             byte;
  uint16_t            word;
  int                 num_bytes;

  /* If the source file wasn't found, can't write it to the list file. */
  if (state.lst.src->missing_source) {
    return;
  }

  org = 0;

  while (true) {
    /* When last_line is 0 print all lines, else print to last_line. */
    if (Last_line && state.lst.src->line_number > (unsigned)Last_line) {
      break;
    }

    if (fgets(linebuf, LINESIZ, state.lst.src->f) == NULL) {
      break;
    }

    state.lst.was_byte_addr = org;

    if (list_enabled) {
      /* Eat the trailing newline. */
      pc = strrchr(linebuf, '\n');

      if (pc) {
        *pc = '\0';
      }

      first_time = true;

      line = _find_line_number(state.lst.src->symbol, state.lst.src->line_number);
      while (line) {
        /* Print all instructions generated by this line of the source. */
        if (line->line_number != state.lst.src->line_number) {
          break;
        }

        if (!first_time) {
          /* Only print the source line the first time. */
          linebuf[0] = '\0';
        }

        state.cod.emitting = true;
        org = line->address;
        len = gp_mem_b_get_unlisted_size(line_section->data, org);

        if (len == 0) {
          if (linebuf[0] != '\0') {
            _lst_line("%42s %s", "", linebuf);
          }
          else {
            _lst_line("");
          }

          cod_lst_line(COD_NORMAL_LST_LINE);
        }
        else {
          if ((org & 1) || (len < 2)) {
            /* even address or less then two byts to disassemble: disassemble one byte */
            if (len != 0) {
              gp_mem_b_assert_get(line_section->data, org, &byte, NULL, NULL);
              gp_disassemble_byte(line_section->data, org, dasmbuf, sizeof(dasmbuf));
              _lst_line("%06lx   %02x       %-24s %s", gp_processor_insn_from_byte_c(state.pclass, org),
                        (unsigned)byte, _expand_tabs(dasmbuf), linebuf);
              gp_mem_b_set_listed(line_section->data, org, 1);
              state.lst.was_byte_addr = org;
              cod_lst_line(COD_NORMAL_LST_LINE);
              ++org;
            }
          }
          else {
            state.pclass->i_memory_get(line_section->data, org, &word, NULL, NULL);
            num_bytes = gp_disassemble_size(line_section->data, org, state.pclass,
                                            gp_processor_bsr_boundary(state.processor),
                                            state.processor->prog_mem_size, GPDIS_SHOW_ALL_BRANCH,
                                            dasmbuf, sizeof(dasmbuf), len);
            _lst_line("%06lx   %04x     %-24s %s", gp_processor_insn_from_byte_c(state.pclass, org),
                      word, _expand_tabs(dasmbuf), linebuf);
            gp_mem_b_set_listed(line_section->data, org, num_bytes);
            state.lst.was_byte_addr = org;
            cod_lst_line(COD_NORMAL_LST_LINE);
            org += 2;

            if (num_bytes > 2) {
              state.lst.was_byte_addr = org;
              state.pclass->i_memory_get(line_section->data, org, &word, NULL, NULL);
              _lst_line("%06lx   %04x", gp_processor_insn_from_byte_c(state.pclass, org), word);
              cod_lst_line(COD_NORMAL_LST_LINE);
              org += 2;

              if (line->next) {
                /* skip the line number for the other half of this instruction */
                line = line->next;
              }
            }
          }
        }

        first_time = false;
        line = line->next;
      } /* while (line) */

      if (first_time) {
        if (linebuf[0] != '\0') {
          _lst_line("%42s %s", "", linebuf);
        }
        else {
          _lst_line("");
        }

        state.cod.emitting = false;
        cod_lst_line(COD_NORMAL_LST_LINE);
      }
    }

    state.lst.src->line_number++;
  }
}

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

/*
 * _lst_init - initialize the lst file
 */

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

  if (gp_num.errors || 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;
  }

  if (!state.lst.enabled) {
    return;
  }

  state.lst.was_byte_addr = 0;
  state.cod.emitting      = false;

  _lst_line("%s", GPLINK_VERSION_STRING);
  _lst_line("%s", GPUTILS_COPYRIGHT_STRING);
  _lst_line("Listing File Generated: %s", state.start_date);
  _lst_line("");
  _lst_line("");
  _lst_line("Address  Value    Disassembly              Source");
  _lst_line("-------  -----    -----------              ------");
}

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

void lst_write() {
  const gp_aux_t *aux;
  bool      first_time;

  _lst_init();

  if (!state.lst.enabled) {
    return;
  }

  list_enabled  = true;
  first_time    = true;
  state.lst.src = NULL;
  /* scan through the file symbols */
  FOR(gp_symbol_list_t,symbol,state.object->symbol_list) {
    if (symbol->pclass == C_FILE) {
      /* open a pnew file */
      aux = symbol->aux_list.first;
      assert(aux);

      if (aux->_aux_symbol._aux_file.line_number) {
        /* it is an include file, so print the current file
           until the line number is reached */
        assert(state.lst.src);
        _write_source(!!aux->_aux_symbol._aux_file.line_number);
      }else{
        /* it is not an include, so enable listing */
        list_enabled = true;
      }

      _open_source(aux->_aux_symbol._aux_file.filename, &*symbol);

      if (first_time) {
        /* write the line numbers for the lst file header */
        cod_lst_line(COD_FIRST_LST_LINE);
        cod_lst_line(COD_NORMAL_LST_LINE);
        cod_lst_line(COD_NORMAL_LST_LINE);
        cod_lst_line(COD_NORMAL_LST_LINE);
        cod_lst_line(COD_NORMAL_LST_LINE);
        cod_lst_line(COD_NORMAL_LST_LINE);
        cod_lst_line(COD_NORMAL_LST_LINE);
        first_time = false;
      }
    }
    else if (symbol->pclass == C_LIST) {
      if (strcasecmp(symbol->name, ".list") == 0) {
        _write_source(!!symbol->value);
        list_enabled = true;
      }
      else if (strcasecmp(symbol->name, ".nolist") == 0) {
        _write_source(!!symbol->value);
        list_enabled = false;
      }
      else {
        assert(0);
      }
    }
    else if (symbol->pclass == C_EOF) {
      /* print the rest of the current file then, close it */
      _write_source(0);
      _close_source();
    }
  }

  fclose(state.lst.f);
}
Detected encoding: UTF-80