/////////////////////////////////////////////////////////////////////////
// $Id: apic.cc,v 1.30 2003/01/22 21:43:34 cbothamy Exp $
/////////////////////////////////////////////////////////////////////////
//
#define NEED_CPU_REG_SHORTCUTS 1
#include "bochs.h"
#if BX_SUPPORT_APIC

#define LOG_THIS this->

bx_generic_apic_c *apic_index[APIC_MAX_ID];


bx_generic_apic_c::bx_generic_apic_c ()
{
  id = APIC_UNKNOWN_ID;
  put("APIC?");
  settype(APICLOG);
  hwreset ();
}

bx_generic_apic_c::~bx_generic_apic_c () 
{
}

void
bx_generic_apic_c::set_arb_id (int new_arb_id)
{
  // politely ignore it.  This gets sent to every APIC, regardless of its
  // type.
}

// init is called during RESET and when an INIT message is delivered.
void bx_generic_apic_c::init ()
{
}

void bx_local_apic_c::update_msr_apicbase(Bit32u newbase)
{
  Bit64u val64;
  val64 = newbase << 12; /* push the APIC base address to bits 12:35 */
  val64 += cpu->msr.apicbase & 0x0900; /* don't modify other apicbase or reserved bits */
  cpu->msr.apicbase = val64;
}

void bx_generic_apic_c::set_base (Bit32u newbase)
{
  BX_INFO(("relocate APIC id=%d to %8x", id, newbase));
  base_addr = newbase;
}

void bx_generic_apic_c::set_id (Bit8u newid) {
  // update apic_index
  if (id != APIC_UNKNOWN_ID) {
    BX_ASSERT (id < APIC_MAX_ID);
    if (apic_index[id] != this)
      BX_PANIC(("inconsistent APIC id table"));
    apic_index[id] = NULL;
  }
  id = newid;
  if (id != APIC_UNKNOWN_ID) {
    if (apic_index[id] != NULL)
      BX_PANIC(("duplicate APIC id assigned"));
    apic_index[id] = this;
  }
}

void bx_generic_apic_c::reset_all_ids () {
  for (int i=0; i<APIC_MAX_ID; i++) {
    if (apic_index[i]) 
      apic_index[i]->set_id (APIC_UNKNOWN_ID);
  }
}

char *
bx_generic_apic_c::get_name () {
  BX_PANIC(("get_name called on bx_generic_apic_c base class"));
  return NULL;
}

bx_bool
bx_generic_apic_c::is_selected (Bit32u addr, Bit32u len)
{
  if ((addr & ~0xfff) == get_base ()) {
    if ((addr & 0xf != 0) || (len != 4))
      BX_INFO(("warning: misaligned or wrong-size APIC write. addr=%08x, len=%d", addr, len));
    return true;
  }
  return false;
}

void
bx_generic_apic_c::read (Bit32u addr, void *data, unsigned len)
{
  if ((addr & ~0xf) != ((addr+len-1) & ~0xf))
    BX_PANIC(("APIC read spans 32-bit boundary"));
  Bit32u value;
  read_aligned (addr, &value, 4);
  if ((addr&3) == 0) {
    *((Bit32u *)data) = value;
    return;
  }
  // handle partial word read, independent of endian-ness.
  Bit8u bytes[4];
  bytes[0] = value & 0xff;
  bytes[1] = (value >> 8) & 0xff;
  bytes[2] = (value >> 16) & 0xff;
  bytes[3] = (value >> 24) & 0xff;
  Bit8u *p1 = bytes+(addr&3);
  Bit8u *p2 = (Bit8u *)data;
  for (unsigned i=0; i<len; i++) {
    if (bx_dbg.apic)
      BX_INFO(("apic: Copying byte %02x", (unsigned int) *p1));
    *p2++ = *p1++;
  }
}

void bx_generic_apic_c::read_aligned (Bit32u address, Bit32u *data, unsigned len)
{
  BX_PANIC(("read_aligned not implemented in base class bx_generic_apic_c"));
}

void bx_generic_apic_c::write(Bit32u address, Bit32u *value, unsigned len)
{
  BX_PANIC(("write not implemented in base class bx_generic_apic_c"));
}

void bx_generic_apic_c::startup_msg (Bit32u vector)
{
  BX_PANIC(("startup message sent to an I/O APIC"));
}

void bx_generic_apic_c::trigger_irq (unsigned num, unsigned from)
{
  BX_PANIC(("trigger_irq called on base class"));
}

void bx_generic_apic_c::untrigger_irq (unsigned num, unsigned from)
{
  BX_PANIC(("untrigger_irq called on base class"));
}

bx_bool bx_generic_apic_c::match_logical_addr (Bit8u address) {
  BX_PANIC(("match_logical_addr called on base class"));
  return false;
}

bx_apic_type_t bx_generic_apic_c::get_type () {
  BX_PANIC(("get_type called on base class"));
  return APIC_TYPE_NONE;
}

Bit32u
bx_generic_apic_c::get_delivery_bitmask (Bit8u dest, Bit8u dest_mode)
{
  int mask = 0;
  if (dest_mode == 0) {
    // physical 
    if (dest < APIC_MAX_ID)
      mask = 1<<dest;
    else if (dest == 0xff) {
      // physical destination 0xff means everybody. only local APICs can
      // send this.
      BX_ASSERT (get_type () == APIC_TYPE_LOCAL_APIC);
      mask = 0xff; 
    } else BX_PANIC(("bx_generic_apic_c::deliver: illegal physical destination %02x", dest));
  } else {
    // logical destination. call match_logical_addr for each APIC.
    if (dest == 0) return 0;
    for (int i=0; i<APIC_MAX_ID; i++) {
      if (apic_index[i] && apic_index[i]->match_logical_addr(dest))
        mask |= (1<<i);
    }
  }
  if (bx_dbg.apic)
    BX_INFO(("generic::get_delivery_bitmask returning 0x%04x", mask));
  return mask;
}

bx_bool
bx_generic_apic_c::deliver (Bit8u dest, Bit8u dest_mode, Bit8u delivery_mode, Bit8u vector, Bit8u polarity, Bit8u trig_mode)
{
  int bit;
  // return false if we can't deliver for any reason, so that the caller
  // knows not to clear its IRR.
  Bit32u deliver_bitmask = get_delivery_bitmask (dest, dest_mode);
  // mask must include ONLY local APICs, or we will have problems.
  if (!deliver_bitmask) {
    BX_PANIC(("deliver failed for vector %02x: no APICs in destination bitmask", vector));
    return false;
  }
  switch (delivery_mode) {
    case 0:  // fixed
      break;
    case 1:  // lowest priority of destinations
      {
        // find lowest priority of apics in the mask
        int lowest_priority = 0x100, lowest_mask = -1;
        for (int bit=0; bit<APIC_MAX_ID; bit++) {
          if (deliver_bitmask & (1<<bit)) {
            bx_local_apic_c *apic = (bx_local_apic_c *)apic_index[bit];
            if (apic->get_ppr () < lowest_priority) {
              lowest_priority = apic->get_ppr (); lowest_mask = 1<<bit;
            }
          }
        }
        deliver_bitmask = lowest_mask;
        BX_ASSERT (deliver_bitmask >= 0);
      }
      break;
    case 5:  // INIT
      {
        // NOTE: special behavior of local apics is handled in
        // bx_local_apic_c::deliver.
        
        // normal INIT. initialize the local apics in the delivery mask.
        for (int bit=0; bit<APIC_MAX_ID; bit++) {
          if (deliver_bitmask & (1<<bit)) 
            apic_index[bit]->init ();
        }
      }
      return true;
    case 6:  // Start Up (local apic only)
      BX_ASSERT (get_type () == APIC_TYPE_LOCAL_APIC);
      for (bit=0; bit<APIC_MAX_ID; bit++)
        if (deliver_bitmask & (1<<bit))
          apic_index[bit]->startup_msg (vector);
      return true;
    case 7:   // ExtINT we can deliver this as fixed. In theory this requires
	      // extra work from a connected PIC such as an 8259A but we can
	      // skip all of that since all our pins are wired to the IOAPIC
      break;
    case 2:  // SMI
    case 3:  // reserved
    case 4:  // NMI
    default:
      BX_PANIC(("APIC delivery mode %d not implemented", delivery_mode));
  }
  // Fixed delivery mode
  if (bx_dbg.apic)
    BX_INFO(("delivering vector=0x%02x to bitmask=%04x", (int)vector, deliver_bitmask));
  for (bit=0; bit<APIC_MAX_ID; bit++) {
    if (deliver_bitmask & (1<<bit)) {
      if (apic_index[bit] == NULL)
        BX_INFO(("IOAPIC: delivering int0x%x to nonexistent id=%d!", (unsigned)vector, bit));
      else {
        if (bx_dbg.apic)
          BX_INFO(("IOAPIC: delivering int0x%x to apic#%d", (unsigned)vector, bit));
        apic_index[bit]->trigger_irq (vector, id);
      }
    }
  }
  return true;
}

bx_bool
bx_local_apic_c::deliver (Bit8u dest, Bit8u dest_mode, Bit8u delivery_mode, Bit8u vector, Bit8u polarity, Bit8u trig_mode)
{
  // In this function, implement only the behavior that is specific to
  // the local apic.  For general behavior of all apics, just send it to
  // the base class.
  if (delivery_mode == 5)
  {
    int trig_mode = (icr_low >> 15) & 1;
    int level = (icr_low >> 14) & 1;
    if (level == 0 && trig_mode == 1) {
      // special mode in local apic.  See "INIT Level Deassert" in the
      // Intel Soft. Devel. Guide Vol 3, page 7-34.  This magic code
      // causes all APICs (regardless of dest address) to set their
      // arbitration ID to their APIC ID.
      BX_INFO (("INIT with Level&Deassert: synchronize arbitration IDs"));
      for (int bit=0; bit<APIC_MAX_ID; bit++) {
        if (apic_index[bit])
          apic_index[bit]->set_arb_id (apic_index[bit]->get_id ());
      }
      return true;
    }
  }
  // not any special case behavior, just use generic apic code.
  return bx_generic_apic_c::deliver (dest, dest_mode, delivery_mode, vector, polarity, trig_mode);
}

bx_local_apic_c::bx_local_apic_c(BX_CPU_C *mycpu)
  : bx_generic_apic_c ()
{
  cpu = mycpu;
  hwreset ();
  INTR = 0;
}

void
bx_local_apic_c::set_arb_id (int new_arb_id)
{
  BX_DEBUG (("set arbitration ID to %d", new_arb_id));
  arb_id = new_arb_id;
}

void
bx_local_apic_c::hwreset () 
{
  /* same as INIT but also sets arbitration ID and APIC ID */
  init ();
  /* since id is set explicitly by the function that creates the CPU
     object, do not mess around with it */
  // id = APIC_UNKNOWN_ID;
  arb_id = id;
}

void
bx_local_apic_c::init ()
{
  bx_generic_apic_c::init ();

  BX_INFO(("local apic in %s initializing", 
      (cpu && cpu->name) ? cpu->name : "?"));
  // default address for a local APIC, can be moved
  base_addr = APIC_BASE_ADDR;
  update_msr_apicbase(base_addr);
  err_status = 0;
  log_dest = 0;
  dest_format = 0xf;
  for (int bit=0; bit<BX_LOCAL_APIC_MAX_INTS; bit++) {
    irr[bit] = isr[bit] = tmr[bit] = 0;
  }
  icr_high = icr_low = log_dest = task_priority = 0;
  spurious_vec = 0xff;   // software disabled (bit 8)

  // KPL
  // Register a non-active timer for use when the timer is started.
  timer_handle = bx_pc_system.register_timer_ticks(this,
            BX_CPU(0)->local_apic.periodic_smf, 0, 0, 0, "lapic");
}

BX_CPU_C 
*bx_local_apic_c::get_cpu (Bit8u id)
{
  BX_ASSERT (id < APIC_MAX_ID);
  return cpu;
}

bx_local_apic_c::~bx_local_apic_c(void)
{
  // nothing for now
}

void bx_local_apic_c::set_id (Bit8u newid) {
  bx_generic_apic_c::set_id (newid);
  sprintf (cpu->name, "CPU apicid=%02x", (Bit32u)id);
  if (id <= 15) {
    char buffer[16];
    sprintf (buffer, "APIC%x", id);
    put(buffer);
    settype(CPU0LOG + id);
    sprintf (buffer, "CPU%x", id);
    cpu->put (buffer);
  } else {
    BX_INFO (("naming convention for apics requires id=0-15 only"));
  }
  if(BX_CPU_LEVEL<2)
    BX_INFO(( "8086" ));
  else
    BX_INFO(( "80%d86", BX_CPU_LEVEL ));
}

char *
bx_local_apic_c::get_name()
{
  return cpu->name;
}

void bx_local_apic_c::set_divide_configuration (Bit32u value) {
  BX_ASSERT (value == (value & 0x0b));
  // move bit 3 down to bit 0.
  value = ((value & 8) >> 1) | (value & 3);
  BX_ASSERT (value >= 0 && value <= 7);
  timer_divide_factor = (value==7)? 1 : (2 << value);
  BX_DEBUG(("%s: set timer divide factor to %d", cpu->name, timer_divide_factor));
}

void bx_local_apic_c::write (Bit32u addr, Bit32u *data, unsigned len)
{
  if (len != 4) {
    BX_PANIC (("local apic write with len=%d (should be 4)", len));
  }
  BX_DEBUG(("%s: write %08x to APIC address %08x", cpu->name, *data, addr));
  addr &= 0xff0;
  switch (addr) {
    case 0x20: // local APIC id
      id = ((*data)>>24) & 0xf;
      break;
    case 0x80: // task priority
      task_priority = *data & 0xff;
      break;
    case 0xb0: // EOI
      {
        BX_DEBUG(("%s: Wrote 0x%04x to EOI", cpu->name, *data));
        int vec = highest_priority_int (isr);
        if (vec < 0) {
          BX_INFO(("EOI written without any bit in ISR"));
        } else {
          BX_DEBUG(("%s: local apic received EOI, hopefully for vector 0x%02x", cpu->name, vec));
          isr[vec] = 0; 
          service_local_apic ();
        }
        if (bx_dbg.apic)
          print_status ();
      }
      break;
    case 0xd0: // logical destination
      log_dest = (*data >> 24) & 0xff;
      BX_DEBUG (("set logical destiation to %02x", log_dest));
      break;
    case 0xe0: // destination format
      dest_format = (*data >> 28) & 0xf;
      BX_DEBUG (("set destination format to %02x", dest_format));
      break;
    case 0xf0: // spurious interrupt vector
      spurious_vec = (spurious_vec & 0x0f) | (*data & 0x3f0);
      break;
    case 0x280: // error status reg
      // Here's what the IA-devguide-3 says on p.7-45:
      // The ESR is a read/write register and is reset after being written to
      // by the processor. A write to the ESR must be done just prior to
      // reading the ESR to allow the register to be updated.
      // This doesn't seem clear.  If the write clears the register, then
      // wouldn't you always read zero?  Otherwise, what does the write do?
      err_status = 0;
      break;
    case 0x300: // interrupt command reg 0-31
      {
        icr_low = *data & ~(1<<12);  // force delivery status bit = 0 (idle)
        int dest = (icr_high >> 24) & 0xff;
        int trig_mode = (icr_low >> 15) & 1;
        int level = (icr_low >> 14) & 1;
        int dest_mode = (icr_low >> 11) & 1;
        int delivery_mode = (icr_low >> 8) & 7;
        int vector = (icr_low & 0xff);
        //
        // deliver will call get_delivery_bitmask to decide who to send to.
        // This local_apic class redefines get_delivery_bitmask to 
        // implement the destination shorthand field, which doesn't exist
        // for all APICs.
        bx_bool accepted = 
           deliver (dest, dest_mode, delivery_mode, vector, level, trig_mode);
        if (!accepted)
          err_status |= APIC_ERR_TX_ACCEPT_ERR;
      }
      break;
    case 0x310: // interrupt command reg 31-63
      icr_high = *data & 0xff000000;
      break;
    case 0x320: // LVT Timer Reg
      lvt[APIC_LVT_TIMER] = *data & 0x310ff;
      break;
    case 0x330: // LVT Thermal Monitor
      lvt[APIC_LVT_THERMAL] = *data & 0x117ff;
      break;
    case 0x340: // LVT Performance Counter
      lvt[APIC_LVT_PERFORM] = *data & 0x117ff;
      break;
    case 0x350: // LVT LINT0 Reg
      lvt[APIC_LVT_LINT0] = *data & 0x1f7ff;
      break;
    case 0x360: // LVT Lint1 Reg
      lvt[APIC_LVT_LINT1] = *data & 0x1f7ff;
      break;
    case 0x370: // LVT Error Reg
      lvt[APIC_LVT_ERROR] = *data & 0x117ff;
      break;
    case 0x380: // initial count for timer
      {
      // If active before, deactive the current timer before changing it.
      if (timer_active)
        bx_pc_system.deactivate_timer(timer_handle);
      timer_initial = *data;
      if (timer_initial == 0)
        BX_PANIC(("APIC: W(init timer count): count=0"));
      // This should trigger the counter to start.  If already started,
      // restart from the new start value.
//fprintf(stderr, "APIC: W(Initial Count Register) = %u\n", *data);
      timer_current = timer_initial;
      timer_active = true;
//timer_divide_counter = 0; // KPL: delete this field.
      Bit32u timervec = lvt[APIC_LVT_TIMER];
      bx_bool continuous = (timervec & 0x20000) > 0;
      ticksInitial = bx_pc_system.getTicksTotal(); // Take a reading.
      bx_pc_system.activate_timer_ticks(timer_handle,
          Bit64u(timer_initial) * Bit64u(timer_divide_factor), continuous);
      }
      break;
    case 0x3e0: // timer divide configuration
      // only bits 3, 1, and 0 are writable
      timer_divconf = *data & 0xb;
      set_divide_configuration (timer_divconf);
      break;
    /* all read-only registers go here */
    case 0x30: // local APIC version
    case 0x90: // arbitration priority
    case 0xa0: // processor priority
    // ISRs not writable
    case 0x100: case 0x110: case 0x120: case 0x130:
    case 0x140: case 0x150: case 0x160: case 0x170:
    // TMRs not writable
    case 0x180: case 0x190: case 0x1a0: case 0x1b0:
    case 0x1c0: case 0x1d0: case 0x1e0: case 0x1f0:
    // IRRs not writable
    case 0x200: case 0x210: case 0x220: case 0x230:
    case 0x240: case 0x250: case 0x260: case 0x270:
      // current count for timer
    case 0x390:
      // all read-only registers should fall into this line
      BX_INFO(("warning: write to read-only APIC register 0x%02x", addr));
      break;
    default:
      err_status |= APIC_ERR_ILLEGAL_ADDR;
      // but for now I want to know about it in case I missed some.
      BX_PANIC(("APIC register %08x not implemented", addr));
  }
}

void bx_local_apic_c::startup_msg (Bit32u vector)
{
  if (cpu->debug_trap & 0x80000000) {
    cpu->debug_trap &= ~0x80000000;
    cpu->dword.eip = 0;
    cpu->load_seg_reg (&cpu->sregs[BX_SEG_REG_CS], vector*0x100);
    BX_INFO(("%s started up at 0x%x by APIC", cpu->name, cpu->dword.eip));
  } else {
    BX_INFO(("%s started up by APIC, but was not halted at the time", cpu->name));
  }
}

void bx_local_apic_c::read_aligned (Bit32u addr, Bit32u *data, unsigned len)
{
  if (len != 4) {
    BX_PANIC (("local apic write with len=%d (should be 4)", len));
  }
  *data = 0;  // default value for unimplemented registers
  Bit32u addr2 = addr & 0xff0;
  switch (addr2) {
  case 0x20: // local APIC id
    *data = (id) << 24; break;
  case 0x30: // local APIC version
    *data = 0x00170011; break;
  case 0x80: // task priority
    *data = task_priority & 0xff; break;
  case 0x90: // arbitration priority
    *data = get_apr (); break;
  case 0xa0: // processor priority
    *data = get_ppr (); break;
  case 0xb0: // EOI
    /*
     * Read-modify-write operations should operate without generating
     * exceptions, and are used by some operating systems to EOI.
     * The results of reads should be ignored by the OS.
     */
    break;
  case 0xd0: // logical destination
    *data = (log_dest & 0xff) << 24; break;
  case 0xe0: // destination format
    *data = ((dest_format & 0xf) << 24) | 0x0fffffff; break;
  case 0xf0: // spurious interrupt vector
    *data = spurious_vec; break;
  // ISRs not writable
  case 0x100: case 0x110: case 0x120: case 0x130:
  case 0x140: case 0x150: case 0x160: case 0x170:
  case 0x180: case 0x190: case 0x1a0: case 0x1b0:
  case 0x1c0: case 0x1d0: case 0x1e0: case 0x1f0:
  case 0x200: case 0x210: case 0x220: case 0x230:
  case 0x240: case 0x250: case 0x260: case 0x270:
    *data = 0;
    BX_INFO(("reading ISR,TMR,IRR not implemented"));
    break;
  case 0x280: // error status reg
    *data = err_status; break;
  case 0x300: // interrupt command reg 0-31
    *data = icr_low; break;
  case 0x310: // interrupt command reg 31-63
    *data = icr_high; break;
  case 0x320: // LVT Timer Reg
  case 0x330: // LVT Thermal Monitor
  case 0x340: // LVT Performance Counter
  case 0x350: // LVT LINT0 Reg
  case 0x360: // LVT Lint1 Reg
  case 0x370: // LVT Error Reg
    {
      int index = (addr2 - 0x320) >> 4;
      *data = lvt[index];
      break;
    }
  case 0x380: // initial count for timer
    *data = timer_initial;
//fprintf(stderr, "APIC: R(Initial Count Register) = %u\n", *data);
    break;
  case 0x390: // current count for timer
    {
    if (timer_active==0)
      *data = timer_current;
    else {
      Bit64u delta64;
      Bit32u delta32;
      delta64 = (bx_pc_system.time_ticks() - ticksInitial) / timer_divide_factor;
      delta32 = (Bit32u) delta64;
      if (delta32 > timer_initial)
        BX_PANIC(("APIC: R(curr timer count): delta < initial"));
      timer_current = timer_initial - delta32;
      *data = timer_current;
      }
//fprintf(stderr, "APIC: R(Current Count Register) = %u\n", *data);
    }
    break;
  case 0x3e0: // timer divide configuration
    *data = timer_divconf; break;
  default:
    BX_INFO(("APIC register %08x not implemented", addr));
  }
  if (bx_dbg.apic)
    BX_INFO(("%s: read from APIC address %08x = %08x", cpu->name, addr, *data));
}

int 
bx_local_apic_c::highest_priority_int (Bit8u *array)
{
  for (int i=0; i<BX_LOCAL_APIC_MAX_INTS; i++)
    if (array[i]) return i;
  return -1;
}

void bx_local_apic_c::service_local_apic ()
{
  if (bx_dbg.apic) {
    BX_INFO(("service_local_apic()"));
    print_status ();
  }
  if (INTR) return;  // INTR already up; do nothing
  // find first interrupt in irr.
  int first_irr = highest_priority_int (irr);
  int first_isr = highest_priority_int (isr);
  if (first_irr < 0) return;   // no interrupts, leave INTR=0
  if (first_isr >= 0 && first_irr >= first_isr) {
    if (bx_dbg.apic)
      BX_INFO(("local apic (%s): not delivering int%02x because int%02x is in service", cpu->name, first_irr, first_isr));
    return;
  }
  // interrupt has appeared in irr.  raise INTR.  When the CPU
  // acknowledges, we will run highest_priority_int again and
  // return it.
  BX_DEBUG(("service_local_apic(): setting INTR=1 for vector 0x%02x", first_irr));
  INTR = 1;
  cpu->async_event = 1;
}

void bx_local_apic_c::trigger_irq (unsigned vector, unsigned from)
{
  BX_DEBUG(("Local apic on %s: trigger interrupt vector=0x%x", cpu->name, vector));
  irr[vector] = 1;
  service_local_apic ();
}

void bx_local_apic_c::untrigger_irq (unsigned vector, unsigned from)
{
  BX_DEBUG(("Local apic on %s: untrigger interrupt vector=0x%x", cpu->name, vector));
  // hardware says "no more".  clear the bit.  If the CPU hasn't yet
  // acknowledged the interrupt, it will never be serviced.
  BX_ASSERT (irr[vector] == 1);
  irr[vector] = 0;
  if (bx_dbg.apic) print_status ();
}

Bit8u
bx_local_apic_c::acknowledge_int ()
{
  // CPU calls this when it is ready to service one interrupt
  if (!INTR)
    BX_PANIC(("%s: acknowledged an interrupt, but INTR=0", cpu->name));
  BX_ASSERT (INTR);
  int vector = highest_priority_int (irr);
  BX_ASSERT (irr[vector] == 1);
  BX_DEBUG(("%s: acknowledge_int returning vector 0x%x", cpu->name, vector));
  // currently isr never gets cleared, so no point
  //BX_ASSERT (isr[vector] == 0);
  irr[vector] = 0;
  isr[vector] = 1;
  if (bx_dbg.apic) {
    BX_INFO(("Status after setting isr:"));
    print_status ();
  }
  INTR = 0;
  cpu->async_event = 1;
  service_local_apic ();  // will set INTR again if another is ready
  return vector;
}

void bx_local_apic_c::print_status () {
  BX_INFO(("%s local apic: status is {:", cpu->name));
  for (int vec=0; vec<BX_LOCAL_APIC_MAX_INTS; vec++) {
    if (irr[vec] || isr[vec]) {
      BX_INFO(("vec 0x%x: irr=%d, isr=%d", vec, (int)irr[vec], (int)isr[vec]));
    }
  }
  BX_INFO(("}"));
}

bx_bool bx_local_apic_c::match_logical_addr (Bit8u address) 
{
  if (dest_format != 0xf) {
    BX_PANIC(("bx_local_apic_c::match_logical_addr: cluster model addressing not implemented"));
  }
  // if all address bits are 1, send to all local APICs. SDG3:7-27.
  if (address == 0xff) {
    BX_DEBUG (("%s: MDA=0xff matches everybody", cpu->name));
    return true;
  }
  bx_bool match = ((address & log_dest) != 0);
  BX_DEBUG (("%s: comparing MDA %02x to my LDR %02x -> %s", cpu->name,
    address, log_dest, match? "Match" : "Not a match"));
  return match;
}

Bit32u 
bx_local_apic_c::get_delivery_bitmask (Bit8u dest, Bit8u dest_mode)
{
  int dest_shorthand = (icr_low >> 18) & 3;
  Bit32u all_mask = (1<<APIC_MAX_ID) - 1;
  Bit32u mask;
  switch (dest_shorthand) {
  case 0:  // no shorthand, use real destination value
    return bx_generic_apic_c::get_delivery_bitmask (dest, dest_mode);
  case 1:  // self
    return (1<<id);
  case 2:  // all including self
    mask = all_mask;
    break;
  case 3:  // all but self
    mask = all_mask & ~(1<<id);
    break;
  }
  // prune nonexistents and I/O apics from list
  for (int bit=0; bit<APIC_MAX_ID; bit++) {
    if (!apic_index[bit] 
        || (apic_index[bit]->get_type () != APIC_TYPE_LOCAL_APIC))
      mask &= ~(1<<bit);
  }
  BX_DEBUG (("local::get_delivery_bitmask returning 0x%04x", mask));
  return mask;
}

Bit8u bx_local_apic_c::get_ppr ()
{
  static int warned = 0;
  if (warned < 10) {
    BX_ERROR(("WARNING: Local APIC Processor Priority not implemented, returning 0"));
    warned++;
  }
  // should look at TPR, vector of highest priority isr, etc.
  return 0;
}


Bit8u bx_local_apic_c::get_apr ()
{
  return arb_id;
}

  void
bx_local_apic_c::periodic_smf(void *this_ptr)
{
  bx_local_apic_c *class_ptr = (bx_local_apic_c *) this_ptr;

  class_ptr->periodic();
}

  void
bx_local_apic_c::periodic(void) // KPL: changed prototype
{
  if (!timer_active) {
    BX_PANIC(("%s: bx_local_apic_c::periodic called, timer_active==0", cpu->name));
    return;
    }
  BX_DEBUG(("%s: bx_local_apic_c::periodic called", cpu->name));

  // timer reached zero since the last call to periodic.
  Bit32u timervec = lvt[APIC_LVT_TIMER];
  if (timervec & 0x20000) {
    // Periodic mode.
    // If timer is not masked, trigger interrupt.
    if ( (timervec & 0x10000)==0 )
      trigger_irq (timervec & 0xff, id);
    // Reload timer values.
    timer_current = timer_initial;
    ticksInitial = bx_pc_system.getTicksTotal(); // Take a reading.
    BX_DEBUG(("%s: local apic timer (periodic) triggered int, reset counter to 0x%08x", cpu->name, timer_current));
    }
  else {
    // one-shot mode
    timer_current = 0;
    // If timer is not masked, trigger interrupt.
    if ( (timervec & 0x10000)==0 )
      trigger_irq (timervec & 0xff, id);
    timer_active = false;
    BX_DEBUG (("%s: local apic timer (one-shot) triggered int", cpu->name));
    bx_pc_system.deactivate_timer(timer_handle); // Make sure.
    }
}
#endif /* if BX_SUPPORT_APIC */
