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

/* Error handling for gpasm
   Copyright 1998-2005	James Bowman, Craig Franklin
   Copyright 2012	Borut Ražem
   Copyright 2015-2016	Molnár Károly
*/

#include "stdhdr.h"

#include "libgputils.h"
#include "gpasm.h"
#include "gpmsg.h"
#include "lst.h"
#include "gpRanges.h"

static gp_Ranges inhibitList;

/*------------------------------------------------------------------------------------------------*/
// return true when warning/message is not suppressed
static bool _check_code(int Code) {
  return !inhibitList[Code];
}

/*------------------------------------------------------------------------------------------------*/
// classify code to error/warning/message/debug message
static msg_type_t gptyp(enum GP_codes Code) {
 char s[8];
 snprintf(s,sizeof s,"%7d",(int)Code);
 switch (s[4]) {	// position of hundred
  case '2': return MSG_TYPE_WARNING;	// warnings are 2xx or 12xx
  case '3': return MSG_TYPE_MESSAGE;	// messages are 3xx or 13xx
  case '4': return MSG_TYPE_DEBUG;	// debugs seem to be 14xx
  default : return MSG_TYPE_ERROR;	// errors are 1xx, 11xx or 15xx, so catch-all here
 }
}

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

static const char* _get_String(enum GP_codes Code) {
  switch (Code) {
    case GPE_USER:		return "ERROR: (%s)";
    case GPE_NOENT:		return "Cannot open file. Include file \"%s\" not found.";
    case GPE_STRCPLX:		return "String substitution too complex.";
    case GPE_BADCHAR:		return "Illegal character: '%c'";
    case GPE_OPENPAR:		return "Unmatched (";
    case GPE_CLOSEPAR:		return "Unmatched )";
    case GPE_SYM_NOT_DEFINED:	return "Symbol not previously defined: \"%s\"";
    case GPE_DIVBY0:		return "Divide by zero.";
    case GPE_DUPLAB:		return "Duplicate label, \"%s\" or redefining symbol that cannot be redefined.";
    case GPE_DIFFLAB:		return "Address label duplicated or different in second pass: \"%s\"";
    case GPE_ADDROVF:		return "Address wrapped around 0.";
    case GPE_ADDROVR:		return "Overwriting previous address contents: 0x%04X";
    case GPE_BAD_CALL_ADDR:	return "Call or jump not allowed at this address, must be in low half of page.";
    case GPE_ILLEGAL_LABEL:	return "Illegal label: \"%s\"";
    case GPE_ILLEGAL_DIR:	return "Illegal directive: \"%s\"";
    case GPE_ILLEGAL_ARGU:	return "Illegal argument: \"%s\"";
    case GPE_ILLEGAL_COND:	return "Illegal condition.";
    case GPE_OUT_OF_RANGE:	return "Argument out of range.";
    case GPE_TOO_MANY_ARGU:	return "Too many arguments.";
    case GPE_MISSING_ARGU:	return "Missing argument(s).";
    case GPE_EXPECTED:		return "Expected.";
    case GPE_EXTRA_PROC:	return "Processor type previously defined.";
    case GPE_UNDEF_PROC:	return "Processor type is undefined.";
    case GPE_UNKNOWN_PROC:	return "Unknown processor: \"%s\"";
    case GPE_IHEX:		return "Hex file format INHX32 required.";
    case GPE_NO_MACRO_NAME:	return "Macro name missing.";
    case GPE_DUPLICATE_MACRO:	return "Duplicate macro name.";
    case GPE_BAD_WHILE_LOOP:	return "WHILE must terminate within 256 iterations.";
    case GPE_ILLEGAL_NESTING:	return "Illegal nesting.";
    case GPE_UNMATCHED_ENDM:	return "Unmatched ENDM.";
    case GPE_OBJECT_ONLY:	return "Directive only allowed when generating an object file: \"%s\"";
    case GPE_LABEL_IN_SECTION:	return "Labels must be defined in a code or data section when making an object file.";
    case GPE_UNRESOLVABLE:	return "Operand contains unresolvable labels or is too complex.";
    case GPE_WRONG_SECTION:	return "Executable code and data must be defined in an appropriate section.";
    case GPE_CONTIG_SECTION:	return "Each object file section must be contiguous: \"%s\"";
    case GPE_MUST_BE_LABEL:	return "Operand must be an address label.";
    case GPE_ORG_ODD:		return "ORG at odd address.";
    case GPE_FILL_ODD:		return "Cannot use FILL Directive with odd number of bytes.";
    case GPE_CONTIG_CONFIG:	return "__CONFIG directives must be contiguous.";
    case GPE_CONTIG_IDLOC:	return "__IDLOC directives must be contiguous.";
    case GPE_NO_EXTENDED_MODE:	return "Extended mode not available for this device.";
    case GPE_MISSING_BRACKET:	return "Square brackets required around offset operand.";
    case GPE_CONSTANT:		return "Expression within brackets must be constant.";
    case GPE_IDLOCS_ORDER:	return "__IDLOCS directives must be listed in ascending order.";
    case GPE_CONFIG_UNKNOWN:	return "An error with the CONFIG directive occured.";
    case GPE_CONFIG_usCONFIG:	return "You cannot mix CONFIG and __CONFIG directives.";
    case GPE_RES_ODD_PIC16EA:	return "RES directive cannot reserve odd number of bytes in PIC18 absolute mode.";
    case GPE_UNKNOWN:		return "";
    /* gputils special errors */
    case GPE_INTERNAL:		return "Internal error: %s";
    case GPE_PARSER:		return "Parser error: %s";
    case GPE_SCANNER:		return "Scanner error: %s";
    case GPE_IDLOCS_P16E:	return "IDLOCS directive use solely to the pic18 family.";
    case GPE_NO_DEST:		return "The destination of the storage is not selected, use W or F.";
    case GPE_NO_ACC:		return "The access of RAM is not selected, use A or B:";
    case GPE_TOO_LONG:		return "The string (\"%s\") too long (%u" SIZE_FMTu " bytes). It cannot be more than %" SIZE_FMTu " bytes.";
    case GPE_IN_OF_ACCRAM:	return "This register is located on the Access RAM:";
    case GPE_OUT_OF_ACCRAM:	return "This register is not located on the Access RAM:";
    case GPE_OUT_OF_BANK:	return "Register in operand not located in RAM Bank %d. Ensure that Bank bits are correct:";
    case GPE_INVALID_RAM:	return "Invalid RAM location specified.";
    case GPE_INVALID_ROM:	return "Invalid ROM location specified.";
    case GPE_EXCEED_ROM:	return "Address exceeds maximum range for this processor.";
    case GPE_SYM_NO_VALUE:	return "This symbol has no value: \"%s\"";

    case GPW_SYM_NOT_DEFINED:	return "Symbol not previously defined: \"%s\"";
    case GPW_OUT_OF_RANGE:	return "Argument out of range. The %u least significant bits are usable.";
    case GPW_OP_COLUMN_ONE:	return "Found opcode in column 1: \"%s\"";
    case GPW_DIR_COLUMN_ONE:	return "Found directive in column 1: \"%s\"";
    case GPW_MACRO_COLUMN_ONE:	return "Found call to macro in column 1: \"%s\"";
    case GPW_LABEL_COLUMN:	return "Found label after column 1.";
    case GPW_MISSING_QUOTE:	return "Missing quote.";
    case GPW_EXTRANEOUS:	return "Extraneous arguments on the line.";
    case GPW_EXPECTED:		return "Expected.";
    case GPW_CMDLINE_PROC:	return "Processor superseded by command line.";
    case GPW_CMDLINE_RADIX:	return "Radix superseded by command line.";
    case GPW_CMDLINE_HEXFMT:	return "Hex file format specified on command line.";
    case GPW_RADIX:		return "Expected dec, oct, hex. Will use hex.";
    case GPW_INVALID_RAM:	return "Invalid RAM location specified.";
    case GPW_EXCEED_ROM:	return "Address exceeds maximum range for this processor.";
    case GPW_DISABLE_ERROR:	return "Error messages cannot be disabled.";
    case GPW_REDEFINING_PROC:	return "Redefining processor.";
    case GPW_NOT_RECOMMENDED:	return "Use of this instruction is not recommended:";
    case GPW_WORD_ALIGNED:	return "Destination address must be word aligned.";
    case GPW_INVALID_ROM:	return "Invalid ROM location specified.";
    /* gputils special warnings */
    case GPW_BANK_PAGE_SEL_AFTER_SKIP: return "%s after skip instruction. I this really what you intended?";
    case GPW_UNDEF_PROC:	return "Processor type is undefined.";
    case GPW_IN_OF_ACCRAM:	return "This register is located on the Access RAM:";
    case GPW_OUT_OF_ACCRAM:	return "This register is not located on the Access RAM:";
    case GPW_NO_DEST:		return "The destination of the storage is not selected, use W or F.";
    case GPW_OUT_OF_BANK:	return "Register in operand not located in RAM Bank %d. Ensure that Bank bits are correct:";
    case GPW_STRING_TRUNCATE:	return "This string (\"%s\") too long, it will be truncated to %u bytes length.";
    case GPW_SYM_NO_VALUE:	return "This symbol has no value: \"%s\"";
    case GPW_USER:		return "WARNING: (%s)";

    case GPM_USER:		return "MESSAGE: \"%s\"";
    case GPM_OUT_OF_BANK:	return "Register in operand not located in RAM Bank %d. Ensure that Bank bits are correct:";
    case GPM_OUT_OF_RANGE:	return "Program word too large. Truncated to core size: 0x%04X";
    case GPM_IDLOC:		return "An ID Locations value too large. Last four hex digits used: 0x%X ==> 0x%04X";
    case GPM_NO_DEST:		return "Using default destination of 1 (file).";
    case GPM_PAGE_BOUNDARY:	return "Crossing page boundary -- ensure page bits are set.";
    case GPM_PAGEBITS:		return "Setting page bits.";
    case GPM_SUPVAL:		return "Warning level superseded by command line value.";
    case GPM_SUPLIN:		return "Macro expansion superseded by command line value.";
    case GPM_SUPRAM:		return "Superseding current maximum RAM and RAM map.";
    case GPM_EXT_BANK_OR_PAGE:	return "Page or Bank selection not needed for this device. No code generated.";
    case GPM_CBLOCK:		return "CBLOCK constants will start with a value of 0.";
    case GPM_W_MODIFIED:	return "W Register modified.";
    case GPM_SPECIAL_MNEMONIC:	return "Special Instruction Mnemonic used.";
    case GPM_ACC_DEF:		return "Using default access of 0 (Access Bank):";
    case GPM_NO_BANK:		return "RAM Bank undefined in this chunk of code. Ensure that bank bits are correct. Assuming bank %u from now on.";
    case GPM_EXT_BANK:		return "Bank selection not needed for this device. No code generated.";
    case GPM_EXT_PAGE:		return "Page selection not needed for this device. No code generated.";
    default: switch (gptyp(Code)) {
      case MSG_TYPE_WARNING:	return "UNKNOWN WARNING";
      case MSG_TYPE_MESSAGE:	return "UNKNOWN MESSAGE";
      default:			return "UNKNOWN ERROR";
    }
  }
}

/*------------------------------------------------------------------------------------------------*/
static const char*const Typ[]={"Error","Warning","Message","Debug"};

static void _verr(msg_type_t typ, enum GP_codes Code, const char* Message, va_list va) {
  const source_context_t *src = state.src_list.last;
  
  if (src && state.macro_dereference) {
    while (src && src->type == SRC_MACRO) {
      src = src->prev;
    }
  }
  const char* type = Typ[typ];
  const char* gap = typ==MSG_TYPE_ERROR ? "  " : "";
  if (!state.quiet) {
    /* standard output */
    if (src) printf("%s:%d:%s[%03d] %s", src->name, src->line_number, type, Code, gap);
    else printf("%s[%03d] %s", type, Code, gap);
//TODO: Show file/macro nesting to pin-point possible real error reason
    if (va) vprintf(Message, va);
    else puts(Message);
    putchar('\n');
  }

  /* error file */
  if (state.err.enabled) {
    if (src) fprintf(state.err.f, "%s[%03d] %s%s %d : ", type, Code, gap, src->name, src->line_number);
    else fprintf(state.err.f, "%s[%03d] %s", type, Code, gap);
    if (va) vfprintf(state.err.f, Message, va);
    else fputs(Message, state.err.f);
    putc('\n', state.err.f);
  }
}


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

void gpmsg_init(void) {
  if (state.err_file != OUT_NAMED) {
    snprintf(state.err_file_name, sizeof(state.err_file_name), "%s.err", state.base_file_name);
  }

  if (state.err_file == OUT_SUPPRESS) {
    state.err.enabled = false;
    unlink(state.err_file_name);
  }
   else {
    state.err.f = fopen(state.err_file_name, "wt");

    if (state.err.f == NULL) {
      perror(state.err_file_name);
      exit(1);
    }

    state.err.enabled = true;
  }
  inhibitList.clear();
}

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

void gpmsg_close(void) {
  if (state.err.enabled) {
    fclose(state.err.f);
    state.err.f       = NULL;
    state.err.enabled = false;
  }
  inhibitList.clear();
}

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

/* This can not be used in "mpasm compatible" mode! */
// Negative values add the warning/message to the suppressed list,
// positive values remove these warnings/messages
void gpmsg_add_code_range(int Code0, int Code1) {
  bool remove = true;
  if (Code0<0) {
    Code0 = -Code0;
    remove=false;
  }
  if (Code1<0) Code1 = -Code1;
  if (Code0 > Code1) std::swap(Code0,Code1);

  if (gp_num_range_is_overlapped(Code0, Code1, GMSG_ERR_LEVEL0_MIN, GMSG_ERR_LEVEL0_MAX)) {
    gpmsg(GPW_DISABLE_ERROR, NULL);
    return;
  }else if (gp_num_range_is_overlapped(Code0, Code1, GMSG_ERR_LEVEL1_MIN, GMSG_ERR_LEVEL1_MAX)) {
    gpmsg(GPW_DISABLE_ERROR, NULL);
    return;
  }
  if (remove) inhibitList.clearrange(Code0,Code1);
  else inhibitList.setrange(Code0,Code1);
}

void gpmsg_add_code(int Code) {gpmsg_add_code_range(Code,Code);}

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

static bool _suppressed(msg_type_t typ, enum GP_codes Code) {
  switch (typ) {
    case MSG_TYPE_WARNING: if (state.error_level>1 || !_check_code(Code)) {
      state.num.warnings_suppressed++;
      return true;
    }break;
    case MSG_TYPE_MESSAGE: if (state.error_level || !_check_code(Code)) {
      state.num.messages_suppressed++;
      return true;
    }break;
  }
  return false;
}

void gpmsg0(enum GP_codes Code, const char* Message) {
  if (state.pass != 2) return;
  msg_type_t typ=gptyp(Code);
  if (_suppressed(typ,Code)) return;
  if (!Message) Message = _get_String(Code);
    /* standard output */
  _verr(typ,Code, Message, 0);
  /* list file output */
  lst_line("%s[%03d]  : %s", Typ[typ], Code, Message);

  (&state.num.errors)[typ]++;
}

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

void _cdecl gpmsg(enum GP_codes Code, const char* Message, ...) {
  va_list     ap;
  const char* msg;
  char        buf[BUFSIZ];

  if (state.pass != 2) return;
  msg_type_t typ = gptyp(Code);
  if (_suppressed(typ,Code)) return;

  msg = _get_String(Code);

  if (Message && *Message) {
    snprintf(buf, sizeof(buf), "%s %s", msg, Message);
    msg = buf;
  }

  /* standard output */
  va_start(ap, Message);
  _verr(typ, Code, msg, ap);
  va_end(ap);

  /* list file output */
  va_start(ap, Message);
  lst_err_line(Typ[typ], (unsigned)Code, msg, ap);
  va_end(ap);

  (&state.num.errors)[typ]++;
}
Detected encoding: UTF-80