/////////////////////////////////////////////////////////////////////////
// $Id: pc_system.cc,v 1.34 2003/06/07 19:16:51 vruppert Exp $
/////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2002  MandrakeSoft S.A.
//
//    MandrakeSoft S.A.
//    43, rue d'Aboukir
//    75002 Paris - France
//    http://www.linux-mandrake.com/
//    http://www.mandrakesoft.com/
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library 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
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA



#include "bochs.h"
#define LOG_THIS bx_pc_system.

#ifdef WIN32
#ifndef __MINGW32__
// #include <winsock2.h> // +++
#include <winsock.h>
#endif
#endif

#if BX_SHOW_IPS
unsigned long ips_count=0;
#endif

#if defined(PROVIDE_M_IPS)
double     m_ips; // Millions of Instructions Per Second
#endif

// Option for turning off BX_TIMER_DEBUG?
// Check out m_ips and ips

#define SpewPeriodicTimerInfo 0
#define MinAllowableTimerPeriod 1


#if SpewPeriodicTimerInfo
// If debugging, set the heartbeat to 5M cycles.  Each heartbeat
// spews the active timer info.
const Bit64u bx_pc_system_c::NullTimerInterval = 5000000;
#else
// This must be the maximum 32-bit unsigned int value, NOT (Bit64u) -1.
const Bit64u bx_pc_system_c::NullTimerInterval = 0xffffffff;
#endif

  // constructor
bx_pc_system_c::bx_pc_system_c(void)
{
  this->put("SYS");

  // Timer[0] is the null timer.  It is initialized as a special
  // case here.  It should never be turned off or modified, and its
  // duration should always remain the same.
  ticksTotal = 0; // Reset ticks since emulator started.
  timer[0].period     = NullTimerInterval;
  timer[0].timeToFire = ticksTotal + NullTimerInterval;
  timer[0].active     = 1;
  timer[0].continuous = 1;
  timer[0].funct      = nullTimer;
  timer[0].this_ptr   = this;
  currCountdown       = NullTimerInterval;
  currCountdownPeriod = NullTimerInterval;
  numTimers = 1; // So far, only the nullTimer.
  lastTimeUsec = 0;
  usecSinceLast = 0;
}

  void
bx_pc_system_c::init_ips(Bit32u ips)
{
  HRQ = 0;

  enable_a20 = 1;
  //set_INTR (0);

#if BX_CPU_LEVEL < 2
  a20_mask   =    0xfffff;
#elif BX_CPU_LEVEL == 2
  a20_mask   =   0xffffff;
#else /* 386+ */
  a20_mask   = 0xffffffff;
#endif

  // parameter 'ips' is the processor speed in Instructions-Per-Second
  m_ips = double(ips) / 1000000.0L;

  BX_DEBUG(("ips = %u", (unsigned) ips));
}

  void
bx_pc_system_c::set_HRQ(bx_bool val)
{
  HRQ = val;
  if (val)
    BX_CPU(0)->async_event = 1;
}


#if (BX_NUM_SIMULATORS < 2)
  void
bx_pc_system_c::set_INTR(bx_bool value)
{
  if (bx_dbg.interrupts)
    BX_INFO(("pc_system: Setting INTR=%d on bootstrap processor %d", (int)value, BX_BOOTSTRAP_PROCESSOR));
  //INTR = value;
  BX_CPU(BX_BOOTSTRAP_PROCESSOR)->set_INTR(value);
}
#endif

//
// Read from the IO memory address space
//

  Bit32u BX_CPP_AttrRegparmN(2)
bx_pc_system_c::inp(Bit16u addr, unsigned io_len)
{
  Bit32u ret;

  ret = bx_devices.inp(addr, io_len);

  return( ret );
}


//
// Write to the IO memory address space.
//

  void BX_CPP_AttrRegparmN(3)
bx_pc_system_c::outp(Bit16u addr, Bit32u value, unsigned io_len)
{
  bx_devices.outp(addr, value, io_len);
}

  void BX_CPP_AttrRegparmN(1)
bx_pc_system_c::set_enable_a20(Bit8u value)
{
#if BX_CPU_LEVEL < 2
    BX_PANIC(("set_enable_a20() called: 8086 emulation"));
#else

#if BX_SUPPORT_A20
  unsigned old_enable_a20 = enable_a20;

  if (value) {
    enable_a20 = 1;
#if BX_CPU_LEVEL == 2
    a20_mask   = 0xffffff;   /* 286: enable all 24 address lines */
#else /* 386+ */
    a20_mask   = 0xffffffff; /* 386: enable all 32 address lines */
#endif
    }
  else {
    enable_a20 = 0;
    a20_mask   = 0xffefffff;   /* mask off A20 address line */
    }

  BX_DBG_A20_REPORT(value);

  BX_DEBUG(("A20: set() = %u", (unsigned) enable_a20));

  // If there has been a transition, we need to notify the CPUs so
  // they can potentially invalidate certain cache info based on
  // A20-line-applied physical addresses.
  if (old_enable_a20 != enable_a20) {
    for (unsigned i=0; i<BX_SMP_PROCESSORS; i++)
      BX_CPU(i)->pagingA20Changed();
    }
#else
  BX_DEBUG(("set_enable_a20: ignoring: SUPPORT_A20 = 0"));
#endif  // #if BX_SUPPORT_A20

#endif
}

  bx_bool
bx_pc_system_c::get_enable_a20(void)
{
#if BX_SUPPORT_A20
  if (bx_dbg.a20)
    BX_INFO(("A20: get() = %u", (unsigned) enable_a20));

  if (enable_a20) return(1);
  else return(0);
#else
  BX_INFO(("get_enable_a20: ignoring: SUPPORT_A20 = 0"));
  return(1);
#endif  // #if BX_SUPPORT_A20
}

  int
bx_pc_system_c::ResetSignal( PCS_OP operation )
{
  UNUSED( operation );
  // Reset the processor.

  BX_ERROR(( "# bx_pc_system_c::ResetSignal() called" ));
  for (int i=0; i<BX_SMP_PROCESSORS; i++)
    BX_CPU(i)->reset(BX_RESET_SOFTWARE);
  DEV_reset_devices(BX_RESET_SOFTWARE);
  return(0);
}


  Bit8u
bx_pc_system_c::IAC(void)
{
  return( DEV_pic_iac() );
}

  void
bx_pc_system_c::exit(void)
{
  if (DEV_hd_present())
    DEV_hd_close_harddrive();

  BX_INFO(("Last time is %u", (unsigned) DEV_cmos_get_timeval()));

  if (bx_gui) bx_gui->exit();
}


// ================================================
// Bochs internal timer delivery framework features
// ================================================

  int
bx_pc_system_c::register_timer( void *this_ptr, void (*funct)(void *),
  Bit32u useconds, bx_bool continuous, bx_bool active, const char *id)
{
  Bit64u ticks;

  // Convert useconds to number of ticks.
  ticks = (Bit64u) (double(useconds) * m_ips);

  return register_timer_ticks(this_ptr, funct, ticks, continuous, active, id);
}

  int
bx_pc_system_c::register_timer_ticks(void* this_ptr, bx_timer_handler_t funct,
    Bit64u ticks, bx_bool continuous, bx_bool active, const char *id)
{
  unsigned i;

#if BX_TIMER_DEBUG
  if (numTimers >= BX_MAX_TIMERS) {
    BX_PANIC(("register_timer: too many registered timers."));
    }
  if (this_ptr == NULL)
    BX_PANIC(("register_timer_ticks: this_ptr is NULL"));
  if (funct == NULL)
    BX_PANIC(("register_timer_ticks: funct is NULL"));
#endif

  // If the timer frequency is rediculously low, make it more sane.
  // This happens when 'ips' is too low.
  if (ticks < MinAllowableTimerPeriod) {
    //BX_INFO(("register_timer_ticks: adjusting ticks of %llu to min of %u",
    //          ticks, MinAllowableTimerPeriod));
    ticks = MinAllowableTimerPeriod;
    }

  for (i=0; i < numTimers; i++) {
    if (timer[i].inUse == 0)
      break;
    }

  timer[i].inUse      = 1;
  timer[i].period     = ticks;
  timer[i].timeToFire = (ticksTotal + Bit64u(currCountdownPeriod-currCountdown)) +
                        ticks;
  timer[i].active     = active;
  timer[i].continuous = continuous;
  timer[i].funct      = funct;
  timer[i].this_ptr   = this_ptr;
  strncpy(timer[i].id, id, BxMaxTimerIDLen);
  timer[i].id[BxMaxTimerIDLen-1] = 0; // Null terminate if not already.

  if (active) {
    if (ticks < Bit64u(currCountdown)) {
      // This new timer needs to fire before the current countdown.
      // Skew the current countdown and countdown period to be smaller
      // by the delta.
      currCountdownPeriod -= (currCountdown - Bit32u(ticks));
      currCountdown = Bit32u(ticks);
      }
    }

  // If we didn't find a free slot, increment the bound, numTimers.
  if (i==numTimers)
    numTimers++; // One new timer installed.

  // Return timer id.
  return(i);
}


  void
bx_pc_system_c::countdownEvent(void)
{
  unsigned i;
  Bit64u   minTimeToFire;
  bx_bool  triggered[BX_MAX_TIMERS];

  // The countdown decremented to 0.  We need to service all the active
  // timers, and invoke callbacks from those timers which have fired.
#if BX_TIMER_DEBUG
  if (currCountdown != 0)
    BX_PANIC(("countdownEvent: ticks!=0"));
#endif

  // Increment global ticks counter by number of ticks which have
  // elapsed since the last update.
  ticksTotal += Bit64u(currCountdownPeriod);
  minTimeToFire = (Bit64u) -1;

  for (i=0; i < numTimers; i++) {
    triggered[i] = 0; // Reset triggered flag.
    if (timer[i].active) {
#if BX_TIMER_DEBUG
      if (ticksTotal > timer[i].timeToFire)
        BX_PANIC(("countdownEvent: ticksTotal > timeToFire[%u], D " FMT_LL "u", i,
                  timer[i].timeToFire-ticksTotal));
#endif
      if (ticksTotal == timer[i].timeToFire) {
        // This timer is ready to fire.
        triggered[i] = 1;

        if (timer[i].continuous==0) {
          // If triggered timer is one-shot, deactive.
          timer[i].active = 0;
          }
        else {
          // Continuous timer, increment time-to-fire by period.
          timer[i].timeToFire += timer[i].period;
          if (timer[i].timeToFire < minTimeToFire)
            minTimeToFire = timer[i].timeToFire;
          }
        }
      else {
        // This timer is not ready to fire yet.
        if (timer[i].timeToFire < minTimeToFire)
          minTimeToFire = timer[i].timeToFire;
        }
      }
    }

  // Calculate next countdown period.  We need to do this before calling
  // any of the callbacks, as they may call timer features, which need
  // to be advanced to the next countdown cycle.
  currCountdown = currCountdownPeriod =
      Bit32u(minTimeToFire - ticksTotal);

  for (i=0; i < numTimers; i++) {
    // Call requested timer function.  It may request a different
    // timer period or deactivate etc.
    if (triggered[i]) {
      timer[i].funct(timer[i].this_ptr);
      }
    }
}

  void
bx_pc_system_c::nullTimer(void* this_ptr)
{
  // This function is always inserted in timer[0].  It is sort of
  // a heartbeat timer.  It ensures that at least one timer is
  // always active to make the timer logic more simple, and has
  // a duration of less than the maximum 32-bit integer, so that
  // a 32-bit size can be used for the hot countdown timer.  The
  // rest of the timer info can be 64-bits.  This is also a good
  // place for some logic to report actual emulated
  // instructions-per-second (IPS) data when measured relative to
  // the host computer's wall clock.

  UNUSED(this_ptr);

#if SpewPeriodicTimerInfo
  BX_INFO(("==================================="));
  for (unsigned i=0; i < bx_pc_system.numTimers; i++) {
    if (bx_pc_system.timer[i].active) {
      BX_INFO(("BxTimer(%s): period=" FMT_LL "u, continuous=%u",
               bx_pc_system.timer[i].id, bx_pc_system.timer[i].period,
               bx_pc_system.timer[i].continuous));
      }
    }
#endif
}

#if BX_DEBUGGER
  void
bx_pc_system_c::timebp_handler(void* this_ptr)
{
      BX_CPU(0)->break_point = BREAK_POINT_TIME;
      BX_DEBUG(( "Time breakpoint triggered" ));

      if (timebp_queue_size > 1) {
	    Bit64s new_diff = timebp_queue[1] - bx_pc_system.time_ticks();
	    bx_pc_system.activate_timer_ticks(timebp_timer, new_diff, 1);
      }
      timebp_queue_size--;
      for (int i = 0; i < timebp_queue_size; i++)
	    timebp_queue[i] = timebp_queue[i+1];
}
#endif // BX_DEBUGGER

  Bit64u
bx_pc_system_c::time_usec_sequential() {
    Bit64u this_time_usec = time_usec();
    if(this_time_usec != lastTimeUsec) {
      Bit64u diff_usec = this_time_usec-lastTimeUsec;
      lastTimeUsec = this_time_usec;
      if(diff_usec >= usecSinceLast) {
	usecSinceLast = 0;
      } else {
	usecSinceLast -= diff_usec;
      }
    }
    usecSinceLast++;
    return (this_time_usec+usecSinceLast);
}
  Bit64u
bx_pc_system_c::time_usec() {
  return (Bit64u) (((double)(Bit64s)time_ticks()) / m_ips );
}

  void
bx_pc_system_c::start_timers(void)
{
}

  void
bx_pc_system_c::activate_timer_ticks(unsigned i, Bit64u ticks, bx_bool continuous)
{
#if BX_TIMER_DEBUG
  if (i >= numTimers)
    BX_PANIC(("activate_timer_ticks: timer %u OOB", i));
  if (timer[i].period < MinAllowableTimerPeriod)
    BX_PANIC(("activate_timer_ticks: timer[%u].period of " FMT_LL "u < min of %u",
              i, timer[i].period, MinAllowableTimerPeriod));
#endif

  // If the timer frequency is rediculously low, make it more sane.
  // This happens when 'ips' is too low.
  if (ticks < MinAllowableTimerPeriod) {
    //BX_INFO(("activate_timer_ticks: adjusting ticks of %llu to min of %u",
    //          ticks, MinAllowableTimerPeriod));
    ticks = MinAllowableTimerPeriod;
    }

  timer[i].period = ticks;
  timer[i].timeToFire = (ticksTotal + Bit64u(currCountdownPeriod-currCountdown)) +
                        ticks;
  timer[i].active     = 1;
  timer[i].continuous = continuous;

  if (ticks < Bit64u(currCountdown)) {
    // This new timer needs to fire before the current countdown.
    // Skew the current countdown and countdown period to be smaller
    // by the delta.
    currCountdownPeriod -= (currCountdown - Bit32u(ticks));
    currCountdown = Bit32u(ticks);
    }
}

  void
bx_pc_system_c::activate_timer(unsigned i, Bit32u useconds, bx_bool continuous)
{
  Bit64u ticks;

#if BX_TIMER_DEBUG
  if (i >= numTimers)
    BX_PANIC(("activate_timer: timer %u OOB", i));
#endif

  // if useconds = 0, use default stored in period field
  // else set new period from useconds
  if (useconds==0) {
    ticks = timer[i].period;
    }
  else {
    // convert useconds to number of ticks
    ticks = (Bit64u) (double(useconds) * m_ips);

    // If the timer frequency is rediculously low, make it more sane.
    // This happens when 'ips' is too low.
    if (ticks < MinAllowableTimerPeriod) {
      //BX_INFO(("activate_timer: adjusting ticks of %llu to min of %u",
      //          ticks, MinAllowableTimerPeriod));
      ticks = MinAllowableTimerPeriod;
      }

    timer[i].period = ticks;
    }

  activate_timer_ticks(i, ticks, continuous);
}

  void
bx_pc_system_c::deactivate_timer( unsigned i )
{
#if BX_TIMER_DEBUG
  if (i >= numTimers)
    BX_PANIC(("deactivate_timer: timer %u OOB", i));
#endif

  timer[i].active = 0;
}

  unsigned
bx_pc_system_c::unregisterTimer(int timerIndex)
{
  unsigned i = (unsigned) timerIndex;

#if BX_TIMER_DEBUG
  if (i >= numTimers)
    BX_PANIC(("unregisterTimer: timer %u OOB", i));
  if (i == 0)
    BX_PANIC(("unregisterTimer: timer 0 is the nullTimer!"));
  if (timer[i].inUse == 0)
    BX_PANIC(("unregisterTimer: timer %u is not in-use!", i));
#endif

  if (timer[i].active) {
    BX_PANIC(("unregisterTimer: timer '%s' is still active!", timer[i].id));
    return(0); // Fail.
    }

  // Reset timer fields for good measure.
  timer[i].inUse      = 0; // No longer registered.
  timer[i].period     = BX_MAX_BIT64S; // Max value (invalid)
  timer[i].timeToFire = BX_MAX_BIT64S; // Max value (invalid)
  timer[i].continuous = 0;
  timer[i].funct      = NULL;
  timer[i].this_ptr   = NULL;
  memset(timer[i].id, 0, BxMaxTimerIDLen);

  return(1); // OK
}
