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

/* Displays contents of ".COD" files
   Copyright 2001-2005	Scott Dattalo
   Copyright 2012	Borut Ražem
   Copyright 2016	Molnár Károly
*/

#include "stdhdr.h"

#include "libgputils.h"
#include "gpvc.h"
#include "dump.h"
#include "block.h"

#define MAX_SOURCE_FILES        100

typedef struct memmap_info {
  unsigned        start_addr;
  unsigned        end_addr;
  struct memmap_info *next;
} memmap_info_t;

static uint8_t       cod_block[COD_BLOCK_SIZE];
static bool used_map[COD_BLOCK_SIZE];

static unsigned  number_of_source_files              = 0;
static char         *source_file_names[MAX_SOURCE_FILES] = { NULL, };
static FILE         *source_files[MAX_SOURCE_FILES]      = { NULL, };

static const char *symbol_type_str[] = {
  "a_reg"            , "x_reg"             , "c_short"          , "c_long"            , /*   0 -   3 */
  "c_ushort"         , "c_ulong"           , "c_pointer"        , "c_upointer"        , /*   4 -   7 */
  "table"            , "m_byte"            , "m_boolean"        , "m_index"           , /*   8 -  11 */
  "byte_array"       , "u_byte_array"      , "word_array"       , "u_word_array"      , /*  12 -  15 */
  "func_void_none"   , "func_void_byte"    , "func_void_twobyte", "func_void_long"    , /*  16 -  19 */
  "func_void_undef"  , "func_int_none"     , "func_int_byte"    , "func_int_twobyte"  , /*  20 -  23 */
  "func_int_long"    , "func_int_undef"    , "func_long_none"   , "func_long_byte"    , /*  24 -  27 */
  "func_long_twobyte", "func_long_long"    , "func_long_undef"  , "pfunc_void_none"   , /*  28 -  31 */
  "pfunc_void_byte"  , "pfunc_void_twobyte", "pfunc_void_long"  , "pfunc_void_undef"  , /*  32 -  35 */
  "pfunc_int_none"   , "pfunc_int_byte"    , "pfunc_int_twobyte", "pfunc_int_long"    , /*  36 -  39 */
  "pfunc_int_undef"  , "pfunc_long_none"   , "pfunc_long_byte"  , "pfunc_long_twobyte", /*  40 -  43 */
  "pfunc_long_long"  , "pfunc_long_undef"  , "address"          , "constant"          , /*  44 -  47 */
  "bad_und"          , "br_und"            , "upper_und"        , "byte_und"          , /*  48 -  51 */
  "add_und"          , "m_keyword"         , "add_mi_und"       , "vector"            , /*  52 -  55 */
  "port_w"           , "port_r"            , "port_rw"          , "port_rmw"          , /*  56 -  59 */
  "endif"            , "exundef"           , "macro_t"          , "macro_s"           , /*  60 -  63 */
  "macro_a"          , "macro_c"           , "c_keyword"        , "void"              , /*  64 -  67 */
  "c_enum"           , "c_typedef1"        , "c_utypedef1"      , "c_typedef2"        , /*  68 -  71 */
  "c_utypedef2"      , "cp_typedef1"       , "cp_utypedef1"     , "cp_typedef2"       , /*  72 -  75 */
  "cp_utypedef2"     , "c_struct"          , "i_struct"         , "l_const"           , /*  76 -  79 */
  "s_short"          , "s_ushort"          , "s_long"           , "s_ulong"           , /*  80 -  83 */
  "sa_short"         , "sa_ushort"         , "sa_long"          , "sa_ulong"          , /*  84 -  87 */
  "sp_short"         , "sp_ushort"         , "sp_long"          , "sp_ulong"          , /*  88 -  91 */
  "fixed_pointer"    , "pointer_function"  , "cc_reg"           , "PtrF_void_none"    , /*  92 -  95 */
  "PtrF_void_byte"   , "PtrF_void_twobyte" , "PtrF_void_long"   , "PtrF_void_undef"   , /*  96 -  99 */
  "PtrF_int_none"    , "PtrF_int_byte"     , "PtrF_int_twobyte" , "PtrF_int_long"     , /* 100 - 103 */
  "PtrF_int_undef"   , "PtrF_long_none"    , "PtrF_long_byte"   , "PtrF_long_twobyte" , /* 104 - 107 */
  "PtrF_long_long"   , "PtrF_long_undef"   , "PfAR_void_none"   , "PfAR_void_byte"    , /* 108 - 111 */
  "PfAR_void_twobyte", "PfAR_void_long"    , "PfAR_void_undef"  , "PfAR_int_none"     , /* 112 - 115 */
  "PfAR_int_byte"    , "PfAR_int_twobyte"  , "PfAR_int_long"    , "PfAR_int_undef"    , /* 116 - 119 */
  "PfAR_long_none"   , "PfAR_long_byte"    , "PfAR_long_twobyte", "PfAR_long_long"    , /* 120 - 123 */
  "PfAR_long_undef"  , "FWDlit"            , "Pfunc"            , "mgoto"             , /* 124 - 127 */
  "mcgoto"           , "mcgoto2"           , "mcgoto3"          , "mcgoto4"           , /* 128 - 131 */
  "mcgoto74"         , "mcgoto17"          , "mccall17"         , "mcall"             , /* 132 - 135 */
  "mc_call"          , "res_word"          , "local"            , "unknown"           , /* 136 - 139 */
  "varlabel"         , "external"          , "global"           , "segment"           , /* 140 - 143 */
  "bank_addr"        , "bit_0"             , "bit_1"            , "bit_2"             , /* 144 - 147 */
  "bit_3"            , "bit_4"             , "bit_5"            , "bit_6"             , /* 148 - 151 */
  "bit_7"            , "debug"                                                          /* 152 - 153 */
};

static memmap_info_t *memmap_info_list      = NULL;
static memmap_info_t *memmap_info_list_last = NULL;

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

static const char *
_symbol_type_to_str(unsigned Type)
{
  static char err[64];

  if (Type < ARRAY_SIZE(symbol_type_str)) {
    return symbol_type_str[Type];
  }

  snprintf(err, sizeof(err), "invalid type: %u", Type);
  return err;
}

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

void _memmap_add(unsigned Start_addr, unsigned End_addr) {
  memmap_info_t*pnew = new memmap_info_t;
  memset(pnew,0,sizeof*pnew);
  pnew->start_addr = Start_addr;
  pnew->end_addr   = End_addr;

  if (!memmap_info_list) {
    /* The list is empty. */
    memmap_info_list = pnew;
  }else{
    /* Append the pnew node to the end of the list. */
    memmap_info_list_last->next = pnew;
  }

  memmap_info_list_last = pnew;
}

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

static void _memmap_free() {
  memmap_info_t *info;
  memmap_info_t *next;

  info = memmap_info_list;
  while (info) {
    next = info->next;
    free(info);
    info = next;
  }

  memmap_info_list      = NULL;
  memmap_info_list_last = NULL;
}

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

static void _memmap_create_used_map(unsigned Start_address) {
  const memmap_info_t *info;
  unsigned         block_start;
  unsigned         block_end;
  unsigned         offset;
  unsigned         size;

  info = memmap_info_list;
  if (!info) {
    memset(used_map, true, sizeof(used_map));
    return;
  }

  memset(used_map, false, sizeof(used_map));

  block_start = (Start_address / COD_BLOCK_SIZE) * COD_BLOCK_SIZE;
  block_end   = block_start + COD_BLOCK_SIZE;
  while (info) {
    if ((info->start_addr >= block_start) && (info->start_addr < block_end)) {
      offset = info->start_addr - block_start;
      size   = info->end_addr   - info->start_addr;

      if ((offset + size) > COD_BLOCK_SIZE) {
        size = COD_BLOCK_SIZE - offset;
      }

      if (size > 0) {
        /* This is a used ROM range. */
        memset(used_map+offset, true, size);
      }
    }else if (info->start_addr < block_start && info->start_addr < block_end) {
      size = info->end_addr - block_start;

      if (size > COD_BLOCK_SIZE) size = COD_BLOCK_SIZE;

      if (size > 0) {
        /* This is a used ROM range. */
        memset(used_map, true, size);
      }
    }else if (info->start_addr >= block_end) {
      /* The further values will be too high. */
      break;
    }

    info = info->next;
  }
}

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

/*
  _fget_line - read a line from a file.
*/

static char* _fget_line(int Line, char *Buffer, int Size, FILE *pFile) {
  static FILE *plastFile = NULL;
  static int   lastline  = -1;
  static long  lastPos   = -1;

  char        *ps;

  if (pFile == NULL) {
    return NULL;
  }

  /*
    If we read a line from the same file the last time we were called
    then see if we can take advantage of the file state:
   */
  if ((pFile != plastFile) ||       /* if the file is the same */
      (Line < (lastline - 1)) ||    /* and the line is past the previous */
      (ftell(pFile) != lastPos)) {  /* and the file hasn't been touched */
    plastFile = pFile;
    lastline  = 1;
    lastPos   = -1;
    rewind(pFile);
  }

  while (Line >= ++lastline) {
    ps = fgets(Buffer, Size, plastFile);
    assert(ps == Buffer);
  }

  ps = fgets(Buffer, Size, plastFile);
  assert(ps == Buffer);

  lastPos = ftell(plastFile);

  return Buffer;
}

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

/*
 * Dump directory block.
 */

static void _dump_directory_block(const uint8_t *Cblock, unsigned Block_num) {
  char         temp_buf[256];
  unsigned year;
  char         month[8];
  unsigned day;
  unsigned time;
  unsigned bytes_for_address;
#if defined(HAVE_LOCALE_H) && defined(HAVE_LANGINFO_H)
  struct tm    tm;
#endif

  /* Pascal style string. */
  gp_str_from_Pstr(temp_buf, sizeof(temp_buf), &Cblock[COD_DIR_DATE], COD_DIR_DATE_SIZE, NULL);
  time = gp_getl16(&Cblock[COD_DIR_TIME]);
  sscanf(temp_buf, "%u%3s%u", &day, month, &year);

#if defined(HAVE_LOCALE_H) && defined(HAVE_LANGINFO_H)
  memset(&tm, 0, sizeof(tm));
  snprintf(temp_buf, sizeof(temp_buf), "%u %s %u %u %u", year + 2000, month, day, time / 100, time % 100);
  strptime(temp_buf, "%Y %b %d %H %M", &tm);
  setlocale(LC_ALL, "");
#endif

  printf("Directory block:                %04x\n"
         "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", Block_num);

  /* Pascal style string. */
  printf("%03x - Source file:              %s\n",
         COD_DIR_SOURCE,
         gp_str_from_Pstr(temp_buf, sizeof(temp_buf), &Cblock[COD_DIR_SOURCE], COD_DIR_SOURCE_SIZE, NULL));

#if defined(HAVE_LOCALE_H) && defined(HAVE_LANGINFO_H)
  strftime(temp_buf, sizeof(temp_buf), nl_langinfo(D_FMT), &tm);
  printf("%03x - Date:                     %s\n",
         COD_DIR_DATE, temp_buf);
#else
  printf("%03x - Date:                     %u %s %u\n",
         COD_DIR_DATE, day, month, year + 2000);
#endif

#if defined(HAVE_LOCALE_H) && defined(HAVE_LANGINFO_H)
  strftime(temp_buf, sizeof(temp_buf), nl_langinfo(T_FMT), &tm);
  setlocale(LC_ALL, "C");
  printf("%03x - Time:                     %s\n",
         COD_DIR_TIME, temp_buf);
#else
  printf("%03x - Time:                     %02u:%02u\n",
         COD_DIR_TIME, time / 100, time % 100);
#endif

  /* Pascal style string. */
  printf("%03x - Compiler version:         %s\n",
         COD_DIR_VERSION,
         gp_str_from_Pstr(temp_buf, sizeof(temp_buf), &Cblock[COD_DIR_VERSION], COD_DIR_VERSION_SIZE, NULL));
  /* Pascal style string. */
  printf("%03x - Compiler:                 %s\n",
         COD_DIR_COMPILER,
         gp_str_from_Pstr(temp_buf, sizeof(temp_buf), &Cblock[COD_DIR_COMPILER], COD_DIR_COMPILER_SIZE, NULL));
  /* Pascal style string. */
  printf("%03x - Notice:                   %s\n",
         COD_DIR_NOTICE,
         gp_str_from_Pstr(temp_buf, sizeof(temp_buf), &Cblock[COD_DIR_NOTICE], COD_DIR_NOTICE_SIZE, NULL));

  bytes_for_address = Cblock[COD_DIR_ADDRSIZE];
  printf("%03x - Bytes for address:        %u\n", COD_DIR_ADDRSIZE, bytes_for_address);

  if (bytes_for_address != 0) {
    printf("WARNING: Address size looks suspicious.\n");
  }

  printf("%03x - High word of 64k address: %04x\n",
         COD_DIR_HIGHADDR, gp_getl16(&Cblock[COD_DIR_HIGHADDR]));
  printf("%03x - Next directory block:     %04x\n",
         COD_DIR_NEXTDIR, gp_getl16(&Cblock[COD_DIR_NEXTDIR]));
  printf("%03x - COD file version:         %d\n",
         COD_DIR_CODTYPE, gp_getl16(&Cblock[COD_DIR_CODTYPE]));
  /* Pascal style string. */
  printf("%03x - Processor:                %s\n",
         COD_DIR_PROCESSOR,
         gp_str_from_Pstr(temp_buf, sizeof(temp_buf), &Cblock[COD_DIR_PROCESSOR], COD_DIR_PROCESSOR_SIZE, NULL));

  printf("%03x,%03x - Short symbol table start block: %04x  end block: %04x\n",
         COD_DIR_SYMTAB, COD_DIR_SYMTAB + 2,
         gp_getl16(&Cblock[COD_DIR_SYMTAB]),
         gp_getl16(&Cblock[COD_DIR_SYMTAB + 2]));
  printf("%03x,%03x - File name table start block:    %04x  end block: %04x\n",
         COD_DIR_NAMTAB, COD_DIR_NAMTAB + 2,
         gp_getl16(&Cblock[COD_DIR_NAMTAB]),
         gp_getl16(&Cblock[COD_DIR_NAMTAB + 2]));
  printf("%03x,%03x - Source info table start block:  %04x  end block: %04x\n",
         COD_DIR_LSTTAB, COD_DIR_LSTTAB + 2,
         gp_getl16(&Cblock[COD_DIR_LSTTAB]),
         gp_getl16(&Cblock[COD_DIR_LSTTAB + 2]));
  printf("%03x,%03x - Rom table start block:          %04x  end block: %04x\n",
         COD_DIR_MEMMAP, COD_DIR_MEMMAP + 2,
         gp_getl16(&Cblock[COD_DIR_MEMMAP]),
         gp_getl16(&Cblock[COD_DIR_MEMMAP + 2]));
  printf("%03x,%03x - Local scope table start block:  %04x  end block: %04x\n",
         COD_DIR_LOCALVAR, COD_DIR_LOCALVAR + 2,
         gp_getl16(&Cblock[COD_DIR_LOCALVAR]),
         gp_getl16(&Cblock[COD_DIR_LOCALVAR + 2]));
  printf("%03x,%03x - Long symbol table start block:  %04x  end block: %04x\n",
         COD_DIR_LSYMTAB, COD_DIR_LSYMTAB + 2,
         gp_getl16(&Cblock[COD_DIR_LSYMTAB]),
         gp_getl16(&Cblock[COD_DIR_LSYMTAB + 2]));
  printf("%03x,%03x - Debug messages start block:     %04x  end block: %04x\n",
         COD_DIR_MESSTAB, COD_DIR_MESSTAB + 2,
         gp_getl16(&Cblock[COD_DIR_MESSTAB]),
         gp_getl16(&Cblock[COD_DIR_MESSTAB + 2]));
  putchar('\n');
}

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

/*
 * Dump directory block code index.
 */

static void _dump_index(proc_class_t Class, const uint8_t *Cblock) {
  unsigned _64k_base;
  unsigned i;
  unsigned curr_block;
  int          addr_digits;

  printf("Code blocks index:\n"
         "Block range    Block number\n"
         "---------------------------\n");

  _64k_base   = IMemAddrFromBase((unsigned)gp_getu16(&Cblock[COD_DIR_HIGHADDR]));
  addr_digits = Class->addr_digits;

  for (i = 0; i < COD_CODE_IMAGE_BLOCKS; ++i) {
    curr_block = gp_getu16(&Cblock[i * 2]);

    if (curr_block != 0) {
      printf("%0*x-%0*x:    %u\n",
             addr_digits,  _64k_base |  MemOffsFromBlock(i),
             addr_digits, (_64k_base | (MemOffsFromBlock(i + 1) - 1)),
             curr_block);
    }
  }

  putchar('\n');
}

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

/*
 * Dump directory block.
 */

void dump_directory_blocks(const DirBlockInfo *Main_dir, proc_class_t Class) {
  const DirBlockInfo *dbi;
  unsigned        block_num;

  block_num = 0;
  dbi       = Main_dir;

  do {
    _dump_directory_block(dbi->dir, block_num);
    _dump_index(Class, dbi->dir);
    block_num = gp_getl16(&dbi->dir[COD_DIR_NEXTDIR]);
    dbi = dbi->next;
  } while (dbi);
}

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

/*
 * ROM usage information.
 */

void dump_memmap(FILE *Code_file, const DirBlockInfo *Main_dir,
	proc_class_t Class, bool Make_list) {
  const DirBlockInfo *dbi;
  unsigned        _64k_base;
  int                 addr_digits;
  unsigned        start_map_block;
  unsigned        end_map_block;
  unsigned        i;
  unsigned        j;
  unsigned        offset;
  const uint8_t      *record;
  unsigned        first_offset;
  unsigned        last_offset;
  unsigned        start_addr;
  unsigned        end_addr;
  bool          first;

  first       = true;
  addr_digits = Class->addr_digits;
  dbi         = Main_dir;

  do {
    _64k_base       = IMemAddrFromBase((unsigned)gp_getu16(&dbi->dir[COD_DIR_HIGHADDR]));
    start_map_block = gp_getu16(&dbi->dir[COD_DIR_MEMMAP]);

    if (start_map_block != 0) {
      end_map_block = gp_getu16(&dbi->dir[COD_DIR_MEMMAP + 2]);

      if (first) {
        printf("ROM Usage:\n"
               "----------------------------\n");
        first = false;
      }

      for (j = start_map_block; j <= end_map_block; j++) {
        read_block(Code_file, cod_block, j);

        for (i = 0, offset = 0; i < COD_CODE_IMAGE_BLOCKS; offset += COD_MAPENTRY_SIZE, ++i) {
          record = &cod_block[offset];
          first_offset = gp_getl16(&record[COD_MAPTAB_START]);
          last_offset  = gp_getl16(&record[COD_MAPTAB_LAST]);

          if ((first_offset != 0) || (last_offset != 0)) {
            start_addr = _64k_base + first_offset;
            end_addr   = _64k_base + last_offset + 1;
            printf("using ROM %0*x to %0*x\n",
                   addr_digits, gp_processor_insn_from_byte_c(Class, start_addr),
                   addr_digits, gp_processor_insn_from_byte_c(Class, end_addr) - 1);

            if (Make_list) {
              _memmap_add(start_addr, end_addr);
            }
          }
        }
      }
    }
    else if (first) {
      printf("No ROM usage information available.\n");
    }

    putchar('\n');

    dbi = dbi->next;
  } while (dbi);
}

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

static bool _is_empty_to_last(unsigned Index, unsigned End) {
  while (Index < End) {
    if (used_map[Index * WORD_SIZE]) {
      return false;
    }

    ++Index;
  }

  return true;
}

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

/*
 * Dump all of the machine code in the .cod file.
 */

#define CODE_COLUMN_NUM         8
#define CODE_COLUMN_NUM_WIDE    16

void dump_code(FILE *Code_file, const DirBlockInfo *Main_dir,
	pic_processor_t Processor, bool Wide_dump) {
  proc_class_t        pclass;
  char                border_gap0[16];
  char                border_gap1[16];
  char                buffer[BUFSIZ];
  const DirBlockInfo *dbi;
  MemBlock_t         *data;
  int                 addr_digits;
  unsigned        _64k_base;
  unsigned        block_index;
  unsigned        byte_address;
  int                 num_words;
  unsigned        bsr_boundary;
  unsigned        i;
  unsigned        j;
  unsigned        k;
  unsigned        column_num;
  uint16_t            word;
  bool          used_prev;
  bool          used_act;
  bool          empty_signal;
  bool          empty_line;

  pclass = Processor->pclass;
  dump_memmap(Code_file, Main_dir, pclass, true);

  bsr_boundary = gp_processor_bsr_boundary(Processor);
  addr_digits  = pclass->addr_digits;
  column_num   = (Wide_dump) ? CODE_COLUMN_NUM_WIDE : CODE_COLUMN_NUM;
  dbi          = Main_dir;

  i = addr_digits + 2;

  j = (i < (sizeof(border_gap0) - 1)) ? i : (sizeof(border_gap0) - 1);
  if (j > 0) {
    memset(border_gap0, ' ', j);
  }

  border_gap0[j] = '\0';

  j = (i < (sizeof(border_gap1) - 1)) ? i : (sizeof(border_gap1) - 1);
  if (j > 0) {
    memset(border_gap1, '-', j);
  }

  border_gap1[j] = '\0';

  printf("Formatted Code Dump:\n");

  do {
    _64k_base = IMemAddrFromBase((unsigned)gp_getu16(&dbi->dir[COD_DIR_HIGHADDR]));
    for (k = 0; k < COD_CODE_IMAGE_BLOCKS; k++) {
      block_index = gp_getu16(&dbi->dir[(k + COD_DIR_CODE) * WORD_SIZE]);

      if (block_index != 0) {
        read_block(Code_file, cod_block, block_index);

        byte_address = _64k_base + (k * COD_BLOCK_N_WORDS) * WORD_SIZE;
        if (Wide_dump) {
          printf("%s---------------------------------------------------------------------------------\n"
                 " Block %u -- %0*x-%0*x\n"
                 "%s+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+\n"
                 "%s|  0 |  1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 |  a |  b |  c |  d |  e |  f |\n"
                 "%s+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+\n",
                 border_gap1,
                 block_index,
                 addr_digits, byte_address,
                 addr_digits, byte_address + COD_BLOCK_SIZE - 1,
                 border_gap1,
                 border_gap0,
                 border_gap1);
        }
        else {
          printf("%s-----------------------------------------\n"
                 " Block %u -- %0*x-%0*x\n"
                 "%s+----+----+----+----+----+----+----+----+\n"
                 "%s| 0;8| 1;9| 2;a| 3;b| 4;c| 5;d| 6;e| 7;f|\n"
                 "%s+----+----+----+----+----+----+----+----+\n",
                 border_gap1,
                 block_index,
                 addr_digits, byte_address,
                 addr_digits, byte_address + COD_BLOCK_SIZE - 1,
                 border_gap1,
                 border_gap0,
                 border_gap1);
        }

        _memmap_create_used_map(byte_address);

	/* Shows the COD block. */

	if (memmap_info_list) {
	  /* Use the informations of memory map. */
          i = 0;
          do {
            empty_line = true;
            for (j = 0; j < column_num; j++) {
              if (used_map[(i + j) * WORD_SIZE]) {
                empty_line = false;
                break;
              }
            }

            if (! empty_line) {
              byte_address = _64k_base + (k * COD_BLOCK_N_WORDS + i) * WORD_SIZE;
              printf("%0*x: ", addr_digits, gp_processor_insn_from_byte_c(pclass, byte_address));

              for (j = 0; j < column_num; j++) {
                if (used_map[(i + j) * WORD_SIZE]) {
                  /* This is a used ROM word. */
                  printf(" %04x", gp_getu16(&cod_block[(i + j) * WORD_SIZE]));
                }
                else if (!_is_empty_to_last(i + j, i + column_num)) {
                  /* In this line there is also at least one used ROM word. */
                  printf(" ....");
                }
                else {
                  /* Sooner ends the line. */
                  break;
                }
              }
              putchar('\n');
            }

            i += column_num;
          } while (i < COD_BLOCK_N_WORDS);
	}
	else {
	  /* No memory map. */
          i = 0;
          do {
            empty_line = true;
            for (j = 0; j < column_num; j++) {
              if (gp_getu16(&cod_block[(i + j) * WORD_SIZE]) != 0) {
                empty_line = false;
                break;
              }
            }

            if (! empty_line) {
              byte_address = _64k_base + (k * COD_BLOCK_N_WORDS + i) * WORD_SIZE;
              printf("%0*x: ", addr_digits, gp_processor_insn_from_byte_c(pclass, byte_address));

              for (j = 0; j < column_num; j++) {
                printf(" %04x", gp_getu16(&cod_block[(i + j) * WORD_SIZE]));
              }

              putchar('\n');
            }

            i += column_num;
          } while (i < COD_BLOCK_N_WORDS);
        }

        putchar('\n');

        /* Create the instruction memory and populates from COD block. */

        byte_address = _64k_base + k * COD_BLOCK_N_WORDS * WORD_SIZE;
        data         = gp_mem_i_create();
        for (j = 0; j < COD_BLOCK_SIZE; j++) {
          gp_mem_b_put(data, byte_address + j, cod_block[j], NULL, NULL);
        }

        /* Disassembles this code array. */

        i            = 0;
        used_prev    = false;
        empty_signal = false;
        do {
          num_words = 1;
          used_act  = used_map[i * WORD_SIZE];

          if (used_act) {
            if (empty_signal) {
              /* This is a previous empty block. */
              printf("  . . . . . . . . . . .\n");
              empty_signal = false;
            }

            if (pclass->i_memory_get(data, byte_address, &word, NULL, NULL) != W_USED_ALL) {
              /* Internal memory handling error. */
              assert(0);
            }

            num_words = gp_disassemble(data, byte_address, pclass, bsr_boundary, 0,
                                       GPDIS_SHOW_ALL_BRANCH, buffer, sizeof(buffer), 0);
            printf("%0*x:  %04x  %s\n", addr_digits, gp_processor_insn_from_byte_c(pclass, byte_address), word, buffer);

            if (num_words != 1) {
              if (pclass->i_memory_get(data, byte_address + WORD_SIZE, &word, NULL, NULL) != W_USED_ALL) {
                /* Internal memory handling error. */
                assert(0);
              }

              printf("%0*x:  %04x\n", addr_digits, gp_processor_insn_from_byte_c(pclass, byte_address + WORD_SIZE), word);
            }

            empty_signal = false;
          }
          else if (used_prev && (!used_act)) {
            empty_signal = true;
          }

          used_prev = used_act;

          byte_address += num_words * WORD_SIZE;
          i            += num_words;
        } while (i < COD_BLOCK_N_WORDS);

        gp_mem_i_free(data);
      } /* if (block_index != 0) */
    } /* for (k = 0; k < COD_CODE_IMAGE_BLOCKS; k++) */

    dbi = dbi->next;
  } while (dbi);

  putchar('\n');
  _memmap_free();
}

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

/*
 * Dump all of the (short) Symbol Table stuff in the .cod file.
 */

void dump_symbols(FILE *Code_file, const DirBlockInfo *Main_dir) {
  unsigned   start_block;
  unsigned   end_block;
  unsigned   i;
  unsigned   j;
  unsigned   offset;
  const uint8_t *record;
  unsigned   length;
  char           name[COD_SSYMBOL_NAME_LENGTH + 1];
  unsigned   type;
  unsigned   value;

  start_block = gp_getu16(&Main_dir->dir[COD_DIR_SYMTAB]);

  if (start_block != 0) {
    end_block = gp_getu16(&Main_dir->dir[COD_DIR_SYMTAB + 2]);

    printf("Symbol Table Information:\n"
           "-------------------------\n");

    for (j = start_block; j <= end_block; j++) {
      read_block(Code_file, cod_block, j);

      for (i = 0, offset = 0; i < SYMBOLS_PER_BLOCK; offset += SSYMBOL_SIZE, ++i) {
        record = &cod_block[offset];
        length = record[COD_SSYMBOL_NAME];

        if (length != 0) {
          gp_str_from_Pstr(name, sizeof(name), &record[COD_SSYMBOL_NAME], COD_SSYMBOL_NAME_SIZE, NULL);
          type  = record[COD_SSYMBOL_STYPE];
          value = gp_getu16(&record[COD_SSYMBOL_SVALUE]);
          printf("%-12s = %04x (%6d), type = %s\n", name, value, value, _symbol_type_to_str(type));
        }
      }
    }
  }
  else {
    printf("No symbol table info.\n");
  }

  putchar('\n');
}

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

static unsigned _lsymbols_max_length(FILE *Code_file, const DirBlockInfo *Main_dir) {
  unsigned start_block;
  unsigned end_block;
  unsigned i;
  unsigned j;
  unsigned length;
  unsigned max_length;

  max_length  = 0;
  start_block = gp_getu16(&Main_dir->dir[COD_DIR_LSYMTAB]);

  if (start_block != 0) {
    end_block = gp_getu16(&Main_dir->dir[COD_DIR_LSYMTAB + 2]);

    max_length = 0;
    for (j = start_block; j <= end_block; j++) {
      read_block(Code_file, cod_block, j);

      for (i = 0; i < COD_BLOCK_SIZE; ) {
        /* Pascal style string. */
        length = cod_block[i];

        if (length == 0) {
          break;
        }

        if (max_length < length) {
          max_length = length;
        }

        i += length + COD_LSYMBOL_EXTRA;
      }
    }
  }

  return max_length;
}

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

/*
 * Dump all of the Long Symbol Table stuff in the .cod file.
 */

void dump_lsymbols(FILE *Code_file, const DirBlockInfo *Main_dir) {
  unsigned   start_block;
  unsigned   end_block;
  unsigned   i;
  unsigned   j;
  const uint8_t *sym;
  unsigned   length;
  char           name[COD_LSYMBOL_NAME_MAX_LENGTH + 1];
  unsigned   type;
  unsigned   value;
  int            symbol_align;

  symbol_align = _lsymbols_max_length(Code_file, Main_dir);
  start_block  = gp_getu16(&Main_dir->dir[COD_DIR_LSYMTAB]);

  if (start_block != 0) {
    end_block = gp_getu16(&Main_dir->dir[COD_DIR_LSYMTAB + 2]);

    printf("Long Symbol Table Information:\n"
           "------------------------------------------------------------------------\n");

    for (j = start_block; j <= end_block; j++) {
      read_block(Code_file, cod_block, j);

      for (i = 0; i < COD_BLOCK_SIZE; ) {
        /* Pascal style string. */
        sym    = &cod_block[i + COD_LSYMBOL_NAME];
        length = *sym;

        if (length == 0) {
          break;
        }

        gp_str_from_Pstr(name, sizeof(name), sym, COD_LSYMBOL_NAME_MAX_SIZE, NULL);
        type  = gp_getu16(&sym[length + COD_LSYMBOL_TYPE]);
        /* read big endian */
        value = gp_getb32(&sym[length + COD_LSYMBOL_VALUE]);
        printf("%-*s = %08x (%11d), type = %s\n", symbol_align, name, value, value, _symbol_type_to_str(type));
        i += length + COD_LSYMBOL_EXTRA;
      }
    }
  }
  else {
    printf("No long symbol table info.\n");
  }

  putchar('\n');
}

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

/*
 * Source files.
 */

void dump_source_files(FILE *Code_file, const DirBlockInfo *Main_dir) {
  unsigned  start_block;
  unsigned  end_block;
  unsigned  i;
  unsigned  offset;
  unsigned  length;
  char          name_str[COD_FILE_NAME_LENGTH + 1];
  char         *name;

  start_block = gp_getu16(&Main_dir->dir[COD_DIR_NAMTAB]);

  if (start_block != 0) {
    end_block = gp_getu16(&Main_dir->dir[COD_DIR_NAMTAB + 2]);

    printf("Source File Information:\n"
           "------------------------\n");

    for (i = start_block; i <= end_block; ++i) {
      read_block(Code_file, cod_block, i);

      for (offset = 0; offset < COD_BLOCK_SIZE; ) {
        length = cod_block[offset];

        if (length > 0) {
          gp_str_from_Pstr(name_str, sizeof(name_str), &cod_block[offset], COD_FILE_NAME_SIZE, NULL);
          name = GP_Strdup(name_str);
          source_file_names[number_of_source_files] = name;
          printf("%s\n", name);
          source_files[number_of_source_files] = fopen(name, "rt");
          number_of_source_files++;

          if (number_of_source_files >= MAX_SOURCE_FILES) {
            fprintf(stderr, "Too many source files, increase MAX_SOURCE_FILES and recompile the program.\n");
            exit(1);
          }

          /* In this way will find the shorter (64 bytes length) names also. */
	  length += COD_SHORT_FILE_NAME_SIZE - 1;
	  length /= COD_SHORT_FILE_NAME_SIZE;
	  length *= COD_SHORT_FILE_NAME_SIZE;
          offset += length;
        }
        else {
          offset += COD_SHORT_FILE_NAME_SIZE;
        }
      }
    }
  }
  else {
    printf("No source file info.\n");
  }

  putchar('\n');
}

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

static const char* _mod_flags_to_str(int Mode) {
  static char fl[9];

  fl[0] = (Mode & COD_LS_SMOD_FLAG_C1) ? 'C' : '.';
  fl[1] = (Mode & COD_LS_SMOD_FLAG_F)  ? 'F' : '.';
  fl[2] = (Mode & COD_LS_SMOD_FLAG_I)  ? 'I' : '.';
  fl[3] = (Mode & COD_LS_SMOD_FLAG_D)  ? 'D' : '.';
  fl[4] = (Mode & COD_LS_SMOD_FLAG_C0) ? 'C' : '.';
  fl[5] = (Mode & COD_LS_SMOD_FLAG_L)  ? 'L' : '.';
  fl[6] = (Mode & COD_LS_SMOD_FLAG_N)  ? 'N' : '.';
  fl[7] = (Mode & COD_LS_SMOD_FLAG_A)  ? 'A' : '.';
  fl[8] = '\0';

  return fl;
}

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

/*
 * Line number info from the source files.
 */

void dump_line_symbols(FILE *Code_file, const DirBlockInfo *Main_dir) {
  static unsigned  lst_line_number = 1;
  static unsigned  last_src_line   = 0;

  char                 line[2048];
  char                 nbuf[128];
  const char          *source_file_name;
  const DirBlockInfo  *dbi;
  bool           has_line_num_info;
  unsigned         _64k_base;
  unsigned         start_block;
  unsigned         end_block;
  unsigned         i;
  unsigned         j;
  unsigned         offset;
  const uint8_t       *record;
  unsigned         src_file_num;
  unsigned         src_mod_flag;
  unsigned         src_line_num;
  unsigned         insn_offset;
  unsigned         _64k_seg_address;

  has_line_num_info = false;
  dbi               = Main_dir;
  while (dbi) {
    _64k_base        = gp_getu16(&dbi->dir[COD_DIR_HIGHADDR]);
    _64k_seg_address = IMemAddrFromBase(_64k_base);

    start_block = gp_getu16(&dbi->dir[COD_DIR_LSTTAB]);

    if (start_block != 0) {
      end_block = gp_getu16(&dbi->dir[COD_DIR_LSTTAB + 2]);

      if (!has_line_num_info) {
        has_line_num_info = true;

        printf("Line Number Information:\n"
               " LstLn  SrcLn  Addr    Flags        FileName\n"
               " -----  -----  ------  -----------  ---------------------------------------------------------------\n");
      }

      for (j = start_block; j <= end_block; j++) {
        read_block(Code_file, cod_block, j);

        for (i = 0, offset = 0; i < COD_MAX_LINE_SYM; offset += COD_LINE_SYM_SIZE, ++i) {
          record       = &cod_block[offset];
          src_file_num = record[COD_LS_SFILE];
          src_mod_flag = record[COD_LS_SMOD];
          src_line_num = gp_getu16(&record[COD_LS_SLINE]);
          insn_offset  = gp_getu16(&record[COD_LS_SLOC]);

          if (((src_file_num != 0) || (src_mod_flag != 0) || (src_line_num != 0) || (insn_offset != 0)) &&
              (FlagIsClr(src_mod_flag, COD_LS_SMOD_FLAG_L))) {
            if (src_file_num < number_of_source_files) {
              source_file_name = source_file_names[src_file_num];
            }
            else {
              snprintf(nbuf, sizeof(nbuf), "Bad source file index: %u", src_file_num);
              source_file_name = nbuf;
            }

            if ((source_file_name) && (source_file_name[0] != '\0')) {
              printf(" %5u  %5u  %06x  %2x %s  %s\n",
                     lst_line_number, src_line_num, _64k_seg_address | insn_offset,
                     src_mod_flag, _mod_flags_to_str(src_mod_flag), source_file_name);
            }
            else {
              printf(" %5u  %5u  %06x  %2x %s\n",
                     lst_line_number, src_line_num, _64k_seg_address | insn_offset,
                     src_mod_flag, _mod_flags_to_str(src_mod_flag));
            }

            lst_line_number++;

            if ((src_file_num < number_of_source_files) && (src_line_num != last_src_line)) {
              if (source_files[src_file_num]) {
                _fget_line(src_line_num, line, sizeof(line), source_files[src_file_num]);
                printf("%s", line);
              }
              else {
                printf("ERROR: Source file \"%s\" does not exist.\n", source_file_names[src_file_num]);
              }
            }
          }

          last_src_line = src_line_num;
        } /* for (i = 0, offset = 0; i < COD_MAX_LINE_SYM; ...  */
      } /* for (j = start_block; j <= end_block; j++) */
    } /* if (start_block != 0) */

    dbi = dbi->next;
  } /* while (dbi) */

  if (!has_line_num_info) {
    printf("No line number info.\n");
  }

  putchar('\n');
}

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

/*
 * Debug Message area.
 */

void dump_debug_message_area(FILE *Code_file, const DirBlockInfo *Main_dir) {
  unsigned   start_block;
  unsigned   end_block;
  unsigned   i;
  unsigned   j;
  const uint8_t *record;
  unsigned   address;
  char           type;
  unsigned   length;
  char           message[COD_DEBUG_MSG_MAX_LENGTH + 1];

  start_block = gp_getu16(&Main_dir->dir[COD_DIR_MESSTAB]);

  if (start_block != 0) {
    end_block = gp_getu16(&Main_dir->dir[COD_DIR_MESSTAB + 2]);

    printf("Debug Message area:\n"
           "     Addr  Cmd  Message\n"
           " --------  ---  -------------------------------------\n");

    for (i = start_block; i <= end_block; i++) {
      read_block(Code_file, cod_block, i);

      for (j = 0; j < (COD_BLOCK_SIZE - COD_DEBUG_MIN_SIZE); ) {
        /* read big endian */
        record  = &cod_block[j];
        address = gp_getb32(&record[COD_DEBUG_ADDR]);
        type    = record[COD_DEBUG_CMD];

        if (type == '\0') {
          break;
        }

        /* Pascal style string. */
        length = record[COD_DEBUG_MSG];
        gp_str_from_Pstr(message, sizeof(message), &record[COD_DEBUG_MSG], COD_DEBUG_MSG_MAX_SIZE, NULL);
        printf(" %8x    %c  %s\n", address, type, message);
        j += length + COD_DEBUG_EXTRA;
      } /* for (j = 0; ... */
    } /* for (i = start_block; ... */
  }
  else {
    printf("No Debug Message information available.\n");
  }

  putchar('\n');
}

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

/*
 * Display the local symbol table information.
 */

void dump_local_vars(FILE *Code_file, const DirBlockInfo *Main_dir, proc_class_t Class) {
  unsigned   start_block;
  unsigned   end_block;
  unsigned   i;
  unsigned   j;
  unsigned   offset;
  const uint8_t *record;
  unsigned   length;
  unsigned   start;
  unsigned   stop;
  char           name[COD_SSYMBOL_NAME_LENGTH + 1];
  unsigned   type;
  unsigned   value;

  start_block = gp_getu16(&Main_dir->dir[COD_DIR_LOCALVAR]);

  if (start_block != 0) {
    end_block = gp_getu16(&Main_dir->dir[COD_DIR_LOCALVAR + 2]);

    printf("Local Symbol Scoping Information:\n"
           "---------------------------------\n");

    for (i = start_block; i <= end_block; i++) {
      read_block(Code_file, cod_block, i);

      for (j = 0, offset = 0; j < SYMBOLS_PER_BLOCK; offset += SSYMBOL_SIZE, ++j) {
        record = &cod_block[offset];
        length = record[COD_SSYMBOL_NAME];

        if (length != 0) {
          if (memcmp(&record[COD_SSYMBOL_NAME + 1], "__LOCAL", length) == 0) {
            start = gp_getl32(&record[COD_SSYMBOL_START]);
            stop  = gp_getl32(&record[COD_SSYMBOL_STOP]);

            printf("Local symbols between %06x and %06x:  ",
                   gp_processor_insn_from_byte_c(Class, start),
                   gp_processor_insn_from_byte_c(Class, stop + 1) - 1);
          }
          else {
            gp_str_from_Pstr(name, sizeof(name), &record[COD_SSYMBOL_NAME], COD_SSYMBOL_NAME_SIZE, NULL);
            type  = record[COD_SSYMBOL_STYPE];
            value = gp_getl16(&record[COD_SSYMBOL_SVALUE]);
            printf("%-12s = %04x (%6d), type = %s\n", name, value, value, _symbol_type_to_str(type));
          }
        }
      }
    }
  }
  else {
    printf("No local variable scoping info available.\n");
  }

  putchar('\n');
}

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

void dump_free() {
  unsigned i;

  if (number_of_source_files > 0) {
    for (i = 0; i < number_of_source_files; ++i) {
      if (source_file_names[i]) {
        free(source_file_names[i]);
        source_file_names[i] = NULL;
      }

      if (source_files[i]) {
        fclose(source_files[i]);
        source_files[i] = NULL;
      }
    }

    number_of_source_files = 0;
  }
}
Detected encoding: UTF-80