/* top level functions 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 "gperror.h"
#include "scan.h"
#include "deps.h"
#include "directive.h"
#include "lst.h"
#include "cod.h"
#include "processor.h"
#include "coff.h"

struct gpasm_state state;

static gp_boolean cmd_processor = false;
static char *processor_name = NULL;

int yyparse(void);
extern int yydebug;

#define GET_OPTIONS "?D:I:a:cde:ghilLmMno:p:qr:uvw:y"

static struct option longopts[] =
{
  { "define",      1, 0, 'D' },
  { "include",     1, 0, 'I' },
  { "hex-format",  1, 0, 'a' },
  { "object",      0, 0, 'c' },
  { "debug",       0, 0, 'd' },
  { "expand",      1, 0, 'e' },
  { "debug-info",  0, 0, 'g' },
  { "help",        0, 0, 'h' },
  { "ignore-case", 0, 0, 'i' },
  { "list-chips",  0, 0, 'l' },
  { "force-list",  0, 0, 'L' },
  { "dump",        0, 0, 'm' },
  { "deps",        0, 0, 'M' },
  { "dos",         0, 0, 'n' },
  { "output",      1, 0, 'o' },
  { "processor",   1, 0, 'p' },
  { "quiet",       0, 0, 'q' },
  { "radix",       1, 0, 'r' },
  { "absolute",    0, 0, 'u' },
  { "version",     0, 0, 'v' },
  { "warning",     1, 0, 'w' },
  { "extended",    0, 0, 'y' },
  { 0, 0, 0, 0 }
};

void
init(void)
{

  gp_init();

  /* restore gpasm to its initialized state */
  state.mode = absolute;
  state.extended_pic16e = false;

  state.radix = 16;
  state.hex_format = inhx32;
  state.case_insensitive = false;
  state.quiet = false;
  state.use_absolute_path = false;
  state.error_level = 0;
  state.debug_info = false;
  state.path_num = 0;

  state.cmd_line.radix = false;
  state.cmd_line.hex_format = false;
  state.cmd_line.error_level = false;
  state.cmd_line.macro_expand = false;
  state.cmd_line.processor = false;
  state.cmd_line.lst_force = false;

  state.pass = 0;
  state.org = 0;
  state.dos_newlines = false;
  state.memory_dump = false;
  state.found_config = false;
  state.found_devid = false;
  state.found_idlocs = false;
  state.maxram = (MAX_RAM - 1);

  state.codfile = normal;
  state.depfile = suppress;
  state.hexfile = normal;
  state.lstfile = normal;
  state.objfile = suppress;

  state.num.errors    = 0;
  state.num.warnings  = 0;
  state.num.messages  = 0;
  state.num.warnings_suppressed = 0;
  state.num.messages_suppressed = 0;
  
  state.processor = no_processor;
  state.processor_chosen = 0;

  state.cod.enabled = false;
  state.dep.enabled = false;
  state.lst.enabled = false;
  state.obj.enabled = false;

  state.obj.object = NULL;
  state.obj.section = NULL;
  state.obj.symbol_num = 0;
  state.obj.section_num = 0;
  
  state.astack = NULL;
  
  state.next_state = state_nochange;
  
  return;
}

void 
add_path(char *path)
{
  if(state.path_num < MAX_PATHS) {
    state.paths[state.path_num++] = strdup(path);
  } else {
    fprintf(stderr, "too many -I paths\n");
    exit(1);
  }
}

static void
show_usage(void)
{
  printf("Usage: gpasm [options] file\n");
  printf("Options: [defaults in brackets after descriptions]\n");
  printf("  -a FMT, --hex-format FMT       Select hex file format. [inhx32]\n");
  printf("  -c, --object                   Output relocatable object.\n");
  printf("  -d, --debug                    Output debug messages.\n");
  printf("  -D SYM=VAL, --define SYM=VAL   Define SYM with value VAL.\n");
  printf("  -e [ON|OFF], --expand [ON|OFF] Macro expansion.\n");
  printf("  -g, --debug-info               Use debug directives for COFF.\n");
  printf("  -h, --help                     Show this usage message.\n");
  printf("  -i, --ignore-case              Case insensitive.\n");
  printf("  -I DIR, --include DIR          Specify include directory.\n");
  printf("  -l, --list-chips               List supported processors.\n");
  printf("  -L, --force-list               Ignore nolist directives.\n");
  printf("  -m, --dump                     Memory dump.\n");
  printf("  -M, --deps                     Output dependency file.\n");
#ifndef HAVE_DOS_BASED_FILE_SYSTEM
  printf("  -n, --dos                      Use DOS newlines in hex file.\n");
#endif
  printf("  -o FILE, --output FILE         Alternate name of output file.\n");
  printf("  -p PROC, --processor PROC      Select processor.\n");
  printf("  -q, --quiet                    Quiet.\n");
  printf("  -r RADIX, --radix RADIX        Select radix. [hex]\n");
  printf("  -u, --absolute                 Use absolute pathes. \n");
  printf("  -v, --version                  Show version.\n");
  printf("  -w [0|1|2], --warning [0|1|2]  Set message level. [0]\n");
  printf("  -y, --extended                 Enable 18xx extended mode.\n");
  printf("\n");
#ifdef USE_DEFAULT_PATHS
  if (gp_header_path) {
    printf("Default header file path %s\n", gp_header_path);
  } else {
    printf("Default header file path NOT SET\n");
  }
  printf("\n");
#endif
  printf("Report bugs to:\n");
  printf("%s\n", PACKAGE_BUGREPORT);
  exit(0);
}

void
process_args( int argc, char *argv[])
{
  extern char *optarg;
  extern int optind;
  int c;
  gp_boolean usage = false;
  char *pc;

  /* Scan through the options for the -i flag.  It must be set before the 
     defines are read */
  while ((c = getopt_long(argc, argv, GET_OPTIONS, longopts, 0)) != EOF) {
    switch (c) {
    case 'i':
      state.case_insensitive = true;
      break;
    }
  }

  /* reset the getopt_long index for the next call */
  optind = 1;

  /* initalize the defines table for command line arguments */
  state.stDefines = push_symbol_table(NULL, state.case_insensitive);

  while ((c = getopt_long(argc, argv, GET_OPTIONS, longopts, 0)) != EOF) {
    switch (c) {
    case '?':
    case 'h':
      usage = true;
      break;
    case 'a':
      select_hexformat(optarg);
      state.cmd_line.hex_format = true;
      break;
    case 'c':
      state.mode    = relocatable;
      state.codfile = suppress;
      state.hexfile = suppress;
      state.lstfile = normal;
      state.objfile = normal;
      break;
    case 'd':
      gp_debug_disable = false;
      break;
    case 'D':
      if ((optarg != NULL) && (strlen(optarg) > 0)) {
	struct symbol *sym;
	char *lhs, *rhs;

	lhs = strdup(optarg);
	rhs = strchr(lhs, '=');
	if (rhs != NULL) {
	  *rhs = '\0';	/* Terminate the left-hand side */
	  rhs++;	/* right-hand side begins after the '=' */
	}

	sym = get_symbol(state.stDefines, lhs);
	if (sym == NULL)
	  sym = add_symbol(state.stDefines, lhs);
	if (rhs)
	  annotate_symbol(sym, rhs);
      }
      break;
    case 'e':
      select_expand(optarg);
      state.cmd_line.macro_expand = true;
      break;
    case 'g':
      state.debug_info = true;
      break;
    case 'I':
      add_path(optarg);
      break;    
    case 'i':
      state.case_insensitive = true;
      break;
    case 'L':
      state.cmd_line.lst_force = true;
      break;  
    case 'l':
      gp_dump_processor_list(true, 0);
      exit(0);
      break;
    case 'M':
      state.depfile = normal;
      break;
    case 'm':
      state.memory_dump = true;
      break;
    case 'n':
      #ifndef HAVE_DOS_BASED_FILE_SYSTEM
        state.dos_newlines = true;
      #endif
      break;
    case 'o':
      strncpy(state.hexfilename, optarg, sizeof(state.hexfilename));
      strncpy(state.basefilename, optarg, sizeof(state.basefilename));
      pc = strrchr(state.basefilename, '.');
      if (pc)
        *pc = 0;
      break;
    case 'p':
      cmd_processor = true;
      processor_name = optarg;
      break;
    case 'q':
      state.quiet = true;
      break;
    case 'r':
      select_radix(optarg);
      state.cmd_line.radix = true;
      break;
    case 'u':
      state.use_absolute_path = true;
      break;
    case 'w':
      select_errorlevel(atoi(optarg));
      state.cmd_line.error_level = true;
      break;
    case 'y':
      state.extended_pic16e = true;
      break;
    case 'v':
      fprintf(stderr, "%s\n", GPASM_VERSION_STRING);
      exit(0);
      
    }
    if (usage)
      break;
  }
  
  if ((optind + 1) == argc)
    state.srcfilename = argv[optind];
  else
    usage = true;

  if (usage) {
    show_usage();
  }

  /* Add the header path to the include paths list last, so that the user
     specified directories are searched first */
  if (gp_header_path) {
    add_path(gp_header_path);
  }

  if (state.use_absolute_path) {
    state.srcfilename = gp_absolute_path(state.srcfilename);
  }

}

int
assemble(void)
{
  char *pc; 
  struct symbol_table *cmd_defines;

  /* store the command line defines to restore on second pass */
  cmd_defines = state.stDefines;
  state.c_memory = state.i_memory = i_memory_create();

  if(state.basefilename[0] == '\0') {
    strncpy(state.basefilename, state.srcfilename, sizeof(state.basefilename));
    pc = strrchr(state.basefilename, '.');
    if (pc)
      *pc = 0;
  }

  /* Builtins are always case insensitive */
  state.stBuiltin = push_symbol_table(NULL, true);
  state.stDirective = state.stBuiltin;
  state.stMacros = push_symbol_table(NULL, state.case_insensitive);
  state.stTop = 
    state.stGlobal = push_symbol_table(NULL, state.case_insensitive);
  state.stTopDefines = 
    state.stDefines = push_symbol_table(cmd_defines, state.case_insensitive);

  opcode_init(0);

  /* the tables are built, select the processor if -p was used */
  if (cmd_processor) {
    select_processor(processor_name);
    state.cmd_line.processor = true;
  }

  state.pass = 1;
  open_src(state.srcfilename, 0);
  yyparse();
 
  state.pass++;
  state.org = 0;
  state.cblock = 0;
  /* clean out defines for second pass */
  state.stTopDefines = 
    state.stDefines = push_symbol_table(cmd_defines, state.case_insensitive);
  if (!state.cmd_line.radix)
    state.radix = 16;
  state.obj.symbol_num = 0;
  state.obj.section_num = 0;
  state.found_config = false;
  state.found_devid = false;
  state.found_idlocs = false;
  coff_init();
  cod_init();
  deps_init();
  lst_init();
  open_src(state.srcfilename, 0);
  if (!gp_debug_disable) {
    yydebug = 1;
  } else {
    yydebug = 0;
  }
  yyparse();

  assert(state.pass == 2);
  
  pop_symbol_table(state.stBuiltin);
  
  hex_init();

  if(state.memory_dump)
    print_i_memory(state.i_memory, 
                   state.device.class == PROC_CLASS_PIC16E ? 1 : 0);

  /* Maybe produce a symbol table */
  if (state.lst.symboltable) {
    lst_throw(); /* Start symbol table on a fresh page */
    lst_symbol_table(state.stGlobal);
    lst_defines_table(state.stDefines);
  }

  /* Maybe produce a memory map */
  if ((state.mode == absolute) && (state.lst.memorymap)) {
    lst_memory_map(state.i_memory);
  }
  
  /* Finish off the object, dependency, listing, and symbol files*/
  coff_close_file();
  deps_close();
  lst_close();
  if (state.processor_info)
    cod_close_file();
  free_files();

  if ((state.num.errors > 0) ||
      (gp_num_errors > 0))
    return EXIT_FAILURE;
  else
    return EXIT_SUCCESS;
}


syntax highlighted by Code2HTML, v. 0.9.1