2.6. Internal timers

2.6.1. Overview

The Bochs internal timers are required to provide timer features in the device emulation and for the interaction between simulator and gui. They are implemented in the bx_pc_system_c class and driven by the cpu. When programming a timer the interval is specified in useconds and the timer code translates the value to cpu ticks using the IPS value. In the original implementation the cpu object calls a timer method to increment the system time by one tick after completing one instruction. If a timer has expired, the related timer handler function is called. Now it is also possible to execute a number of cpu instructions, finally update the timer subsystem with this number and possibly call several timer handlers. Here are some examples for timers in the devices and gui code:

These are the capabilities of the Bochs internal timers:

2.6.2. Timer definitions, members and methods

Here are the timer-related definitions and members in pc_system.h:

#define BX_MAX_TIMERS 64
#define BX_NULL_TIMER_HANDLE 10000

typedef void (*bx_timer_handler_t)(void *);

  struct {
    bx_bool inUse;      // Timer slot is in-use (currently registered).
    Bit64u  period;     // Timer periodocity in cpu ticks.
    Bit64u  timeToFire; // Time to fire next (in absolute ticks).
    bx_bool active;     // 0=inactive, 1=active.
    bx_bool continuous; // 0=one-shot timer, 1=continuous periodicity.
    bx_timer_handler_t funct;  // A callback function for when the
                               //   timer fires.
    void *this_ptr;            // The this-> pointer for C++ callbacks
                               //   has to be stored as well.
#define BxMaxTimerIDLen 32
    char id[BxMaxTimerIDLen];  // String ID of timer.
    Bit32u param;              // Device-specific value assigned to timer (optional)
  } timer[BX_MAX_TIMERS];

  unsigned   numTimers;  // Number of currently allocated timers.
  unsigned   triggeredTimer;  // ID of the actually triggered timer.
  Bit32u     currCountdown; // Current countdown ticks value (decrements to 0).
  Bit32u     currCountdownPeriod; // Length of current countdown period.
  Bit64u     ticksTotal; // Num ticks total since start of emulator execution.
  Bit64u     lastTimeUsec; // Last sequentially read time in usec.
  Bit64u     usecSinceLast; // Number of useconds claimed since then.

  // A special null timer is always inserted in the timer[0] slot.  This
  // make sure that at least one timer is always active, and that the
  // duration is always less than a maximum 32-bit integer, so a 32-bit
  // counter can be used for the current countdown.
  static const Bit64u NullTimerInterval;
  static void nullTimer(void* this_ptr);

These are the public timer-related methods for timer control, driving the timers with the cpu and retrieving the internal time implemented in the bx_pc_system_c class:

  void   initialize(Bit32u ips);
  int    register_timer(void *this_ptr, bx_timer_handler_t, Bit32u useconds,
                         bx_bool continuous, bx_bool active, const char *id);
  bx_bool unregisterTimer(unsigned timerID);
  void   setTimerParam(unsigned timerID, Bit32u param);
  void   start_timers(void);
  void   activate_timer(unsigned timer_index, Bit32u useconds, bx_bool continuous);
  void   deactivate_timer(unsigned timer_index);
  unsigned triggeredTimerID(void) {
    return triggeredTimer;
  }
  Bit32u triggeredTimerParam(void) {
    return timer[triggeredTimer].param;
  }
  static BX_CPP_INLINE void tick1(void) {
    if (--bx_pc_system.currCountdown == 0) {
      bx_pc_system.countdownEvent();
    }
  }
  static BX_CPP_INLINE void tickn(Bit32u n) {
    while (n >= bx_pc_system.currCountdown) {
      n -= bx_pc_system.currCountdown;
      bx_pc_system.currCountdown = 0;
      bx_pc_system.countdownEvent();
      // bx_pc_system.currCountdown is adjusted to new value by countdownevent().
    }
    // 'n' is not (or no longer) >= the countdown size.  We can just decrement
    // the remaining requested ticks and continue.
    bx_pc_system.currCountdown -= n;
  }

  int register_timer_ticks(void* this_ptr, bx_timer_handler_t, Bit64u ticks,
                           bx_bool continuous, bx_bool active, const char *id);
  void activate_timer_ticks(unsigned index, Bit64u instructions,
                            bx_bool continuous);
  Bit64u time_usec();
  Bit64u time_usec_sequential();
  static BX_CPP_INLINE Bit64u time_ticks() {
    return bx_pc_system.ticksTotal +
      Bit64u(bx_pc_system.currCountdownPeriod - bx_pc_system.currCountdown);
  }

  static BX_CPP_INLINE Bit32u  getNumCpuTicksLeftNextEvent(void) {
    return bx_pc_system.currCountdown;
  }

This private method is called when the function handling the clock ticks finds that an event has occurred:

  void   countdownEvent(void);

2.6.3. Detailed functional description

The Bochs timer implementation requires at least one timer to be active. That's why there is a so-called nullTimer to make it work. It is initialized in the constructor on the first timer slot with the highest possible timer interval and it's handler is an empty function.

The most important variables of the timer subsystem are initialized on startup with the nullTimer values and updated after each timer modification (register / unregister / activate / deactivate / processing handler).

The number if ticks since emulator startup is calculated with the formula ticksTotal + currCountdownPeriod - currCountdown and returned with the time_ticks() method. The number of useconds since emulator startup is returned with the time_usec() method computed from the return value of time_ticks() and the IPS value.

To be continued