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