/***********************************************************************
 *
 *  avra - Assembler for the Atmel AVR microcontroller series
 *
 *  Copyright (C) 1998-2004 Jon Anders Haugum, Tobias Weber
 *
 *  This program 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 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 this program; see the file COPYING.  If not, write to
 *  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA 02111-1307, USA.
 *
 *
 *  Authors of avra can be reached at:
 *     email: jonah@omegav.ntnu.no, tobiw@suprafluid.com
 *     www: http://sourceforge.net/projects/avra
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

#include "misc.h"
#include "args.h"
#include "avra.h"
#include "device.h"

#define debug 0

const char *title =
        "AVRA: advanced AVR macro assembler Version %i.%i.%i Build %i (%s)\n"
        "Copyright (C) 1998-2004 Jon Anders Haugum, Tobias Weber\n"
        "\n"
        "   AVRA is an open source assembler for Atmel AVR microcontroller family\n"
		"   It can be used as a replacement of 'AVRASM32.EXE' the original assembler\n"
		"   shipped with AVR Studio. We do not guarantee full compatibility for avra.\n"
        "\n"
        "   AVRA comes with NO WARRANTY, to the extent permitted by law.\n"
        "   You may redistribute copies of avra under the terms\n"
        "   of the GNU General Public License.\n"
        "   For more information about these matters, see the files named COPYING.\n"
        "\n";

const char *usage =
	"usage: AVRA [-f][O|M|I|G] output file type\n"
	"            [-o <filename>] output file name\n"
	"            [-l <filename>] generate list file\n"
	"            [-m <mapfile>] generate map file\n"
    "[--define <symbol>[=<value>]]  [--includedir <dir>] [--listmac]\n"
	"            [--max_errors <number>] [--devices] [--version]\n"
	"            [-h] [--help] general help\n"
	"            "
	"            <file to assemble>\n"
	"\n"
	"   --listfile    -l : Create list file\n"
	"   --mapfile     -m : Create map file\n"
	"   --define      -D : Define symbol.\n"
	"   --includedir  -I : Additional include dirs.\n"
	"   --listmac        : List macro expansion in listfile.\n"
	"   --max_errors     : Maximum number of errors before exit\n"
    "                      (default: 10)\n"
	"   --devices        : List out supported devices.\n"
	"   --version        : Version information.\n"
	"   --help, -h       : This help text.\n"
	"\n"
    "Just replace the AVRASM32.EXE with AVRA.EXE in your\n"
    "AVRStudio directories to avra's binary.\n"
	"\n"
	"Report bugs to tobiw@suprafluid.com\n"
	"            or jonah@omegav.ntnu.no\n";

int main(int argc, char *argv[])
{
  int show_usage = False;
  struct prog_info *pi;
  struct args *args;
  unsigned char c;

#if debug == 1
  for(i = 0; i < argc; i++) {
    printf(argv[i]);
    printf("\n");
  }
#endif

  printf(title, VER_MAJOR, VER_MINOR, VER_RELEASE, VER_BUILD, VER_DATE);
  
  
  args = alloc_args(ARG_COUNT);
  if(args) {
    define_arg(args, ARG_DEFINE,     ARGTYPE_STRING_MULTISINGLE, 'D', "define",     NULL);
	define_arg(args, ARG_INCLUDEDIR, ARGTYPE_STRING_MULTISINGLE, 'I', "includedir", NULL);
	define_arg(args, ARG_LISTMAC,    ARGTYPE_BOOLEAN,              0, "listmac",    "1");
	define_arg(args, ARG_MAX_ERRORS, ARGTYPE_STRING,               0, "max_errors", "10");
	define_arg(args, ARG_DEVICES,    ARGTYPE_BOOLEAN,              0, "devices",    NULL);
	define_arg(args, ARG_VER,        ARGTYPE_BOOLEAN,              0, "version",    NULL);
	define_arg(args, ARG_HELP,       ARGTYPE_BOOLEAN,            'h', "help",       NULL);
	define_arg(args, ARG_WRAP,        ARGTYPE_BOOLEAN,            'w', "wrap",      NULL);
	define_arg(args, ARG_FILEFORMAT,  ARGTYPE_CHAR_ATTACHED,      'f', "filetype",  "0");
	define_arg(args, ARG_LISTFILE,    ARGTYPE_STRING,             'l', "listfile",  NULL);
	define_arg(args, ARG_OUTFILE,     ARGTYPE_STRING,             'o', "outfile",   NULL);
	define_arg(args, ARG_MAPFILE,     ARGTYPE_STRING,             'm', "mapfile",   NULL);
	define_arg(args, ARG_DEBUGFILE,   ARGTYPE_STRING,             'd', "debugfile", NULL);
	define_arg(args, ARG_EEPFILE,     ARGTYPE_STRING,             'e', "eepfile",   NULL);

    c = read_args(args, argc, argv);
    
    if(c != 0) {
	  if(!GET_ARG(args, ARG_HELP) && (argc != 1))	{
	    if(!GET_ARG(args, ARG_VER)) {
		  if(!GET_ARG(args, ARG_DEVICES)) {
            pi = get_pi(args);
		    if(pi) {
              get_rootpath(pi, args);  /* get assembly root path */
			  assemble(pi);            /* the main assembly routine */
			  free_pi(pi);             /* free all allocated memory */
			}
		  }
		  else {
		    list_devices();            /* list all supported devices */
		  }
		}
	  }
	  else
	    show_usage = True;
	}
	free_args(args);
  }
  else {
	show_usage = True;
	printf("\n");
  }
  if(show_usage) {
	printf("%s", usage);
  }
  exit(EXIT_SUCCESS);
  return (0);  /* compiler warning, JEG 4-23-03 */
}

void get_rootpath(struct prog_info *pi, struct args *args)
{
  int i;
  int j;
  char c;
  struct data_list *data;
  
  data = args->first_data;
  while(data->next) data = ((data)->next);
  
  if (data != NULL) {
    i = strlen((char *)data->data);
    if (i > 0) {
      pi->root_path = malloc(i + 1);
      strcpy(pi->root_path,(char *)data->data);
      j = 0;
      do {
       c = pi->root_path[i];
       if(c == '\\' || c == '/') {
         j = i + 1;
         break;
       }
      } while(i-- > 0);
      pi->root_path[j] = '\0';
      return;
    }
  }
  pi->root_path = "";
}


void assemble(struct prog_info *pi) {
  unsigned char c;

  if(pi->args->first_data) {
	printf("Pass 1...\n");
	c = load_arg_defines(pi);
	if(c == 0)
	  return;

	predef_dev(pi);
	c = parse_file(pi, (char *)pi->args->first_data->data, PASS_1);
	if(c != 0) {
	  if(pi->error_count == 0) {
		prepare_second_pass(pi);
		c = load_arg_defines(pi);
		if(c == 0)
		  return;
		
		predef_dev(pi);
        c = open_out_files(pi, pi->args->first_data->data);
		if(c != 0) {
          printf("Pass 2...\n");
     	  parse_file(pi, (char *)pi->args->first_data->data, PASS_2);
		  printf("done\n\n");

		  if(GET_ARG(pi->args, ARG_COFF) && (pi->error_count == 0)) {
            write_coff_file(pi);
		  }
          write_map_file(pi);
		  close_out_files(pi);
		  if(pi->error_count)
			printf("\nAssembly aborted with %d errors and %d warnings.\n", pi->error_count, pi->warning_count);
		  else if(pi->warning_count)
		    printf("\nAssembly complete with no errors (%d warnings).\n", pi->warning_count);
		  else
		    printf("\nAssembly complete with no errors.\n");
		}
	  }
	}
  }
  else {
    printf("Error: You need to specify a file to assemble\n");
  }
}


int load_arg_defines(struct prog_info *pi)
{
  int i;
  char *expr;
  char buff[256];
  struct data_list *define;
  struct label *label;

  for(define = GET_ARG(pi->args, ARG_DEFINE); define; define = define->next) {
	strcpy(buff, define->data);
	expr = get_next_token(buff, TERM_EQUAL);
	if(expr) {
	  if(!get_expr(pi, expr, &i)) {
		return(False);
	  }
	  else i = 1;

	  for(label = pi->first_constant; label; label = label->next)
	    if(!nocase_strcmp(label->name, buff)) {
		  printf("Error: Can't define symbol %s twice\n", buff);
		  return(False);
		}
		label = malloc(sizeof(struct label));
		if(!label) {
		  print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
		  return(False);
		}
		label->next = NULL;
		if(pi->last_constant)
		  pi->last_constant->next = label;
		else
		  pi->first_constant = label;
		pi->last_constant = label;
		label->name = malloc(strlen(buff) + 1);
		if(!label->name) {
		  print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
		  return(False);
		}
		strcpy(label->name, buff);
		label->value = i;
	}
  }
  return(True);
}

struct prog_info *get_pi(struct args *args) {
  struct prog_info *pi;

  pi = (struct prog_info *)calloc(1, sizeof(struct prog_info));
  if(pi) {
	pi->args = args;
	pi->device = get_device(pi,NULL);
	if (GET_ARG(args, ARG_LISTFILE) == NULL) {
      pi->list_on = False;
    }
    else {
      pi->list_on = True;
    }
	if (GET_ARG(args, ARG_MAPFILE) == NULL) {
      pi->map_on = False;
    }
    else {
      pi->map_on = True;
    }
	pi->segment = SEGMENT_CODE;
	pi->dseg_addr = DSEG_START;
	pi->max_errors = atoi(GET_ARG(args, ARG_MAX_ERRORS));
	return(pi);
  }
  else return(NULL);
}


void free_pi(struct prog_info *pi) {
  free(pi);
}


void prepare_second_pass(struct prog_info *pi) {
  struct def *def, *temp_def;
  struct label *label, *temp_label;

  pi->segment = SEGMENT_CODE;
  pi->cseg_addr = 0;
  pi->dseg_addr = DSEG_START;
  pi->eseg_addr = 0;
	//pi->macro_nstlblnr = 0;
	
  for(def = pi->first_def; def;) {
	temp_def = def;
	def = def->next;
	free(temp_def->name);
	free(temp_def);
  }
  pi->first_def = NULL;	
  pi->last_def = NULL;	

  for(label = pi->first_constant; label;) {
	temp_label = label;
	label = label->next;
	free(temp_label->name);
	free(temp_label);
  }
  pi->first_constant = NULL;
  pi->last_constant = NULL;

  for(label = pi->first_variable; label;) {
	temp_label = label;
	label = label->next;
	free(temp_label->name);
	free(temp_label);
  }
  pi->first_variable = NULL;
  pi->last_variable = NULL;
}


void print_msg(struct prog_info *pi, int type, char *fmt, ... )
{
	va_list args;
	char *pc;

	if(type == MSGTYPE_OUT_OF_MEM) {
	  fprintf(stderr, "Error: Unable to allocate memory!\n");
	}
	else {
	  va_start(args, fmt);
      /* check if adding path name is needed*/	  
	  pc = strstr(pi->fi->include_file->name, pi->root_path);
	  if(pc == NULL) { 
	    fprintf(stderr, "%s%s(%d) : ", pi->root_path ,pi->fi->include_file->name, pi->fi->line_number);
      }
      else {
        fprintf(stderr, "%s(%d) : ", pi->fi->include_file->name, pi->fi->line_number);
      }

	  switch(type) {
		case MSGTYPE_ERROR:
		  pi->error_count++;
		  fprintf(stderr, "error : ");
		  break;
		case MSGTYPE_WARNING:
		  pi->warning_count++;
		  fprintf(stderr, "warning : ");
		  break;
		case MSGTYPE_MESSAGE:
		  break;
	  }

	  if(pi->macro_call) {
		fprintf(stderr, "[Macro: %s: %d:] ", pi->macro_call->macro->include_file->name, pi->macro_call->line_index + pi->macro_call->macro->first_line_number);
	  }

	  vfprintf(stderr, fmt, args);
	  fprintf(stderr, "\n");
	  va_end(args);
	}
}

/* avra.c */
