/*
   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"
#include "unistd.h"
#else
#include <unistd.h>
#endif
#ifndef _MSC_VER
#include <sys/time.h>
#endif
#include <time.h>
#include <iostream>
#include <iomanip>
#include <string>
#include <map>

#include "exports.h"
#include "gpsim_def.h"
#include "pic-processor.h"
#include "pic-registers.h"
#include "picdis.h"
#include "symbol.h"
#include "stimuli.h"
#include "p16x5x.h"
#include "p16f62x.h"
#include "p16f8x.h"
#include "p16x8x.h"
#include "p16f87x.h"
#include "p16x6x.h"
#include "p16x7x.h"
#include "p12x.h"
#ifdef P17C7XX	// code no longer works
#include "p17c75x.h"
#endif
#include "p18x.h"
#include "icd.h"

#include "fopen-path.h"

guint64 simulation_start_cycle;

#include "cod.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.
//
//

ProcessorConstructor pP10F200(P10F200::construct ,
			      "__10F200", "pic10f200",  "p10f200", "10f200");
ProcessorConstructor pP10F202(P10F202::construct ,
			      "__10F202", "pic10f202",  "p10f202", "10f202");
ProcessorConstructor pP12C508(P12C508::construct ,
			      "__12C508", "pic12c508",  "p12c508", "12c508");
ProcessorConstructor pP12C509(P12C509::construct ,
			      "__12C509", "pic12c509",  "p12c509", "12c509");
ProcessorConstructor pP12CE518(P12CE518::construct ,
			      "__12ce518", "pic12ce518",  "p12ce518", "12ce518");
ProcessorConstructor pP12CE519(P12CE519::construct ,
			      "__12ce519", "pic12ce519",  "p12ce519", "12ce519");
ProcessorConstructor pP16C84(P16C84::construct ,
			     "__16C84",  "pic16c84",   "p16c84", "16c84");
ProcessorConstructor pP16CR83(P16CR83::construct ,
			      "__16CR83", "pic16cr83",  "p16cr83", "16cr83");
ProcessorConstructor pP16CR84(P16CR84::construct ,
			      "__16CR84", "pic16cr84",  "p16cr84", "16cr84");
ProcessorConstructor pP16F83(P16F83::construct ,
			     "__16F83",   "pic16f83",   "p16f83", "16f83");
ProcessorConstructor pP16F84(P16F84::construct ,
			     "__16F84",   "pic16f84",   "p16f84", "16f84");
ProcessorConstructor pP16C54(P16C54::construct ,
			     "__16C54",   "pic16c54",   "p16c54", "16c54");
ProcessorConstructor pP16C55(P16C55::construct ,
			     "__16C55",   "pic16c55",   "p16c55", "16c55");
ProcessorConstructor pP16C56(P16C56::construct ,
			     "__16C56",   "pic16c56",   "p16c56", "16c56");
ProcessorConstructor pP16C61(P16C61::construct ,
			     "__16C61",   "pic16c61",   "p16c61", "16c61");
ProcessorConstructor pP16C71(P16C71::construct ,
			     "__16C71",   "pic16c71",   "p16c71", "16c71");
ProcessorConstructor pP16C712(P16C712::construct ,
			      "__16C712",  "pic16c712",  "p16c712", "16c712");
ProcessorConstructor pP16C716(P16C716::construct ,
			      "__16C716",  "pic16c716",  "p16c716", "16c716");
ProcessorConstructor pP16C62(P16C62::construct ,
			     "__16C62",   "pic16c62",   "p16c62", "16c62");
ProcessorConstructor pP16C62A(P16C62::construct ,
			      "__16C62A", "pic16c62a",  "p16c62a", "16c62a");
ProcessorConstructor pP16CR62(P16C62::construct ,
			      "__16CR62", "pic16cr62",  "p16cr62", "16cr62");
ProcessorConstructor pP16C63(P16C63::construct ,
			     "__16C63",   "pic16c63",   "p16c63", "16c63");
ProcessorConstructor pP16C64(P16C64::construct ,
			     "__16C64",   "pic16c64",   "p16c64", "16c64");
ProcessorConstructor pP16C65A(P16C65::construct ,
			     "__16C65A", "pic16c65a",  "p16c65a", "16c65a");
ProcessorConstructor pP16C65(P16C65::construct ,
			     "__16C65",   "pic16c65",   "p16c65", "16c65");
ProcessorConstructor pP16C72(P16C72::construct ,
			     "__16C72",   "pic16c72",   "p16c72", "16c72");
ProcessorConstructor pP16C73(P16C73::construct ,
			     "__16C73",   "pic16c73",   "p16c73", "16c73");
ProcessorConstructor pP16C74(P16C74::construct ,
			     "__16C74",   "pic16c74",   "p16c74", "16c74");
ProcessorConstructor pP16F73(P16F73::construct ,
			     "__16F73",   "pic16f73",   "p16f73", "16f73");
ProcessorConstructor pP16F74(P16F74::construct ,
			     "__16F74",   "pic16f74",   "p16f74", "16f74");
ProcessorConstructor pP16F627(P16F627::construct ,
			      "__16F627", "pic16f627",  "p16f627", "16f627");
ProcessorConstructor pP16F627A(P16F627::construct ,
			      "__16F627A", "pic16f627a",  "p16f627a", "16f627a");
ProcessorConstructor pP16F628(P16F628::construct ,
			      "__16F628", "pic16f628",  "p16f628", "16f628");
ProcessorConstructor pP16F628A(P16F628::construct ,
			      "__16F628A", "pic16f628a",  "p16f628a", "16f628a");
ProcessorConstructor pP16F648(P16F648::construct ,
			      "__16F648", "pic16f648",  "p16f648", "16f648");
ProcessorConstructor pP16F648A(P16F648::construct ,
			      "__16F648A", "pic16f648a",  "p16f648a", "16f648a");
ProcessorConstructor pP16F87(P16F87::construct ,
			      "__16F87", "pic16f87",  "p16f87", "16f87");
ProcessorConstructor pP16F88(P16F88::construct ,
			      "__16F88", "pic16f88",  "p16f88", "16f88");
ProcessorConstructor pP16F871(P16F871::construct ,
			      "__16F871", "pic16f871",  "p16f871", "16f871");
ProcessorConstructor pP16F873(P16F873::construct ,
			      "__16F873", "pic16f873",  "p16f873", "16f873");
ProcessorConstructor pP16F874(P16F874::construct ,
			      "__16F874", "pic16f874",  "p16f874", "16f874");
ProcessorConstructor pP16F876(P16F876::construct ,
			      "__16F876", "pic16f876",  "p16f876", "16f876");
ProcessorConstructor pP16F877(P16F877::construct ,
			      "__16F877", "pic16f877",  "p16f877", "16f877");
ProcessorConstructor pP16F873A(P16F873A::construct ,
			      "__16F873a", "pic16f873a", "p16f873a", "16f873a");
ProcessorConstructor pP16F874A(P16F874A::construct ,
			      "__16F874a", "pic16f874a", "p16f874a", "16f874a");
ProcessorConstructor pP16F876A(P16F876A::construct ,
			      "__16F876a", "pic16f876a", "p16f876a", "16f876a");
ProcessorConstructor pP16F877A(P16F877A::construct ,
			      "__16F877a", "pic16f877a", "p16f877a", "16f877a");
#ifdef P17C7XX	// code no longer works
ProcessorConstructor pP17C7xx(P17C7xx::construct ,
			      "__17C7xx", "pic17c7xx",  "p17c7xx", "17c7xx");
ProcessorConstructor pP17C75x(P17C75x::construct ,
			      "__17C75x", "pic17c75x",  "p17c75x", "17c75x");
ProcessorConstructor pP17C752(P17C752::construct ,
			      "__17C752", "pic17c752",  "p17c752", "17c752");
ProcessorConstructor pP17C756(P17C756::construct ,
			      "__17C756", "pic17c756",  "p17c756", "17c756");
ProcessorConstructor pP17C756A(P17C756A::construct ,
			       "__17C756A", "pic17c756a",  "p17c756a", "17c756a");
ProcessorConstructor pP17C762(P17C762::construct ,
			      "__17C762", "pic17c762",  "p17c762", "17c762");
ProcessorConstructor pP17C766(P17C766::construct ,
			      "__17C766", "pic17c766",  "p17c766", "17c766");
#endif // P17C7XX
ProcessorConstructor pP18C242(P18C242::construct ,
			      "__18C242", "pic18c242",  "p18c242", "18c242");
ProcessorConstructor pP18C252(P18C252::construct ,
			      "__18C252", "pic18c252",  "p18c252", "18c252");
ProcessorConstructor pP18C442(P18C442::construct ,
			      "__18C442", "pic18c442",  "p18c442", "18c442");
ProcessorConstructor pP18C452(P18C452::construct ,
			      "__18C452", "pic18c452",  "p18c452", "18c452");
ProcessorConstructor pP18F242(P18F242::construct ,
			      "__18F242", "pic18f242",  "p18f242", "18f242");
ProcessorConstructor pP18F252(P18F252::construct ,
			      "__18F252", "pic18f252",  "p18f252", "18f252");
ProcessorConstructor pP18F442(P18F442::construct ,
			      "__18F442", "pic18f442",  "p18f442", "18f442");
ProcessorConstructor pP18F248(P18F248::construct ,
			      "__18F248", "pic18f248",  "p18f248", "18f248");
ProcessorConstructor pP18F448(P18F448::construct ,
			      "__18F448", "pic18f448",  "p18f448", "18f448");
ProcessorConstructor pP18F452(P18F452::construct,
			      "__18F452", "pic18f452",  "p18f452", "18f452");
ProcessorConstructor pP18F1220(P18F1220::construct,
			      "__18F1220", "pic18f1220",  "p18f1220", "18f1220");
ProcessorConstructor pP18F1320(P18F1320::construct,
			      "__18F1320", "pic18f1320",  "p18f1320", "18f1320");


void pic_processor::set_eeprom(EEPROM *e)
{ 
  eeprom = e; 

  ema.set_cpu(this);
  ema.set_Registers(e->rom, e->rom_size);

}
//-------------------------------------------------------------------
//
// sleep - Begin sleeping and stay asleep until something causes a wake
//

void pic_processor::sleep ()
{
  simulation_mode = eSM_SLEEPING;

  if(!bp.have_sleep())
    return;

  do
    {
      get_cycles().increment();   // burn cycles until something wakes us
    } while(bp.have_sleep() && !bp.have_halt());

  if(!bp.have_sleep())
    pc->increment();

  simulation_mode = eSM_RUNNING;

}
//-------------------------------------------------------------------
//
// enter_sleep - The processor is about to go to sleep, so update 
//  the status register.

void pic_processor::enter_sleep()
{
  status->put_TO(1);
  status->put_PD(0);
}

//-------------------------------------------------------------------
//
// pm_write - program memory write
//

void pic_processor::pm_write ()
{
  //  simulation_mode = PM_WRITE;

  do
    {
      get_cycles().increment();     // burn cycles until we're through writing
    } while(bp.have_pm_write());

  simulation_mode = eSM_RUNNING;

}

static bool realtime_mode = false;
static bool realtime_mode_with_gui = false;

void EnableRealTimeMode(bool bEnable) {
  realtime_mode = bEnable;
}

void EnableRealTimeModeWithGui(bool bEnable) {
  realtime_mode_with_gui = bEnable;
}

extern void update_gui();

class RealTimeBreakPoint : public TriggerObject
{
public:
  Processor *cpu;
  struct timeval tv_start;
  guint64 cycle_start;
  guint64 future_cycle;
  int warntimer;
  guint64 period;

  RealTimeBreakPoint()
  {
    cpu = 0;
    warntimer = 1;
    period = 1;
    future_cycle = 0;
  }

  void start(Processor *active_cpu) 
  {
    if(!active_cpu)
      return;

    // Grab the system time and record the simulated pic's time.
    // We'll then set a break point a short time in the future
    // and compare how the two track.

    cpu = active_cpu;

    gettimeofday(&tv_start,0);

    cycle_start=get_cycles().value;

    guint64 fc = cycle_start+100;

    cout << "real time start : " << future_cycle << '\n';

    if(future_cycle)
      get_cycles().reassign_break(future_cycle, fc, this);
    else
      get_cycles().set_break(fc, this);

    future_cycle = fc;

  }

  void stop()
  {

    // Clear any pending break point.
    cout << "real time stop : " << future_cycle << '\n';

    if(future_cycle) {
      cout << " real time clearing\n";
      get_cycles().clear_break(this);
      future_cycle = 0;
    }
      
  }

  void callback()
  {
    gint64 system_time;
    double diff;
    struct timeval tv;

    // We just hit the break point. A few moments ago we 
    // grabbed a snap shot of the system time and the simulated
    // pic's time. Now we're going to compare the two deltas and
    // see how well they've tracked. If the host is running
    // way faster than the PIC, we'll put the host to sleep 
    // briefly.


    gettimeofday(&tv,0);

    system_time = (tv.tv_sec-tv_start.tv_sec)*1000000+(tv.tv_usec-tv_start.tv_usec); // in micro-seconds

    diff = system_time - ((get_cycles().value-cycle_start)*4.0e6*cpu->get_OSCperiod());

    guint64  idiff;
    if( diff < 0 )
    {
	// we are simulating too fast

        idiff = (guint64)(-diff/4);

	if(idiff>1000)
	    period -= idiff/500;
	if(period<1)
            period=1;

	// Then sleep for a while
	if(idiff)
	  usleep((unsigned int)idiff);
    }
    else
    {
      idiff = (guint64)(diff/4);

	if(idiff>1000)
	    period+=idiff/500;
	if(period>10000)
            period=10000;

	if(idiff>1000000)
	{
	    // we are simulating too slow
	    if(warntimer<10)
		warntimer++;
	    else
	    {
		warntimer=0;
		puts("Processor is too slow for realtime mode!");
	    }
	}
	else
            warntimer=0;
    }

    guint64 delta_cycles= (guint64)(100*period*cpu->get_frequency()/4000000);
    if(delta_cycles<1)
      delta_cycles=1;

    // Look at realtime_mode_with_gui and update the gui if true
    if(realtime_mode_with_gui)
    {
	update_gui();
    }


    guint64 fc = get_cycles().value + delta_cycles;

    if(future_cycle)
      get_cycles().reassign_break(future_cycle, fc, this);
    else
      get_cycles().set_break(fc, this);

    future_cycle = fc;

  }

};

RealTimeBreakPoint realtime_cbp;

//-------------------------------------------------------------------
void pic_processor::save_state()
{
  Processor::save_state();

  if(W)
    W->put_trace_state(W->value);

  if(eeprom)
    eeprom->save_state();

  option_reg.put_trace_state(option_reg.value);
}

//-------------------------------------------------------------------
//
// run  -- Begin simulating and don't stop until there is a break.
//
void pic_processor::run (bool refresh)
{
  if(get_use_icd())
  {
    cout  << "WARNING: gui_refresh is not being called " 
	  <<  __FILE__<<':'<<__LINE__<<endl;

      simulation_mode = eSM_RUNNING;
      icd_run();
      while(!icd_stopped())
      {
	//#ifdef HAVE_GUI
	//	  if(use_gui)
	//	      gui_refresh();
	//#endif
      }
      simulation_mode=eSM_STOPPED;
      disassemble((signed int)pc->get_value(), (signed int)pc->get_value());
      gi.simulation_has_stopped();
      return;
  }


  if(simulation_mode != eSM_STOPPED) {
    if(verbose)
      cout << "Ignoring run request because simulation is not stopped\n";
    return;
  }

  simulation_mode = eSM_RUNNING;

  if(realtime_mode)
    realtime_cbp.start(active_cpu);

  // If the first instruction we're simulating is a break point, then ignore it.

  simulation_start_cycle = get_cycles().value;

  do {

    // Take one step to get past any break point.
    step(1,false);

    do {

      program_memory[pc->value]->execute();

    } while(!bp.global_break);

    if(bp.have_interrupt())
      interrupt();

    if(bp.have_sleep())
      sleep();

    if(bp.have_pm_write())
      pm_write();

    if(bp.have_socket_break()) {
      cout << " socket break point \n";
      Interface *i = gi.get_socket_interface();
      if (i)
	i->Update(0);
      bp.clear_socket_break();
    }

  } while(!bp.global_break);

  if(realtime_mode)
    realtime_cbp.stop();

  bp.clear_global();
  trace.cycle_counter(get_cycles().value);

  simulation_mode = eSM_STOPPED;

  if(refresh) {
    trace.dump_last_instruction(); 
    gi.simulation_has_stopped();
  }


}


//-------------------------------------------------------------------
//
// step - Simulate one (or more) instructions. If a breakpoint is set
// at the current PC-> 'step' will go right through it. (That's supposed
// to be a feature.)
//

void pic_processor::step (unsigned int steps, bool refresh)
{

  if(get_use_icd())
  {
      if(steps!=1)
      {
	  cout << "Can only step one step in ICD mode"<<endl;
      }
      icd_step();
      pc->get_value();
      disassemble((signed int)pc->value, (signed int)pc->value); // FIXME, don't want this in HLL ICD mode.
      if(refresh)
	gi.simulation_has_stopped();
      return;
  }


  Processor::step(steps,refresh);
}

//-------------------------------------------------------------------
//
// step_over - In most cases, step_over will simulate just one instruction.
// However, if the next instruction is a branching one (e.g. goto, call, 
// return, etc.) then a break point will be set after it and gpsim will
// begin 'running'. This is useful for stepping over time-consuming calls.
//

void pic_processor::step_over (bool refresh)
{

  if(simulation_mode != eSM_STOPPED) {
    if(verbose)
      cout << "Ignoring step-over request because simulation is not stopped\n";
    return;
  }

  unsigned int saved_pc = pma->get_PC();
  instruction *nextInstruction = pma->getFromAddress(saved_pc);
  if (!nextInstruction) {
    // this is really fatal...
    return;
  }
  unsigned int nextExpected_pc = 
    saved_pc + map_pm_index2address(nextInstruction->instruction_size());

  step(1,refresh); // Try one step

  // if the pc did not advance just one instruction, then some kind of branch occurred.

  unsigned int current_pc = pma->get_PC();
  if( ! (current_pc >= saved_pc && current_pc <= nextExpected_pc)) {

    // If the branch is not a skip instruction then we'll set a break point and run.
    // (note, the test that's performed will treat a goto $+2 as a skip.
    
    instruction *nextNextInstruction = pma->getFromAddress(nextExpected_pc);
    unsigned int nextNextExpected_pc = nextExpected_pc + 
      (nextNextInstruction ? map_pm_index2address(nextNextInstruction->instruction_size()) : 0);

    if (! (current_pc >= saved_pc && current_pc <= nextNextExpected_pc)) {
    
      unsigned int bp_num = pma->set_break_at_address(nextExpected_pc);
      if (bp_num != INVALID_VALUE) {
	run();
	bp.clear(bp_num);
      }
    }

  }

  // note that we don't need to tell the gui to update its windows since
  // that is already done by step() or run().

}

//-------------------------------------------------------------------
//
// finish
//
// this method really only applies to processors with stacks.

void pic_processor::finish()
{
  if(!stack)
    return;

  run_to_address( stack->contents[stack->pointer-1 & stack->stack_mask]);

}


//-------------------------------------------------------------------
//
// reset - reset the pic based on the desired reset type.
//

void pic_processor::reset (RESET_TYPE r)
{
  bool bHaltSimulation = true;

  if(get_use_icd())
  {
      puts("RESET");
      icd_reset();
      disassemble((signed int)pc->get_value(), (signed int)pc->get_value());
      gi.simulation_has_stopped();
      return;
  }

  if(r == SOFT_RESET)
    {
      trace.reset(r);
      pc->reset();
      gi.simulation_has_stopped();
      cout << " --- Soft Reset (not fully implemented)\n";
      return;
    }

  for(unsigned int i=0; i<register_memory_size(); i++)
    if (registers[i])
      registers[i]->reset(r);


  trace.reset(r);
  pc->reset();
  stack->reset();
  bp.clear_global();

  switch (r) {
  case POR_RESET:
    status->put_TO(1);
    status->put_PD(1);

    if(verbose) {
      cout << "POR\n";
      if(config_modes) config_modes->print();
    }
    wdt.reset();

    bHaltSimulation = false;
    break;
  case WDT_RESET:
    status->put_TO(0);
    break;
  default:
    break;
  }

  if(bHaltSimulation || getBreakOnReset())
    bp.halt();

  gi.simulation_has_stopped();
}

//-------------------------------------------------------------------
//
// por - power on reset %%% FIX ME %%% needs lots of work...
//

void pic_processor::por()
{
  wdt.reset();

}

//-------------------------------------------------------------------
//
// pic_processor -- constructor
//

pic_processor::pic_processor(const char *_name, const char *_desc)
  : Processor(_name,_desc),
    wdt(this, 18.0e-3),indf(0),fsr(0), stack(0), status(0), W(0), pcl(0), pclath(0), m_configMemory(0)
{
  m_Capabilities = eSTACK | eWATCHDOGTIMER;

  if(verbose)
    cout << "pic_processor constructor\n";

  eeprom = 0;
  config_modes = create_ConfigMode();

  pll_factor = 0;

  Integer::setDefaultBitmask(0xff);

  // Test code for logging to disk:
  GetTraceLog().switch_cpus(this);
}
//-------------------------------------------------------------------
pic_processor::~pic_processor()
{

}
//-------------------------------------------------------------------
//
// 
//    create
//
//  The purpose of this member function is to 'create' a pic processor.
// Since this is a base class member function, only those things that
// are common to all pics are created.

void pic_processor::create ()
{

  init_program_memory (program_memory_size());

  init_register_memory (register_memory_size());

  create_stack();

  // Now, initialize the core stuff:
  pc->set_cpu(this);

  W = new WREG(this);

  pcl = new PCL;
  pclath = new PCLATH;
  status = new Status_register;
  //FIXME more
  
  W->new_name("W");
  
  indf = new INDF;

  register_bank = &registers[0];  // Define the active register bank 
  W->value.put(0);

  Vdd = 5.0;                      // Assume 5.0 volt power supply

  if(pma) {
    
    rma.SpecialRegisters.push_back(new PCHelper(pma));
    rma.SpecialRegisters.push_back(status);
    rma.SpecialRegisters.push_back(W);

    pma->SpecialRegisters.push_back(new PCHelper(pma));
    pma->SpecialRegisters.push_back(status);
    pma->SpecialRegisters.push_back(W);

  }

  create_config_memory();

}

//-------------------------------------------------------------------
//
// add_sfr_register
//
// The purpose of this routine is to add one special function register
// to the file registers. If the sfr has a physical address (like the 
// status or tmr0 registers) then a pointer to that register will be
// placed in the file register map. 

// FIXME It doesn't make any sense to initialize the por_value here!
// FIXME The preferred way is to initialize all member data in their
// FIXME parent's constructor.

void pic_processor::add_sfr_register(Register *reg, unsigned int addr,
				     RegisterValue por_value, const char *new_name)
{

  reg->set_cpu(this);
  if(addr < register_memory_size())
    {
      registers[addr] = reg;
      registers[addr]->address = addr;
      registers[addr]->alias_mask = 0;
      if(new_name)
        registers[addr]->new_name(new_name);

      RegisterValue rv = getWriteTT(addr);
      registers[addr]->set_write_trace(rv);
      rv = getReadTT(addr);
      registers[addr]->set_read_trace(rv);
    }

  reg->value       = por_value;
  reg->por_value   = por_value;  /// FIXME why are we doing this?
  reg->initialize();
}

//-------------------------------------------------------------------
//
// init_program_memory
//
// The purpose of this member function is to allocate memory for the
// pic's code space. The 'memory_size' parameter tells how much memory
// is to be allocated AND it should be an integer of the form of 2^n. 
// If the memory size is not of the form of 2^n, then this routine will
// round up to the next integer that is of the form 2^n.
//   Once the memory has been allocated, this routine will initialize
// it with the 'bad_instruction'. The bad_instruction is an instantiation
// of the instruction class that chokes gpsim if it is executed. Note that
// there is only one instance of 'bad_instruction'.

void pic_processor::init_program_memory (unsigned int memory_size)
{

  if(verbose)
    cout << "Initializing program memory: 0x"<<memory_size<<" words\n";

  // The memory_size_mask is used by the branching instructions 

  pc->memory_size_mask = memory_size - 1;

  Processor::init_program_memory(memory_size);
}

//-------------------------------------------------------------------
//
// Add a symbol table entry for each one of the sfr's
//

void pic_processor::create_symbols ()
{

  if(verbose)
    cout << __FUNCTION__ << " register memory size = " << register_memory_size() << '\n';

  for(unsigned int i = 0; i<register_memory_size(); i++)
    {
      switch (registers[i]->isa()) {
      case Register::SFR_REGISTER:
	if(!symbol_table.find((char *)registers[i]->name().c_str()))
	  symbol_table.add_register(registers[i]);
	break;
      default:
	break;
      }
    }

  val_symbol *vpc = new val_symbol(pc);
  vpc->set_description("Program Counter");
  symbol_table.add(vpc);

}


//-------------------------------------------------------------------

bool pic_processor::set_config_word(unsigned int address,unsigned int cfg_word)
{

  // Clear all of the configuration bits in config_modes and then
  // reset each of them based on the config bits in cfg_word:
  //config_modes &= ~(CM_WDTE);
  //config_modes |= ( (cfg_word & WDTE) ? CM_WDTE : 0);

  if((address == config_word_address()) && config_modes) {

    config_word = cfg_word;

    config_modes->config_mode = (config_modes->config_mode & ~7) | (cfg_word & 7);

    if((bool)verbose && config_modes)
      config_modes->print();

    return true;
  }

  return false;

}


unsigned int pic_processor::get_config_word(unsigned int address)
{
  return address == config_word_address() ? config_word : 0xffffffff;
}



//-------------------------------------------------------------------
//
// load_hex
//

bool pic_processor::LoadProgramFile(const char *pFilename, FILE *pFile,
				    const char *pProcessorName)
{
  Processor * pProcessor = this;
  // Tries the file type based on the file extension first.
  // If it fails tries the other type. This code will need
  // to change if pic_processor is moved to its own module
  // because then we cannot garrentee that these file types
  // will be the first two in the list.
  ProgramFileType * aFileTypes[] = {
    ProgramFileTypeList::GetList()[0],  // IntelHexProgramFileType
    ProgramFileTypeList::GetList()[1]   // PicCodProgramFileType
  };
  if(IsFileExtension(pFilename,"cod")) {
    // If 'cod' file extension, try PicCodProgramFileType first
    swap(aFileTypes[0], aFileTypes[1]);
  }
  int iReturn  = aFileTypes[0]->LoadProgramFile(&pProcessor, pFilename, pFile, pProcessorName);
  if (iReturn != ProgramFileType::SUCCESS) {
    fseek(pFile, 0, SEEK_SET);
    iReturn = aFileTypes[1]->LoadProgramFile(&pProcessor, pFilename, pFile, pProcessorName);
  }
  return iReturn == ProgramFileType::SUCCESS;
}

//-------------------------------------------------------------------
//-------------------------------------------------------------------
//  ConfigMode
//
void ConfigMode::print()
{


  if(config_mode & CM_FOSC1x) {
    // Internal Oscillator type processor 

    switch(config_mode& (CM_FOSC0 | CM_FOSC1)) {  // Lower two bits are the clock type
    case 0: cout << "LP"; break;
    case CM_FOSC0: cout << "XT"; break;
    case CM_FOSC1: cout << "Internal RC"; break;
    case (CM_FOSC0|CM_FOSC1): cout << "External RC"; break;

    } 
  }else {
    switch(config_mode& (CM_FOSC0 | CM_FOSC1)) {  // Lower two bits are the clock type
    case 0: cout << "LP"; break;
    case CM_FOSC0: cout << "XT"; break;
    case CM_FOSC1: cout << "HS"; break;
    case (CM_FOSC0|CM_FOSC1): cout << "RC"; break;
    }
  }

  cout << " oscillator\n";

  if(valid_bits & CM_WDTE) 
    cout << " WDT is " << (get_wdt() ? "enabled\n" : "disabled\n");

  if(valid_bits & CM_MCLRE) 
    cout << "MCLR is " << (get_mclre() ? "enabled\n" : "disabled\n");

  if(valid_bits & CM_CP0) {

    if(valid_bits & CM_CP1) {
      cout << "CP0 is " << (get_cp0() ? "high\n" : "low\n");
      cout << "CP1 is " << (get_cp1() ? "high\n" : "low\n");
    } else {

      cout << "code protection is " << (get_cp0() ? "enabled\n" : "disabled\n");

    }
  }


}

//-------------------------------------------------------------------
//-------------------------------------------------------------------
void ProgramMemoryAccess::callback()
{

  if(_state)
    {
      _state = 0;
      //cout << __FUNCTION__ << " address= " << address << ", opcode= " << opcode << '\n';
      //cpu->program_memory[address]->opcode = opcode;
      put_opcode(_address,_opcode);
      trace.opcode_write(_address,_opcode);
      bp.clear_pm_write();
    }

}

//--------------------------------------------------
WDT::WDT(pic_processor *p_cpu, double _timeout)
  : cpu(p_cpu), breakpoint(0), future_cycle(0), timeout(_timeout), wdte(false)
{
}

//--------------------------------------------------
void WDT::update()
{
  if(wdte){

    value = (unsigned int )(cpu->get_frequency()*timeout);
    prescale = cpu->option_reg.get_psa() ? (cpu->option_reg.get_prescale()) : 0;

    if(future_cycle) {

      guint64 fc = get_cycles().value + value * (1<<prescale);

      //cout << "WDT::update:  moving break from " << future_cycle << " to " << fc << '\n';

      get_cycles().reassign_break(future_cycle, fc, this);
      future_cycle = fc;

    } else {
    
      future_cycle = get_cycles().value + value * (1<<prescale);

      get_cycles().set_break(future_cycle, this);
    }
  }

}

//--------------------------------------------------
// WDT::put - shouldn't be called?
//

void WDT::put(unsigned int new_value)
{
  value = new_value;

  update();

}
void WDT::set_timeout( double _timeout)
{
  timeout = _timeout;
  update();
}
void WDT::initialize(bool enable)
{
  wdte = enable;
  warned = 0;

  if(verbose)
    cout << " WDT init called "<< ( (enable) ? "enabling\n" :", but disabling WDT\n");

  if(wdte) {
    cout << "Enabling WDT " << " timeout = " << timeout << " seconds\n";
    value = (unsigned int) (cpu->get_frequency()*timeout);
    prescale = cpu->option_reg.get_psa() ? (cpu->option_reg.get_prescale()) : 0;

    future_cycle = get_cycles().value + value * (1<<prescale);

    get_cycles().set_break(future_cycle, this);

  } else {

    if (future_cycle) {
      cout << "Disabling WDT\n";
      get_cycles().clear_break(this);
      future_cycle = 0;
    }
  }

}

void WDT::reset()
{
  update();
}
void WDT::set_breakpoint(unsigned int bpn)
{
  breakpoint = bpn;
}

void WDT::callback()
{


  if(wdte) {
    cout<<"WDT timeout: " << hex << get_cycles().value << '\n';

    //future_cycle = 0;
    update();

    // The TO bit gets cleared when the WDT times out.
    cpu->status->put_TO(0);

    if(breakpoint)
      bp.halt();
    else 
    {
      bp.clear_sleep();
      cpu->reset(WDT_RESET);
    }
    /*    else if(bp.have_sleep()) {
      bp.clear_sleep();
    }else
      cpu->reset(WDT_RESET);
    */
  }

}

void WDT::clear()
{
  if(wdte)
    update();
  else
    {
      if(!warned)
	{
	  warned = 1;
	  cout << "The WDT is not enabled - clrwdt has no effect!\n";
	}
    }

}

void WDT::start_sleep()
{

  if(wdte) {
    prescale = 0;

    guint64 fc = get_cycles().value + value * (1<<prescale);

    //cout << "WDT::start_sleep:  moving break from " << future_cycle << " to " << fc << '\n';

    get_cycles().reassign_break(future_cycle, fc, this);

    future_cycle = fc;
  }
}

void WDT::new_prescale()
{

  update();

}

void WDT::callback_print()
{

  cout << "WDT\n";
}


//------------------------------------------------------------------------
// ConfigMemory - Base class
ConfigMemory::ConfigMemory(const char *_name, unsigned int default_val, const char *desc,
			   pic_processor *pCpu, unsigned int addr)
  : Integer(_name, default_val, desc), m_pCpu(pCpu), m_addr(addr)
{
  if (m_pCpu)
    m_pCpu->add_attribute(this);
}



syntax highlighted by Code2HTML, v. 0.9.1