code: /**
   * Copyright (c) 2004-2005, Regents of the University of California
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * Redistributions of source code must retain the above copyright notice,
   * this list of conditions and the following disclaimer.
   *
   * Redistributions in binary form must reproduce the above copyright
   * notice, this list of conditions and the following disclaimer in the
   * documentation and/or other materials provided with the distribution.
   *
   * Neither the name of the University of California, Los Angeles nor the
   * names of its contributors may be used to endorse or promote products
   * derived from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  package avrora.sim;
  
  import avrora.sim.util.MulticastInterruptProbe;
  import cck.util.Arithmetic;
  
  /**
   * The <code>InterruptTable</code> class encapsulates the functionality relating to handling
   * the state of the interrupts in the simulation. It allows probes to be inserted on each
   * interrupt (and globally, for each interrupt) that allow the user's code to be called back
   * when the state of interrupts changes.
   *
   * @author Ben L. Titzer
   */
  public class InterruptTable {
  
      protected final int numInterrupts;
      protected long posted1, posted2;
      protected long pending1, pending2;
      protected long enabled1, enabled2;
      protected MulticastInterruptProbe globalProbe;
      protected final MulticastInterruptProbe[] probes;
      protected final Notification[] notify;
      protected final Interpreter interpreter;
      protected final State state;
  
      /**
       * The <code>Notification</code> interface serves a very specific role in simulation;
       * for device implementations to be notified when an interrupt that a device may have
       * posted is executed, or when the user forces an interrupt to be notified. Some interrupts
       * are "auto-clear" which means that they automatically unpost themselves when their
       * handler is invoked. To support this functionality, a <code>Notification</code> is inserted
       * on them that allows the flag register to be updated when the handler is invoked.
       *
       * As a user of the simulation, you SHOULD NOT implement a notification.
       *
       * @see Simulator.InterruptProbe for probing interrupts
       */
      public interface Notification {
          public void force(int inum);
          public void invoke(int inum);
      }
  
      public InterruptTable(Interpreter interp, int numInterrupts) {
          interpreter = interp;
          probes = new MulticastInterruptProbe[numInterrupts];
          notify = new Notification[numInterrupts];
          state = interpreter.getState();
          this.numInterrupts = numInterrupts;
      }
  
      /**
       * The <code>post()</code> method is called by the interpreter when an interrupt is posted.
       * The interrupt table will adjust the posted and pending masks appropriately and notify
       * any probes for the interrupt.
       * @param inum the interrupt number to post
       */
      public void post(int inum) {
          interpreter.innerLoop = false;
          if (inum < 64) {
              posted1 = Arithmetic.setBit(posted1, inum, true);
              pending1 = posted1 & enabled1;
          } else {
              posted2 = Arithmetic.setBit(posted2, inum - 64, true);
              pending2 = posted2 & enabled2;
          }
          MulticastInterruptProbe probe = probes[inum];
          if ( globalProbe != null ) globalProbe.fireWhenPosted(state, inum);
          if ( probe != null ) probe.fireWhenPosted(state, inum);
      }
  
      /**
       * The <code>force()</code> method is called by the interpreter when the user attempts to
       * force an interrupt to become posted. The interrupt table will adjust the posted and pending
       * masks appropriately and notify any probes for the interrupt.
       * @param inum the interrupt number to force
       */
      void force(int inum) {
          post(inum);
          Notification n = notify[inum];
          if ( n != null ) n.force(inum);
      }
  
      /**
       * The <code>unpost()</code> method is called by the interpreter when an interrupt is unposted.
       * The interrupt table will adjust the posted and pending masks appropriately and notify
       * any probes for the interrupt.
       * @param inum the interrupt number to unpost
       */
      public void unpost(int inum) {
          if (inum < 64) {
              posted1 = Arithmetic.setBit(posted1, inum, false);
              pending1 = posted1 & enabled1;
          } else {
              posted2 = Arithmetic.setBit(posted2, inum - 64, false);
              pending2 = posted2 & enabled2;
          }
          MulticastInterruptProbe probe = probes[inum];
          if ( globalProbe != null ) globalProbe.fireWhenUnposted(state, inum);
          if ( probe != null ) probe.fireWhenUnposted(state, inum);
      }
  
      /**
       * The <code>enable()</code> method is called by the interpreter when an interrupt is enabled.
       * The interrupt table will adjust the posted and pending masks appropriately and notify
       * any probes for the interrupt.
       * @param inum the interrupt number to enable
       */
      void enable(int inum) {
          if (inum < 64) {
              enabled1 = Arithmetic.setBit(enabled1, inum, true);
              pending1 = posted1 & enabled1;
          } else {
              enabled2 = Arithmetic.setBit(enabled2, inum - 64, true);
              pending2 = posted2 & enabled2;
          }
          MulticastInterruptProbe probe = probes[inum];
          if ( globalProbe != null ) globalProbe.fireWhenEnabled(state, inum);
          if ( probe != null ) probe.fireWhenEnabled(state, inum);
      }
  
      /**
       * The <code>disable()</code> method is called by the interpreter when an interrupt is disabled.
       * The interrupt table will adjust the posted and pending masks appropriately and notify
       * any probes for the interrupt.
       * @param inum the interrupt number to disable
       */
      public void disable(int inum) {
      //If an ATTiny do nothing
          if (((AtmelInterpreter)interpreter).getFlashByte(0) != 0x0c) return;
           if (inum < 64) {
              enabled1 = Arithmetic.setBit(enabled1, inum, false);
              pending1 = posted1 & enabled1;
          } else {
              enabled2 = Arithmetic.setBit(enabled2, inum - 64, false);
              pending2 = posted2 & enabled2;
          }
      //    System.err.println("inum is " + inum);
          MulticastInterruptProbe probe = probes[inum];
          if ( globalProbe != null ) globalProbe.fireWhenDisabled(state, inum);
          if ( probe != null ) probe.fireWhenDisabled(state, inum);
      }
  
      /**
       * The <code>enableAll()</code> method is called by the interpreter when the all of
       * the interrupts are enabled by setting the global interrupt enable bit.
       */
      public void enableAll() {
          if ( globalProbe != null ) globalProbe.fireWhenEnabled(state, 0);
      }
  
      /**
       * The <code>disableAll()</code> method is called by the interpreter when the all of
       * the interrupts are disabled by clearing the global interrupt enable bit.
       */
      public void disableAll() {
          if ( globalProbe != null ) globalProbe.fireWhenDisabled(state, 0);
      }
  
      /**
       * The <code>beforeInvoke()</code> method is called by the interpreter before it
       * invokes an interrupt handler.
       * @param inum the interrupt number about to be invoked
       */
      public void beforeInvoke(int inum) {
          MulticastInterruptProbe probe = probes[inum];
          if ( globalProbe != null ) globalProbe.fireBeforeInvoke(state, inum);
          if ( probe != null ) probe.fireBeforeInvoke(state, inum);
          Notification n = notify[inum];
          if ( n != null ) n.invoke(inum);
      }
  
      /**
       * The <code>afterInvoke()</code> method is called by the interpreter after it
       * invokes an interrupt handler.
       * @param inum the interrupt number that was just invoked
       */
      public void afterInvoke(int inum) {
          MulticastInterruptProbe probe = probes[inum];
          if ( globalProbe != null ) globalProbe.fireAfterInvoke(state, inum);
          if ( probe != null ) probe.fireAfterInvoke(state, inum);
      }
  
      /**
       * The <code>registerInternalNotification()</code> method is used by devices that
       * require notifications when their interrupt numbers are either forced or invoked.
       * @param n the notification to register for this interrupt
       * @param inum the interrupt number for which to register the notification
       */
      public void registerInternalNotification(Notification n, int inum) {
          notify[inum] = n;
      }
      
      /**
       * Returns the previously registered notification for the specified interrupt.
       */
      public Notification getInternalNotification(int inum) {
          return notify[inum];
      }
  
      /**
       * The <code>getPostedInterrupts()</code> method returns a long integer that represents a bit map
       * of which interrupts are currently posted. For example, bit 1 of this long corresponds to whether
       * interrupt 1 is currently posted.
       * @return a long integer representing a bit map of the posted interrupts
       */
       /*
      public long getPostedInterrupts() {
          return posted;
      }
  */
      /**
       * The <code>getPendingInterrupts()</code> method returns a long integer that represents a bit map
       * of which interrupts are currently pending, meaning they are both posted and enabled. For example, bit 1
       * of this long corresponds to whether
       * interrupt 1 is currently pending.
       * @return a long integer representing a bit map of the pending interrupts
       */
      public long getPendingInterrupts1() {
          return pending1;
      }
      public long getPendingInterrupts2() {
          return pending2;
      }
  
      /**
       * The <code>getEnabledInterrupts()</code> method returns a long integer that represents a bit map
       * of which interrupts are currently enabled, meaning not masked out. For example, bit 1 of this long
       * corresponds to whether
       * interrupt 1 is currently enabled.
       * @return a long integer representing a bit map of the posted interrupts
       */
       /*
      public long getEnabledInterrupts() {
          return enabled;
      }
  */
      /**
       * The <code>isPosted()</code> method checks whether the specified interrupt is currently posted.
       * @param inum the interrupt number to check
       * @return true if the specified interrupt is currently posted; false otherwise
       */
      public boolean isPosted(int inum) {
          if (inum < 64) {
              return Arithmetic.getBit(posted1, inum);
          } else {
              return Arithmetic.getBit(posted2, inum - 64);
          }
      }
  
      /**
       * The <code>isPending()</code> method checks whether the specified interrupt is currently pending.
       * @param inum the interrupt number to check
       * @return true if the specified interrupt is currently pending; false otherwise
       */
      public boolean isPending(int inum) {
          if (inum < 64) {
              return Arithmetic.getBit(pending1, inum);
          } else {
              return Arithmetic.getBit(pending2, inum - 64);
          }
      }
  
      /**
       * The <code>isEnabled()</code> method checks whether the specified interrupt is currently enabled.
       * @param inum the interrupt number to check
       * @return true if the specified interrupt is currently enabled; false otherwise
       */
      public boolean isEnabled(int inum) {
          if (inum < 64) {
              return Arithmetic.getBit(enabled1, inum);
          } else {
              return Arithmetic.getBit(enabled2, inum - 64);
          }
      }
  
      /**
       * The <code>insertProbe()</code> method inserts an interrupt probe on the specified interrupt. When
       * the specified interrupt changes state, i.e. it is either posted, unposted, or invoked, the probe
       * will be notified.
       * @param p the probe to insert on the specified interrupt
       * @param inum the interrupt on which to insert the probe
       */
      public void insertProbe(Simulator.InterruptProbe p, int inum) {
          MulticastInterruptProbe mp = probes[inum];
          if ( mp == null ) probes[inum] = mp = new MulticastInterruptProbe();
          mp.add(p);
      }
  
      /**
       * The <code>removeProbe()</code> method removes a probe from an interrupt.
       * @param p the probe to remove from this interrupt
       * @param inum the interrupt number from which to remove this probe
       */
      public void removeProbe(Simulator.InterruptProbe p, int inum) {
          MulticastInterruptProbe mp = probes[inum];
          if ( mp != null ) mp.remove(p);
      }
  
      /**
       * The <code>insertProbe()</code> method inserts a global probe on all of the interrupts. This probe
       * will be notified when any of the interrupts changes state, and also when the global interrupt flag
       * is enabled or disabled. When the global interrupt flag (master bit) is changed, the special interrupt
       * number <code>0</code> will be passed to the <code>fireWhenEnabled()</code> method of the probe.
       * @param p the probe to insert on all the interrupts
       */
      public void insertProbe(Simulator.InterruptProbe p) {
          if ( globalProbe == null ) globalProbe = new MulticastInterruptProbe();
          globalProbe.add(p);
      }
  
      /**
       * The <code>removeProbe()</code> method removes a global probe from all of the interrupts.
       * @param p the probe to remove from all of the interrupts
       */
      public void removeProbe(Simulator.InterruptProbe p) {
          if ( globalProbe != null ) globalProbe.remove(p);
      }
  
      /**
       * The <code>getNumberOfInterrupts()</code> method returns the number of interrupts in this interrupt table.
       * @return the number of interrupts in this interrupt table
       */
      public int getNumberOfInterrupts() {
          return numInterrupts;
      }
  }
  