/*
   Copyright (C) 1998 T. Scott Dattalo

This file is part of gpsim.

gpsim 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.

gpsim 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 gpasm; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

#include <stdio.h>
#ifdef _WIN32
#include "uxtime.h"
#endif
#ifndef _MSC_VER
#include <sys/time.h>
#endif
#include <time.h>
#include <iostream>
#include <iomanip>
#include <string>
#include <map>

#ifndef _WIN32
#if !defined(_MAX_PATH)
  #define _MAX_PATH 1024
#endif
#include <unistd.h>
#else
#include <direct.h>
#endif


#include "../config.h"

#include "errors.h"
#include "fopen-path.h"
#include "program_files.h"
#include "sim_context.h"
#include "breakpoints.h"
#include "trace.h"

//================================================================================
// Global Declarations
//  FIXME -  move these global references somewhere else

// don't print out a bunch of garbage while initializing


//================================================================================
//
// pic_processor
//
// This file contains all (most?) of the code that simulates those features 
// common to all pic microcontrollers.
//
//

CSimulationContext::CSimulationContext() :
  m_bEnableLoadSource(*new Boolean("EnableSourceLoad", true,
    "Enables and disables loading of source code")) {
  active_cpu_id = 0;
  cpu_ids = 0;
  m_bEnableLoadSource.setClearableSymbol(false);
  m_pbUserCanceled = NULL;
}

void CSimulationContext::Initialize() {
  get_symbol_table().add(&m_bEnableLoadSource);
}

CSimulationContext *CSimulationContext::s_SimulationContext = new CSimulationContext();

CSimulationContext *CSimulationContext::GetContext() {
  return s_SimulationContext;
}

bool CSimulationContext::SetDefaultProcessor(const char * processor_type,
                                             const char * processor_new_name) 
{
  if (processor_type) {
    ProcessorConstructor *pc = ProcessorConstructorList::GetList()->findByType(processor_type);

    if (pc) {
      m_DefProcessorName    = processor_type;
      if(processor_new_name == NULL)
	m_DefProcessorNameNew.clear();
      else
	m_DefProcessorNameNew = processor_new_name;
      return true;
    }
  } else {

    m_DefProcessorNameNew = processor_new_name;

  }

  return false;
}

//-------------------------------------------------------------------
Processor * CSimulationContext::SetProcessorByType(const char * processor_type,
                                                   const char * processor_new_name)
{
  Processor *p;
  CProcessorList::iterator it = processor_list.findByType(string(processor_type));
  GetBreakpoints().clear_all(GetActiveCPU());
  GetSymbolTable().Reinitialize();
  if(processor_list.end() == it) {
    p = add_processor(processor_type,processor_new_name);
  }
  else {
    p = it->second;
    delete p;
    p = add_processor(processor_type,processor_new_name);
//    p->init
  }
  return p;
}

//-------------------------------------------------------------------
Processor * CSimulationContext::add_processor(const char * processor_type,
                                              const char * processor_new_name)
{
  if(verbose)
    cout << "Trying to add new processor '" << processor_type << "' named '" 
	 << processor_new_name << "'\n";

  ProcessorConstructor *pc = ProcessorConstructorList::GetList()->findByType(processor_type);
  if(pc) {
    return add_processor(pc,processor_new_name ? processor_new_name : m_DefProcessorNameNew.c_str());
  } else
    cout << processor_type << " is not a valid processor.\n"
      "(try 'processor list' to see a list of valid processors.\n";
  return 0;
}

Processor * CSimulationContext::add_processor(ProcessorConstructor *pc,
					      const char * processor_new_name)
{
  Processor *  p = pc->ConstructProcessor(processor_new_name);
  if(p) {
    add_processor(p);
    p->m_pConstructorObject = pc;
  }
  else
    cout << " unable to add a processor (BUG?)\n";
  return p;
}

Processor * CSimulationContext::add_processor(Processor *p)
{
    processor_list.insert(CProcessorList::value_type(p->name(), p));
    p->initializeAttributes();
    active_cpu = p;
    //p->processor_id = 
    active_cpu_id = ++cpu_ids;
    if(verbose) {
      cout << p->name() << '\n';
      cout << "Program Memory size " <<  p->program_memory_size() << '\n';
      cout << "Register Memory size " <<  p->register_memory_size() << '\n';
    }

    trace.switch_cpus(p);
    // Tell the gui or any modules that are interfaced to gpsim
    // that a new processor has been declared.
    gi.new_processor(p);

    return p;

  return 0;
}

int CSimulationContext::LoadProgram(const char *filename,
                                    const char *pProcessorType,
                                    Processor **ppProcessor,
				    const char *pProcessorName)
{
  bool bReturn = false;
  Processor *pProcessor;
  FILE * pFile = fopen_path (filename, "rb");
  if(pFile == NULL) {
    char cw[_MAX_PATH];

    perror((string("failed to open program file ") + filename).c_str());
    getcwd(cw, sizeof(cw));
    cerr << "current working directory is ";
    cerr << cw;
    cerr << endl;
    return false;
  }
  if(pProcessorType != NULL) {
    pProcessor = SetProcessorByType(pProcessorType, NULL);
    if(pProcessor != NULL) {
      bReturn  = pProcessor->LoadProgramFile(filename, pFile, pProcessorName);
    }
  }
  else if(!m_DefProcessorName.empty()) {
    pProcessor = SetProcessorByType(m_DefProcessorName.c_str(), NULL);
    if(pProcessor != NULL) {
      bReturn  = pProcessor->LoadProgramFile(filename, pFile, pProcessorName);
    }
  }
  else {
    pProcessor = NULL;
    if (!m_DefProcessorNameNew.empty())
      pProcessorName = m_DefProcessorNameNew.c_str();
    // use processor defined in program file
    bReturn  = ProgramFileTypeList::GetList().LoadProgramFile(
			   &pProcessor, filename, pFile, pProcessorName);

  }

  fclose(pFile);
  if(bReturn) {
    // Tell all of the interfaces that a new program exists.
    gi.new_program(pProcessor);
  }
  if(ppProcessor != NULL) {
    *ppProcessor = pProcessor;
  }
  return bReturn;
}

//------------------------------------------------------------------------
// dump_processor_list - print out all of the processors a user is 
//                       simulating.

void CSimulationContext::dump_processor_list(void)
{

  cout << "Processor List\n";

  bool have_processors = 0;
  CProcessorList::iterator processor_iterator; 
  for (processor_iterator = processor_list.begin();
       processor_iterator != processor_list.end(); 
       processor_iterator++) {
      CProcessorList::value_type vt = *processor_iterator;
      Processor *p = vt.second;
      cout << p->name() << '\n';
      have_processors = 1;
    }

  if(!have_processors)
    cout << "(empty)\n";

}

void CSimulationContext::Clear() {
  GetBreakpoints().clear_all(GetActiveCPU());
  CProcessorList::iterator processor_iterator; 
  for (processor_iterator = processor_list.begin();
       processor_iterator != processor_list.end(); 
       processor_iterator++) {
      CProcessorList::value_type vt = *processor_iterator;
      Processor *p = vt.second;
      delete p;
    }
  GetSymbolTable().clear_all();
  processor_list.clear();
}

void CSimulationContext::Reset(RESET_TYPE r) {
  Symbol_Table &ST = get_symbol_table();
  Symbol_Table::module_symbol_iterator it;
  Symbol_Table::module_symbol_iterator itEnd = ST.endModuleSymbol();
  for(it = ST.beginModuleSymbol(); it != itEnd; it++) {
      Module *m = (*it)->get_module();
      if(m) {
        m->reset(r);
      }
  }
}

void CSimulationContext::NotifyUserCanceled() {
  if(m_pbUserCanceled != NULL) {
    *m_pbUserCanceled = true;
    m_pbUserCanceled = NULL;
    return;
  }
  if(CSimulationContext::GetContext()->GetActiveCPU()->simulation_mode
    == eSM_RUNNING) {
    // If we get a CTRL->C while processing a command file
    // we should probably stop the command file processing.
    CSimulationContext::GetContext()->GetBreakpoints().halt();
  }
}

extern Symbol_Table symbol_table;  // There's only one instance of "the" symbol table
Symbol_Table & CSimulationContext::GetSymbolTable() {
  return symbol_table;
}

Breakpoints & CSimulationContext::GetBreakpoints() {
  return get_bp();
}

Processor * CSimulationContext::GetActiveCPU() {
  return get_active_cpu();
}

Cycle_Counter * CSimulationContext::GetCycleCounter() {
  return &cycles;
}

CSimulationContext::CProcessorList::iterator
CSimulationContext::CProcessorList::findByType(const key_type& _Keyval) {
  // First find a ProcessorConstructor that matches the
  // processor type we are looking for. This should handle
  // naming variations.
  ProcessorConstructorList * pcl = ProcessorConstructorList::GetList();
  ProcessorConstructor * pc = pcl->findByType(_Keyval.c_str());
  if(pc == NULL)
    return end();
  // Now find the specific allocated processor that
  // was created with the ProcessorConstructor object 
  // we found above.
  iterator it;
  iterator itEnd = end();
  for(it = begin(); it != itEnd; it++) {
    if(it->second->m_pConstructorObject == pc) {
      return it;
    }
  }
  return itEnd;
}


syntax highlighted by Code2HTML, v. 0.9.1