/* ".LST" file output for gpasm
   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
   James Bowman, Craig Franklin

This file is part of gputils.

gputils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

gputils is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with gputils; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

#include "stdhdr.h"

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

extern int _16bit_core;

void lst_throw()
{
  if(state.lst.f) {
    state.lst.page++;
    fprintf(state.lst.f,
	    "%s%-32s%-12s%-29sPAGE %2d\n%s\n%s\n",
	    (state.lst.page == 1) ? "" : "\f",
	    GPASM_VERSION_STRING,
	    state.srcfilename,
	    state.lst.startdate,
	    state.lst.page,
	    state.lst.title_name,
            state.lst.subtitle_name);
    state.lst.lineofpage = 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;
  }
}

void lst_line(char *line)
{
  if (state.lst.f) {
    if (state.lst.linesperpage != 0) {
      if ((state.lst.lineofpage++ % state.lst.linesperpage) == 0) {
        lst_throw();
        lst_line("LOC  OBJECT CODE     LINE SOURCE TEXT");
        lst_line("  VALUE");
        lst_line(" ");
      }
    }
    fprintf(state.lst.f, "%s\n", line);
    state.lst.line_number++;
    cod_lst_line(COD_NORMAL_LST_LINE);
  }
}

void lst_init()
{
  state.lst.lineofpage = 0;
  state.lst.page = 0;
  state.lst.linesperpage = 60;
  state.lst.line_number = 1;
  state.lst.memorymap = 1;
  state.lst.symboltable = 1;

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

  if (!state.cmd_line.macro_expand){
    state.lst.expand = true;
  }  
  
  if (state.cmd_line.lst_force)
    state.lst.force = true; 
  else
    state.lst.force = false;

  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 */

  if (state.lstfile != named) {
    strncpy(state.lstfilename, state.basefilename, sizeof(state.lstfilename));
    strncat(state.lstfilename, ".lst", sizeof(state.lstfilename));  
  }

  if (state.lstfile == suppress) {
    state.lst.f = NULL;
    state.lst.enabled = false;
    unlink(state.lstfilename);
  } else {
    state.lst.f = fopen(state.lstfilename, "wt");
    if (state.lst.f == NULL) {
      perror(state.lstfilename);
      exit(1);
    }
    state.lst.enabled = true;
  }

  cod_lst_line(COD_FIRST_LST_LINE);

}

void lst_memory_map(MemBlock *m)
{
  char buf[BUFSIZ];
  int i, j, base, row_used, num_per_line, num_per_block;

  lst_line("");
  lst_line("");
  lst_line("MEMORY USAGE MAP ('X' = Used,  '-' = Unused)");
  lst_line("");

  if (_16bit_core) {
    /* uses byte addressing so read half as many words */
    num_per_line = 32;
    num_per_block = 8;
  } else {
    num_per_line = 64;
    num_per_block = 16;
  }

  while(m) {
    assert(m->memory != NULL);

    base = (m->base << I_MEM_BITS);

    for (i = 0; i < MAX_I_MEM; i += num_per_line) {
      row_used = 0;

      for (j = 0; j < num_per_line; j++) {
	if (m->memory[i+j] & MEM_USED_MASK) {
	  row_used = 1;
	  break;
	}
      }

      if(row_used) {
        snprintf(buf, sizeof(buf), "%08x :", (i + base) << _16bit_core);
	for (j = 0; j < num_per_line; j++) {
          if ((j % num_per_block) == 0) {
	    strncat(buf, " ", sizeof(buf));
          }
          if (m->memory[i + j] & MEM_USED_MASK) {
	    strncat(buf, "X", sizeof(buf));
	    if (_16bit_core) {
	      /* each word has two bytes */
	      strncat(buf, "X", sizeof(buf));
	    }
          } else {
	    strncat(buf, "-", sizeof(buf));
	    if (_16bit_core) {
	      /* each word has two bytes */
	      strncat(buf, "-", sizeof(buf));
	    }
          }
        }

        lst_line(buf);
      }
    }

    m = m->next;
  }

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

  snprintf(buf, sizeof(buf), 
           "Program Memory Words Used: %i",
           i_memory_used(state.i_memory));
  lst_line(buf);

}

void lst_close()
{
  cod_lst_line(COD_LAST_LST_LINE);

  if (state.lst.f) {
    fprintf(state.lst.f,
	    "\n\n");
    fprintf(state.lst.f,
	    "Errors   : %7d\n",
	    state.num.errors);
    fprintf(state.lst.f,
	    "Warnings : %7d reported, %7d suppressed\n",
	    state.num.warnings,
            state.num.warnings_suppressed);
    fprintf(state.lst.f,
	    "Messages : %7d reported, %7d suppressed\n",
	    state.num.messages,
            state.num.messages_suppressed);
    fprintf(state.lst.f,
	    "\f\n");
  
    fclose(state.lst.f);
  }
}

void lst_format_line(char *src_line, int value)
{
  char m[BUFSIZ];
  char buf[BUFSIZ];
  unsigned int emitted = 0;

  assert(src_line != NULL);
  
  switch (state.lst.line.linetype) {
  case equ: 
  case set:
    snprintf(m, sizeof(m), "  %08X", value);
    strncat(m, "     ", sizeof(m));
    break;
  case org:
    snprintf(m, sizeof(m), "%04X      ", state.org << _16bit_core);
    strncat(m, "     ", sizeof(m));
    break;
  case idlocs:
    /* not used for 16 bit devices, config is used */
    snprintf(m, sizeof(m), "%04X %04X %04X ",
            state.device.id_location,
            i_memory_get(state.i_memory, state.device.id_location) & 0xffff,
            i_memory_get(state.i_memory, 
		         state.device.id_location + 1) & 0xffff);
    break;
  case insn:
    snprintf(m, sizeof(m), "%04X ", state.lst.line.was_org << _16bit_core);
    emitted = state.org - state.lst.line.was_org;
    if (emitted >= 1) {
      snprintf(buf, sizeof(buf), "%04X ", i_memory_get(state.i_memory, 
			               state.lst.line.was_org) & 0xffff);
      strncat(m, buf, sizeof(m));
    } else
      strncat(m, "     ", sizeof(m));
    if (emitted >= 2) {
      snprintf(buf, sizeof(buf), "%04X ", i_memory_get(state.i_memory, 
			               state.lst.line.was_org + 1) & 0xffff);
      strncat(m, buf, sizeof(buf));
    } else
      strncat(m, "     ", sizeof(m));
    break;
  case config:
    if(_16bit_core) {
      /* config data is byte addressable, but we only want to print
	 words in the list file. */
      if (state.lst.config_address == CONFIG4L) {
        /* Special case */
        snprintf(m, sizeof(m), "%06X %04X    ",   
                 state.lst.config_address,
                 i_memory_get(state.i_memory, 
		 	      state.lst.config_address >> 1) & 0xffff);
      } else if((state.lst.config_address & 0x1) == 0) {
        /* if it is an even address don't print anything */
	strncpy(m, "               ", sizeof(m));
      } else {
        snprintf(m, sizeof(m), "%06X %04X    ",   
                 state.lst.config_address - 1,
                 i_memory_get(state.i_memory, 
			     (state.lst.config_address - 1) >> 1) & 0xffff);
      }
    } else {
      snprintf(m, sizeof(m), "%06X %04X    ",   
               state.lst.config_address,
               i_memory_get(state.i_memory, 
		            state.lst.config_address) & 0xffff);
    }
    break;
  case res:
    strncpy(m, "               ", sizeof(m));
    if (SECTION_FLAGS & STYP_TEXT) {
      /* generate line numbers for res directives in program memory */
      emitted = state.org - state.lst.line.was_org;
    }
    break;
  case sec:
  case dir:
  case none:
  default:
    strncpy(m, "               ", sizeof(m));
    break;
  }

  if (state.stGlobal == state.stTop) {
    snprintf(buf, sizeof(buf), "%05d ", state.src->line_number);
  } else {		  
    snprintf(buf, sizeof(buf), "    M ");
  }
  strncat(m, buf, sizeof(m));

  /* Now copy 'l' to 'e', expanding tabs as required */
  {
    int column = 0;
    char *old;
    char *e = m + strlen(m);
        
    old = src_line;

    while (*old) {
      if (*old == '\t') {
        *e++ = ' ';
        column++;
	while ((column % state.lst.tabstop) != 0) {
          *e++ = ' ';
          column++;
        }
      } else {
        *e++ = *old;
        column++;
      }
      old++;
    }
    *e = '\0';		/* terminate the new string */
  }

  coff_linenum(emitted);

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

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

  lst_line(m);

#ifdef GPUTILS_DEBUG
  fprintf(stderr, "%s\n\n", m);
#endif

  if (state.lst.line.linetype == idlocs) {
    snprintf(m, sizeof(m), "     %04X %04X ",
             i_memory_get(state.i_memory, 
		          state.device.id_location + 2) & 0xffff,
             i_memory_get(state.i_memory, 
		          state.device.id_location + 3) & 0xffff);      
    lst_line(m);   
  }

  if (emitted > 2) {
    int i;

    for (i = 2; i < emitted; i += 2) {
      if ((i + 1) < emitted)
        snprintf(m, sizeof(m), "%04X %04X %04X",
                 ((state.lst.line.was_org + i) << _16bit_core),
                 i_memory_get(state.i_memory, 
                              state.lst.line.was_org + i) & 0xffff,
		 i_memory_get(state.i_memory, 
                              state.lst.line.was_org + i + 1) & 0xffff);
      else
        snprintf(m, sizeof(m), "%04X %04X",
		 ((state.lst.line.was_org + i) << _16bit_core),
		 i_memory_get(state.i_memory, 
			      state.lst.line.was_org + i) & 0xffff);
        lst_line(m);
      }

    state.cod.emitting = 0;
  }
}

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

void lst_symbol_table(struct symbol_table *table)
{
  int i;
  const char *symbol_format = "%-32s  %08X";
  struct symbol **lst, **ps, *s;
  char buf[BUFSIZ];

  lst_line("SYMBOL TABLE");
  snprintf(buf, sizeof(buf), "%-32s  %-8s", "  LABEL", "  VALUE");
  lst_line(buf);
  lst_line("");

  ps = lst = malloc(table->count * sizeof(lst[0]));

  for (i = 0; i < HASH_SIZE; i++)
    for (s = table->hash_table[i]; s; s = s->next) 
      *ps++ = s;

  assert(ps == &lst[table->count]);

  qsort(lst, table->count, sizeof(lst[0]), symbol_compare);

  for (i = 0; i < table->count; i++) {
    struct variable *var;

    var = get_symbol_annotation(lst[i]);
    snprintf(buf, sizeof(buf),
            symbol_format, 
            get_symbol_name(lst[i]),
            var ? var->value : 0);
    lst_line(buf);
  }
  cod_write_symbols(lst,table->count);
}

void lst_defines_table(struct symbol_table *table)
{
  int i;
  const char *symbol_format = "%-32s  %s";
  struct symbol **lst, **ps, *s;
  char buf[BUFSIZ];

  ps = lst = malloc(table->count * sizeof(lst[0]));

  for (i = 0; i < HASH_SIZE; i++)
    for (s = table->hash_table[i]; s; s = s->next) 
      *ps++ = s;

  assert(ps == &lst[table->count]);

  qsort(lst, table->count, sizeof(lst[0]), symbol_compare);

  for (i = 0; i < table->count; i++) {
    char *defined_as;

    defined_as = get_symbol_annotation(lst[i]);
    snprintf(buf, sizeof(buf),
            symbol_format, 
            get_symbol_name(lst[i]),
            defined_as);
    lst_line(buf);
  }
}


syntax highlighted by Code2HTML, v. 0.9.1