/* $VER: pasm errors.c V0.7 (02.01.98)
 *
 * This file is part of pasm, a portable PowerPC assembler.
 * Copyright (c) 1997-98  Frank Wille
 *
 * pasm is freeware and part of the portable and retargetable ANSI C
 * compiler vbcc, copyright (c) 1995-98 by Volker Barthelmann.
 * pasm may be freely redistributed as long as no modifications are
 * made and nothing is charged for it. Non-commercial usage is allowed
 * without any restrictions.
 * EVERY PRODUCT OR PROGRAM DERIVED DIRECTLY FROM MY SOURCE MAY NOT BE
 * SOLD COMMERCIALLY WITHOUT PERMISSION FROM THE AUTHOR.
 *
 *
 * v0.7 (02.01.98) phx
 *      Deleted "EHF" in warning 52.
 * v0.6 (30.10.97) phx
 *      New error messages.
 * v0.5 (12.10.97) phx
 *      New error messages.
 * v0.4 (05.07.97) phx
 *      Program returns EXIT_FAILURE if an error occurs.
 *      New error messages. Error 33 is a warning now.
 * v0.2 (25.03.97) phx
 *      Writes ELF object for 32-bit PowerPC big-endian. Either absolute
 *      or ELF output format may be selected. ELF is default for all
 *      currently supported platforms. PPCasm supports nine different
 *      relocation types (there are much more...).
 *      Compiles and works also under NetBSD/amiga (68k).
 *      Changed function declaration to 'new style' in all sources
 *      (to avoid problems with '...' for example).
 *      Warnings can be disabled.
 * v0.1 (11.03.97) phx
 *      First test version with all PowerPC instructions and most
 *      important directives. Only raw, absolute output.
 * v0.0 (14.02.97) phx
 *      File created. Project started.
 */


#define ERRORS_C
#include "ppcasm.h"


/* error flags */
#define EF_NONE 0
#define EF_WARNING 1
#define EF_ERROR 2
#define EF_FATAL 3
#define EF_TYPEMASK 0xf

#define EF_NOLINE 0x10


void error(int,...);
void ierror(char *,...);



static struct {
  char *txt;
  int flags;
} errors[] = {
  "No error",EF_NONE|EF_NOLINE,
  "Out of memory",EF_FATAL|EF_NOLINE,                               /* 01 */
  "Missing source file name",EF_FATAL|EF_NOLINE,
  "Missing output file name",EF_FATAL|EF_NOLINE,
  "Multiple source file names detected",EF_FATAL|EF_NOLINE,
  "File \"%s\" has a read error",EF_FATAL|EF_NOLINE,                /* 05 */
  "Can't open \"%s\"",EF_FATAL|EF_NOLINE,
  "Symbol \"%s\" defined twice",EF_ERROR,
  "Macro \"%s\" defined twice",EF_ERROR,
  "Reference to undefined macro parameter \\%d",EF_ERROR,
  "Unknown opcode \"%s\"",EF_ERROR,                                 /* 10 */
  "Too many macro parameters",EF_ERROR,
  "Colon expected",EF_ERROR,
  "Syntax error",EF_ERROR,
  "String constant expected",EF_ERROR,
  "Illegal section attribute \"%c\"",EF_ERROR,                      /* 15 */
  "Section attributes don't match",EF_ERROR,
  "Missing argument",EF_ERROR,
  "Extra characters on line",EF_ERROR,
  "Undefined symbol",EF_ERROR,
  "Double unary operator",EF_ERROR,                                 /* 20 */
  "Missing closing parenthesis",EF_ERROR,
  "Illegal operation for a reloc expression",EF_ERROR,
  "Symbols reside in different sections",EF_ERROR,
  "Constant integer expression required",EF_ERROR,
  "Unable to create output file \"%s\"",EF_FATAL|EF_NOLINE,         /* 25 */
  "Error while writing to \"%s\"",EF_FATAL|EF_NOLINE,
  "Instruction has too few operands",EF_ERROR,
  "Can't assign external symbol",EF_ERROR,
  "Can't assign reloc half word",EF_ERROR,
  "Immediate operand doesn't fit into %d bits",EF_WARNING,          /* 30 */
  "Register operand out of range",EF_ERROR,
  "Reloc symbol required",EF_ERROR,
  "Branch destination is not in current section",EF_WARNING,
  "Branch operand out of range",EF_ERROR,
  "CR-specifier out of range",EF_ERROR,                             /* 35 */
  "Operand field \"%s\" out of range",EF_ERROR,
  "\"%s\" is a 64-bit instruction",EF_WARNING,
  "\"%s\" is an optional instruction",EF_WARNING,
  "\"%s\" is a supervisor-level instruction",EF_WARNING,
  "\"(rA)\" expected, after index expression",EF_ERROR,             /* 40 */
  "Expression must be dividable by four",EF_ERROR,
  "Invalid register field mask",EF_ERROR,
  "Maximum nesting depth for conditional assembly exceeded",EF_ERROR,
  "%s without matching .if",EF_ERROR,
  "\"fail\" directive encountered",EF_FATAL,                        /* 45 */
  "Symbol is not part of a base-relative section",EF_ERROR,
  "Section \"%s\" was never defined",EF_ERROR,
  "Option -%c: argument expected",EF_FATAL|EF_NOLINE,
  "Unknown output format (%d)",EF_FATAL|EF_NOLINE,
  "\"%s\" is not a section base address",EF_ERROR,                  /* 50 */
  "Maximum of %d include paths reached. Path \"%s\" was ignored",
    EF_ERROR|EF_NOLINE,
  "Section \"%s\" with type #%d is not supported and was "
    "changed to HUNK_DATA.",EF_WARNING|EF_NOLINE,
  "Unsupported relocation type %s at offset %d in section \"%s\"",
    EF_ERROR|EF_NOLINE,
  "Option -%c: integer expected",EF_FATAL|EF_NOLINE,
  "Absolute output doesn't allow external references. Symbol "      /* 55 */
   "referenced was: %s",EF_ERROR|EF_NOLINE,
  "Option -%c: symbol name expected",EF_FATAL|EF_NOLINE,
  "Unknown assembler mode -m%s",EF_WARNING|EF_NOLINE,
};


void error(int errn,...)
/* prints errors and warnings */
{
  struct GlobalVars *gv = &gvars;
  va_list vl;
  char *errtype;
  int flags = errors[errn].flags;
  struct SourceThread *mst,*st = gv->cthread;

  if (((flags&EF_TYPEMASK) == EF_WARNING) && gv->dontwarn)
    return;
  switch(flags&EF_TYPEMASK) {
    case EF_WARNING:
      errtype = "Warning";
      break;
    case EF_ERROR:
      gv->returncode = EXIT_FAILURE;
      errtype = "Error";
      break;
    case EF_FATAL:
      gv->returncode = EXIT_FAILURE;
      errtype = "Fatal error";
      break;
    default:
      ierror("Illegal error type %d",flags&EF_TYPEMASK);
      gv->returncode = EXIT_FAILURE;
      errtype = "";
      break;
  }
  if (!(flags&EF_NOLINE))
    printf("\n%s\n",st->lineptr);  /* display error line */

  /* print error message */
  printf("%s %d: ",errtype,errn);
  va_start(vl,errn);
  vprintf(errors[errn].txt,vl);
  va_end(vl);
  
  if (!(flags&EF_NOLINE)) {
    if (st->macro) {
      mst = st;
      do
        if (!(mst = mst->prev))
          ierror("There's no source text where macro \"%s\" is "
                 "called from",st->csource->name);
      while (mst->macro);
      printf(" in line %d of macro \"%s\", called from line %d of "
             "file \"%s\"",(int)st->line,st->csource->name,
             (int)mst->line,mst->csource->name);
    }
    else
      printf(" in line %d of file \"%s\"",(int)st->line,st->csource->name);
  }
  printf(".\n");

  switch(flags&EF_TYPEMASK) {
    case EF_ERROR:
      /* check if maximum number of errors reached */
      if (++gv->errcnt >= gv->maxerrors) {
        gv->errcnt = 0;
        printf("Do you want to continue (y/n) ? ");
        fflush(stdin);
        if (toupper((unsigned char)getchar()) == 'N')
          cleanup(gv);
      }
      break;
    case EF_FATAL:
      printf("Aborting.\n");  /* fatal error aborts the assembler */
      cleanup(gv);
      break;
  }
}


void ierror(char *errtxt,...)
/* display internal error and quit */
{
  struct GlobalVars *gv = &gvars;
  struct SourceThread *st = gv->cthread;
  va_list vl;

  printf("\nINTERNAL ERROR: ");
  va_start(vl,errtxt);
  vprintf(errtxt,vl);
  va_end(vl);
  printf(" in line %d of \"%s\".\nAborting.\n",(int)st->line,
         st->csource->name);
  cleanup(gv);
}
