
/*---------------------------------------------------------------------------
 * Copyright (C) 1999,2000 Dallas Semiconductor Corporation, All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Dallas Semiconductor
 * shall not be used except as stated in the Dallas Semiconductor
 * Branding Policy.
 *---------------------------------------------------------------------------
 */

package com.dalsemi.onewire.container;

// imports
import com.dalsemi.onewire.OneWireException;
import com.dalsemi.onewire.adapter.DSPortAdapter;
import com.dalsemi.onewire.adapter.OneWireIOException;
import com.dalsemi.onewire.utils.Bit;
import java.util.Vector;
import java.util.Enumeration;


/**
 * iButton container for iButton family type 04 (hex), DS1994/DS2404.
 *
 *  @version    0.00, 28 Aug 2000
 *  @author     DS
 */
public class OneWireContainer04
   extends OneWireContainer
   implements ClockContainer
{

   //--------
   //-------- Finals
   //--------

   /** Offset of BITMAP in array returned from read registers */
   public static final int BITMAP_OFFSET = 32;

   /** Offset of status register from read registers */
   public static final int STATUS_OFFSET = 0;

   /** Offset of control register from read registers */
   public static final int CONTROL_OFFSET = 1;

   /** Offset of real-time-clock in array returned from read registers */
   public static final int RTC_OFFSET = 2;

   /** Offset of inverval-counter in array returned from read registers */
   public static final int INTERVAL_OFFSET = 7;

   /** Offset of counter in array returned from read registers */
   public static final int COUNTER_OFFSET = 12;

   /** Offset of real-time-clock-alarm in array returned from read registers */
   public static final int RTC_ALARM_OFFSET = 16;

   /** Offset of inverval-counter-alarm in array returned from read registers */
   public static final int INTERVAL_ALARM_OFFSET = 21;

   /** Offset of counter-alarm in array returned from read registers */
   public static final int COUNTER_ALARM_OFFSET = 26;

   //--------
   //-------- Variables
   //--------

   /**
    * Scratchpad access memory bank
    */
   private MemoryBankScratch scratch;

   /**
    * Clock memory access memory bank
    */
   private MemoryBankNV clock;

   //--------
   //-------- Constructors
   //--------

   /**
    * Default constructor
    */
   public OneWireContainer04 ()
   {
      super();

      // initialize the clock memory bank
      initClock();
   }

   /**
    * Create a container with a provided adapter object
    * and the address of the iButton or 1-Wire device.
    *
    * @param  sourceAdapter     adapter object required to communicate with
    * this iButton.
    * @param  newAddress        address of this 1-Wire device
    */
   public OneWireContainer04 (DSPortAdapter sourceAdapter, byte[] newAddress)
   {
      super(sourceAdapter, newAddress);

      // initialize the clock memory bank
      initClock();
   }

   /**
    * Create a container with a provided adapter object
    * and the address of the iButton or 1-Wire device.
    *
    * @param  sourceAdapter     adapter object required to communicate with
    * this iButton.
    * @param  newAddress        address of this 1-Wire device
    */
   public OneWireContainer04 (DSPortAdapter sourceAdapter, long newAddress)
   {
      super(sourceAdapter, newAddress);

      // initialize the clock memory bank
      initClock();
   }

   /**
    * Create a container with a provided adapter object
    * and the address of the iButton or 1-Wire device.
    *
    * @param  sourceAdapter     adapter object required to communicate with
    * this iButton.
    * @param  newAddress        address of this 1-Wire device
    */
   public OneWireContainer04 (DSPortAdapter sourceAdapter, String newAddress)
   {
      super(sourceAdapter, newAddress);

      // initialize the clock memory bank
      initClock();
   }

   //--------
   //-------- Methods
   //--------

   /**
    * Retrieve the Dallas Semiconductor part number of the iButton
    * as a string.  For example 'DS1992'.
    *
    * @return string represetation of the iButton name.
    */
   public String getName ()
   {
      return "DS1994";
   }

   /**
    * Retrieve the alternate Dallas Semiconductor part numbers or names.
    * A 'family' of MicroLAN devices may have more than one part number
    * depending on packaging.  There can also be nicknames such as
    * 'Crypto iButton'.
    *
    * @return string represetation of the alternate names.
    */
   public String getAlternateNames ()
   {
      return "DS2404, Time-in-a-can";
   }

   /**
    * Retrieve a short description of the function of the iButton type.
    *
    * @return string represetation of the function description.
    */
   public String getDescription ()
   {
      return "4096 bit read/write nonvolatile memory partitioned "
             + "into sixteen pages of 256 bits each and a real "
             + "time clock/calendar in binary format.";
   }

   /**
    * Return an enumeration of memory banks. Look at the
    * MemoryBank, PagedMemoryBank and OTPMemoryBank classes.
    */
   public Enumeration getMemoryBanks ()
   {
      Vector bank_vector = new Vector(4);

      // Address number in read-only-memory
      bank_vector.addElement(new MemoryBankROM(this));

      // scratchpad
      bank_vector.addElement(scratch);

      // NVRAM
      bank_vector.addElement(new MemoryBankNV(this, scratch));

      // clock
      bank_vector.addElement(clock);

      return bank_vector.elements();
   }

   //-------- 
   //-------- Clock Feature methods 
   //-------- 

   /**
    * Query to see if the clock has an alarm feature.
    *
    * @return boolean, true if real-time-clock has an alarm
    */
   public boolean hasClockAlarm ()
   {
      return true;
   }

   /**
    * Query to see if the clock can be disabled.  See
    * the methods 'isClockRunning()' and 'setClockRunEnable()'.
    *
    * @return boolean, true if the clock can be enabled and
    * disabled.
    */
   public boolean canDisableClock ()
   {
      return true;
   }

   /**
    * Query to get the clock resolution in milliseconds
    *
    * @return long, get the clock resolution in milliseconds.
    */
   public long getClockResolution ()
   {
      return 4;
   }

   //--------
   //-------- Clock IO Methods
   //--------

   /**
    * This method retrieves the entire clock register page.  It reads this
    * page up to 5 times or until a match (except for lsbyte of timers)
    * is the same.  Use the byte array return from this method with static
    * utility methods to extract the real-time-clock and other register values.
    * Appended to the clock page data is 4 bytes that represent a bitmap
    * of changed bytes.  These bytes are used in the 'writeClockRegisters()'
    * in conjuction with the 'set' methods to only write back the changed
    * clock register bytes.  This method will clear any pending alarms.
    *
    * @returns <code>byte[]<\code> clock register page contents verified
    *  with multiple reads and cleared bitmap of changed bytes
    *
    *
    * @return
    * @throws OneWireIOException
    * @throws OneWireException
    */
   public byte[] readDevice ()
      throws OneWireIOException, OneWireException
   {
      byte[][] read_buf = new byte [2][36];
      boolean  alarming;
      int      buf_num = 0, attempt = 0, i;

      // put zero's in the bitmap of changed bytes
      for (i = 32; i < 36; i++)
      {
         read_buf [0][i] = 0;
         read_buf [1][i] = 0;
      }

      // check if device alarming
      alarming = isAlarming();

      // loop up to 5 times to read clock register page
      do
      {

         // only read status byte once if device was alarming (will be cleared)
         if (alarming && (attempt != 0))
            clock.read(1, false, read_buf [buf_num], 1, 31);
         else
            clock.read(0, false, read_buf [buf_num], 0, 32);

         // compare if this is not the first read
         if (attempt++ != 0)
         {

            // loop to see if same
            for (i = 1; i < 32; i++)
            {
               if ((i != 2) && (i != 7))
               {
                  if (read_buf [0][i] != read_buf [1][i])
                     break;
               }
            }

            // check on compare, if ok then return most recent read_buf
            if (i == 32)
               return read_buf [buf_num];
         }

         // alternate buffer
         buf_num = (buf_num == 0) ? 1
                                  : 0;
      }
      while (attempt < 5);

      // failed to get a match
      throw new OneWireIOException("Failed to read the clock register page");
   }

   /**
    * This method write the bytes in the provided clock register page that
    * have been changed by the 'set' methods.  It knows which registers have
    * changed by looking at the bitmap fields appended to the clock register
    * page data.  WARNING: If write-protect alarm options have been set then
    * this operation is non-reversable.
    *
    * @param  state - byte array of clock register page contents
    *
    * @throws OneWireIOException
    * @throws OneWireException
    */
   public void writeDevice (byte[] state)
      throws OneWireIOException, OneWireException
   {
      int     start_offset = 0, len = 0, i;
      boolean got_block = false;

      // loop to collect changed bytes and write them in blocks
      for (i = 0; i < 32; i++)
      {

         // check to see if this byte needs writing (skip control register for now)
         if ((Bit.arrayReadBit(i, BITMAP_OFFSET, state) == 1) && (i != 1))
         {

            // check if already in a block 
            if (got_block)
               len++;

               // new block
            else
            {
               got_block    = true;
               start_offset = i;
               len          = 1;
            }

            // check for last byte exception, write current block
            if (i == 31)
               clock.write(start_offset, state, start_offset, len);
         }
         else if (got_block)
         {

            // done with this block so write it
            clock.write(start_offset, state, start_offset, len);

            got_block = false;
         }
      }

      // check if need to write control register
      if (Bit.arrayReadBit(CONTROL_OFFSET, BITMAP_OFFSET, state) == 1)
      {

         // write normaly
         clock.write(CONTROL_OFFSET, state, CONTROL_OFFSET, 1);

         // check if any write-protect bits set
         if ((state [CONTROL_OFFSET] & 0x07) != 0)
         {

            // need to do a copy scratchpad 2 more times to become write-protected
            for (i = 0; i < 2; i++)
               scratch.writeScratchpad(clock.getStartPhysicalAddress()
                                       + CONTROL_OFFSET, state,
                                                         CONTROL_OFFSET, 1);
         }
      }

      // clear out the bitmap
      for (i = BITMAP_OFFSET; i < state.length; i++)
         state [i] = 0;
   }

   //--------
   //-------- Clock 'get' Methods
   //--------

   /**
    * This method extracts the Clock Value in milliseconds from the
    * state data retrieved from the 'readDevice()' method.
    *
    * @param state - byte array of device state
    *
    * @return <code>long<\code> time - in milliseconds that have
    * occured since 1970.
    */
   public long getClock (byte[] state)
   {
      return byteArrayToLong(state, RTC_OFFSET, 5) * 1000 / 256;
   }

   /**
    * This method extracts the Clock Alarm Value from the provided
    * state data retrieved from the 'readDevice()'
    * method.
    *
    * @param state - byte array of device state
    *
    * @return <code>long<\code> time - in milliseconds that have
    * the clock alarm is set to.
    */
   public long getClockAlarm (byte[] state)
   {
      return byteArrayToLong(state, RTC_ALARM_OFFSET, 5) * 1000 / 256;
   }

   /**
    * This method checks if the Real-Time Alarm flag has been set
    * from the provided clock register page retrieved from the
    * 'readClockRegisters()' method.
    *
    * @param  state - byte array of clock register page contents
    *
    * @returns <code>boolean<\code> true if real time clock is alarming
    */
   public boolean isClockAlarming (byte[] state)
   {
      return (Bit.arrayReadBit(0, STATUS_OFFSET, state) == 1);
   }

   /**
    * This method checks if the Clock Alarm is enabled
    * from the provided state data retrieved from the
    * 'readDevice()' method.
    *
    * @param state - byte array of device state
    *
    * @return <code>boolean<\code> true if clock alarm is enabled
    */
   public boolean isClockAlarmEnabled (byte[] state)
   {
      return (Bit.arrayReadBit(3, STATUS_OFFSET, state) == 0);
   }

   /**
    * This method checks if the device's oscilator is enabled.  The clock
    * will not increment if the clock is not enabled.
    * This value is read from the provided state data retrieved from the
    * 'readDevice()' method.
    *
    * @param state - byte array of device state
    *
    * @return <code>boolean<\code> true
    */
   public boolean isClockRunning (byte[] state)
   {
      return (Bit.arrayReadBit(4, CONTROL_OFFSET, state) == 1);
   }

   //-------- 
   //-------- DS1994 Specific Clock 'get' Methods 
   //-------- 

   /**
    * This method extracts the Interval Timer Value in milliseconds
    * from the clock register page retrieved from the 'readClockRegisters()'
    * method.
    *
    * @param  state - byte array of clock register page contents
    *
    * @returns <code>long<\code> time - in milliseconds
    * that have occured since the interval counter was started.
    */
   public long getIntervalTimer (byte[] state)
   {
      return byteArrayToLong(state, INTERVAL_OFFSET, 5) * 1000 / 256;
   }

   /**
    * This method extracts the cycle count Value from the
    * clock register page retrieved from the 'readClockRegisters()' method.
    *
    * @param  state - byte array of clock register page contents
    *
    * @returns <code>long<\code> cycle_count this is the total number of
    * power cycle that the DS1994 has seen since the counter was reset.
    */
   public long getCycleCounter (byte[] state)
   {
      return byteArrayToLong(state, COUNTER_OFFSET, 4);
   }

   /**
    * This method extracts the Interval Timer Alarm Value from the provided
    * clock register page retrieved from the 'readClockRegisters()'
    * method.
    *
    * @param  state - byte array of clock register page contents
    *
    * @returns <code>long<\code> time - in milliseconds that have
    * the interval time alarm is set to.
    */
   public long getIntervalTimerAlarm (byte[] state)
   {
      return byteArrayToLong(state, INTERVAL_ALARM_OFFSET, 5) * 1000 / 256;
   }

   /**
    * This method retrieves the cycle count Alarm Value from the DS1994.
    *
    * @param  state - byte array of clock register page contents
    *
    * @returns <code>long<\code> cycle_count this is the total number of power cycles
    * that the DS1994 has to to see before the alarm will be triggered.
    */
   public long getCycleCounterAlarm (byte[] state)
   {
      return byteArrayToLong(state, COUNTER_ALARM_OFFSET, 4);
   }

   /**
    * This method checks if the Interval Timer Alarm flag has been set
    * from the provided clock register page retrieved from the
    * 'readClockRegisters()' method.
    *
    * @param  state - byte array of clock register page contents
    *
    * @returns <code>boolean<\code> true if interval timer is alarming
    */
   public boolean isIntervalTimerAlarming (byte[] state)
   {
      return (Bit.arrayReadBit(1, STATUS_OFFSET, state) == 1);
   }

   /**
    * This method checks if the Cycle Alarm flag has been set
    * from the provided clock register page retrieved from the
    * 'readClockRegisters()' method.
    *
    * @param  state - byte array of clock register page contents
    *
    * @returns <code>boolean<\code> true if cycle counter is alarming
    */
   public boolean isCycleCounterAlarming (byte[] state)
   {
      return (Bit.arrayReadBit(2, STATUS_OFFSET, state) == 1);
   }

   /**
    * This method checks if the Interval Timer Alarm is enabled
    * from the provided clock register page retrieved from the
    * 'readClockRegisters()' method.
    *
    * @param  state - byte array of clock register page contents
    *
    * @returns <code>boolean<\code> true if interval timer alarm is enabled
    */
   public boolean isIntervalTimerAlarmEnabled (byte[] state)
   {
      return (Bit.arrayReadBit(4, STATUS_OFFSET, state) == 0);
   }

   /**
    * This method checks if the Cycle Alarm is enabled
    * from the provided clock register page retrieved from the
    * 'readClockRegisters()' method.
    *
    * @param  state - byte array of clock register page contents
    *
    * @returns <code>boolean<\code> true if cycle counter alarm is enabled
    */
   public boolean isCycleCounterAlarmEnabled (byte[] state)
   {
      return (Bit.arrayReadBit(5, STATUS_OFFSET, state) == 0);
   }

   /**
    * This method checks if the Real-Time clock/Alarm is write protected
    * from the provided clock register page retrieved from the
    * 'readClockRegisters()' method.
    *
    * @param  state - byte array of clock register page contents
    *
    * @returns <code>boolean<\code> true if real time clock/alarm is write
    * protected
    */
   public boolean isClockWriteProtected (byte[] state)
   {
      return (Bit.arrayReadBit(0, CONTROL_OFFSET, state) == 1);
   }

   /**
    * This method checks if the Interval Timer and Interval Timer Alarm
    * register is write protected from the provided clock register page
    * retrieved from the 'readClockRegisters()' method.
    *
    * @param  state - byte array of clock register page contents
    *
    * @returns <code>boolean<\code> true if interval timer is alarming
    */
   public boolean isIntervalTimerWriteProtected (byte[] state)
   {
      return (Bit.arrayReadBit(1, CONTROL_OFFSET, state) == 1);
   }

   /**
    * This method checks if the Cycle Counter and Alarm is write protected
    * from the provided clock register page retrieved from the
    * 'readClockRegisters()' method.
    *
    * @param  state - byte array of clock register page contents
    *
    * @returns <code>boolean<\code> true if cycle counter/alarm is write
    * protected
    */
   public boolean isCycleCounterWriteProtected (byte[] state)
   {
      return (Bit.arrayReadBit(2, CONTROL_OFFSET, state) == 1);
   }

   /**
    * This method checks if the device can be read after a write protected
    * alarm has occured from the provided clock register page retrieved from the
    * 'readClockRegisters()' method.
    *
    * @param  state - byte array of clock register page contents
    *
    * @returns <code>boolean<\code> true if the device can be read after a
    * write protected alarm has occured
    */
   public boolean canReadAfterExpire (byte[] state)
   {
      return (Bit.arrayReadBit(3, CONTROL_OFFSET, state) == 1);
   }

   /**
    * This method checks if the Interval timer is automatic or manual.  If it is
    * automatic then the interval counter will increment while the devices I/O line
    * is high after the delay select period has elapsed (either 3.5 or 123 ms, see
    * the isAutomaticDelayLong() method).
    * This value is read from the provided clock register page retrieved from the
    * 'readClockRegisters()' method.
    *
    * @param  state - byte array of clock register page contents
    *
    * @returns <code>boolean<\code> true if the interval timer is set to automatic
    * mode.
    */
   public boolean isIntervalTimerAutomatic (byte[] state)
   {
      return (Bit.arrayReadBit(5, CONTROL_OFFSET, state) == 1);
   }

   /**
    * This method checks if the Interval timer is stopped.  This only has meaning
    * if the interval timer is in manual mode (not isIntervalTimerAutomatic()).
    * This value is read from the provided clock register page retrieved from the
    * 'readClockRegisters()' method.
    *
    * @param  state - byte array of clock register page contents
    *
    * @returns <code>boolean<\code> true if the interval timer is stopped
    * mode.
    */
   public boolean isIntervalTimerStopped (byte[] state)
   {
      return (Bit.arrayReadBit(6, CONTROL_OFFSET, state) == 1);
   }

   /**
    * This method checks if the automatic delay for the Inteval Timer and the Cycle
    * counter is either 3.5ms (regular) or 123ms (long).
    * This value is read from the provided clock register page retrieved from the
    * 'readClockRegisters()' method.
    *
    * @param  state - byte array of clock register page contents
    *
    * @returns <code>boolean<\code> true if the automatic interval/cycle counter
    * delay is in the long (123ms) mode.  Delay is 3.5ms if false.
    */
   public boolean isAutomaticDelayLong (byte[] state)
   {
      return (Bit.arrayReadBit(7, CONTROL_OFFSET, state) == 1);
   }

   //--------
   //-------- Clock 'set' Methods
   //--------

   /**
    * This method sets the Clock time in the provided state data
    * Use the method 'writeDevice()' with
    * this data to finalize the change to the device.
    *
    * @param time - <code>long<\code> milliseconds the user
    * wants the Clock set to.
    * @param state - byte array of device state
    */
   public void setClock (long time, byte[] state)
   {
      longToByteArray(state, RTC_OFFSET, 5, time * 256 / 1000);

      // set bitmap field to indicate these clock registers were changed
      for (int i = 0; i < 5; i++)
         Bit.arrayWriteBit(1, RTC_OFFSET + i, BITMAP_OFFSET, state);
   }

   /**
    * This method sets the Clock Alarm in the provided state
    * data.  Use the method 'writeDevice()' with
    * this data to finalize the change to the device.
    *
    * @param time - <code>long<\code> milliseconds the user
    * wants the Clock alarm set to.
    * @param state - byte array of device state
    */
   public void setClockAlarm (long time, byte[] state)
   {
      longToByteArray(state, RTC_ALARM_OFFSET, 5, time * 256 / 1000);

      // set bitmap field to indicate these clock registers were changed 
      for (int i = 0; i < 5; i++)
         Bit.arrayWriteBit(1, RTC_ALARM_OFFSET + i, BITMAP_OFFSET, state);
   }

   /**
     * This method sets the oscillator enable to the specified
     * value. Use the method 'writeDevice()' with this
     * data to finalize the change to the device.
     *
     * @param runEnable - boolean, true if want the clock oscillator to
     *                 be enabled.
     * @param state - byte array of device state
     */
   public void setClockRunEnable (boolean runEnable, byte[] state)
   {
      Bit.arrayWriteBit(runEnable ? 1
                                  : 0, 4, CONTROL_OFFSET, state);

      // set bitmap field to indicate this clock register has changed 
      Bit.arrayWriteBit(1, CONTROL_OFFSET, BITMAP_OFFSET, state);
   }

   /**
     * This method sets the Clock Alarm enable. Use the method
     * 'writeDevice()' with this data to finalize the
     * change to the device.
     *
     * @param  alarmEnable - boolean, true to enable the clock alarm
     * @param state - byte array of device state
     */
   public void setClockAlarmEnable (boolean alarmEnable, byte[] state)
   {
      Bit.arrayWriteBit(alarmEnable ? 0
                                    : 1, 3, STATUS_OFFSET, state);

      // set bitmap field to indicate this clock register has changed 
      Bit.arrayWriteBit(1, STATUS_OFFSET, BITMAP_OFFSET, state);
   }

   //-------- 
   //-------- DS1994 Specific Clock 'set' Methods 
   //-------- 

   /**
    * This method sets the Interval Timer in the provided clock
    * register data field.  Use the method 'writeClockRegisters()' with
    * this data to finalize the change to the device.
    *
    *
    * @param time
    * @param  state - byte array of clock register page contents
    *
    * @parameter <code>long<\code> time - in milliseconds the user
    * wants the Real Time Clock set to (truncated to 1/256th of second).
    *
    * @throws IllegalArgumentException
    */
   public void setIntervalTimer (long time, byte[] state)
      throws IllegalArgumentException
   {
      longToByteArray(state, INTERVAL_OFFSET, 5, time * 256 / 1000);

      // set bitmap field to indicate these clock registers were changed
      for (int i = 0; i < 5; i++)
         Bit.arrayWriteBit(1, INTERVAL_OFFSET + i, BITMAP_OFFSET, state);
   }

   /**
    * This method sets Cycle counter in the provided clock
    * register data field.  Use the method 'writeClockRegisters()' with
    * this data to finalize the change to the device.
    *
    *
    * @param cycles
    * @param  state - byte array of clock register page contents
    *
    * @parameter <code>long<\code> cycles - this variable contains the amount of
    * cycles that the user wants to start the cycle counter at.
    *
    * @throws IllegalArgumentException
    */
   public void setCycleCounter (long cycles, byte[] state)
      throws IllegalArgumentException
   {
      longToByteArray(state, COUNTER_OFFSET, 4, cycles);

      // set bitmap field to indicate these clock registers were changed
      for (int i = 0; i < 4; i++)
         Bit.arrayWriteBit(1, COUNTER_OFFSET + i, BITMAP_OFFSET, state);
   }

   /**
    * This method sets the Interval Timer Alarm in the provided clock
    * register data field.  Use the method 'writeClockRegisters()' with
    * this data to finalize the change to the device.
    *
    *
    * @param time
    * @param  state - byte array of clock register page contents
    *
    * @parameter <code>long<\code> time - in milliseconds the user
    * wants the Interval Timer alarm set to (truncated to 1/256th of second).
    *
    * @throws IllegalArgumentException
    */
   public void setIntervalTimerAlarm (long time, byte[] state)
      throws IllegalArgumentException
   {
      longToByteArray(state, INTERVAL_ALARM_OFFSET, 5, time * 256 / 1000);

      // set bitmap field to indicate these clock registers were changed
      for (int i = 0; i < 5; i++)
         Bit.arrayWriteBit(1, INTERVAL_ALARM_OFFSET + i, BITMAP_OFFSET,
                           state);
   }

   /**
    * This method sets the Cycle Count Alarm in the provided clock
    * register data field.  Use the method 'writeClockRegisters()' with
    * this data to finalize the change to the device.
    *
    *
    * @param cycles
    * @param  state - byte array of clock register page contents
    *
    * @parameter <code>long<\code> time - this variable holds the number
    * of times the user desires to have the DS1994 experience power cycles
    * before it generates an alarm for an interrupt.
    *
    * @throws IllegalArgumentException
    */
   public void setCycleCounterAlarm (long cycles, byte[] state)
      throws IllegalArgumentException
   {
      longToByteArray(state, COUNTER_ALARM_OFFSET, 4, cycles);

      // set bitmap field to indicate these clock registers were changed
      for (int i = 0; i < 4; i++)
         Bit.arrayWriteBit(1, COUNTER_ALARM_OFFSET + i, BITMAP_OFFSET, state);
   }

   /**
    * This method sets the write protect options for the Real-Time
    * clock/Alarm in the provided clock register page retrieved from the
    * 'readClockRegisters()' method.  WARNING: after calling
    * 'writeClockRegisters()' after this method will perminatly write
    * protect these registers.
    *
    * @param  state - byte array of clock register page contents
    */
   public void writeProtectClock (byte[] state)
   {
      Bit.arrayWriteBit(1, 0, CONTROL_OFFSET, state);

      // set bitmap field to indicate this clock register has changed
      Bit.arrayWriteBit(1, CONTROL_OFFSET, BITMAP_OFFSET, state);
   }

   /**
    * This method sets the write protect options for Interval Timer and
    * Interval Timer Alarm register in the provided clock register page
    * retrieved from the 'readClockRegisters()' method.  WARNING: after
    * calling 'writeClockRegisters()' after this method will
    * perminatly write protect these registers.
    *
    * @param  state - byte array of clock register page contents
    */
   public void writeProtectedIntervalTimer (byte[] state)
   {
      Bit.arrayWriteBit(1, 1, CONTROL_OFFSET, state);

      // set bitmap field to indicate this clock register has changed
      Bit.arrayWriteBit(1, CONTROL_OFFSET, BITMAP_OFFSET, state);
   }

   /**
    * This method sets the write protect options for the Cycle Counter
    * and Alarm register in the provided clock register page
    * retrieved from the 'readClockRegisters()' method.  WARNING: after
    * calling 'writeClockRegisters()' after this method will
    * perminatly write protect these registers.
    *
    * @param  state - byte array of clock register page contents
    */
   public void writeProtectedCycleCounter (byte[] state)
   {
      Bit.arrayWriteBit(1, 2, CONTROL_OFFSET, state);

      // set bitmap field to indicate this clock register has changed
      Bit.arrayWriteBit(1, CONTROL_OFFSET, BITMAP_OFFSET, state);
   }

   /**
    * This method sets the read state of the device after
    * write protected alarm has occured. Use the method
    * 'writeClockRegisters()' with this data to finalize the
    * change to the device.
    *
    * @param  readAfter - boolean, true if want to read device after it
    *                 expires from a write protected alarm event.
    * @param  state - byte array of clock register page contents
    */
   public void setReadAfterExpire (boolean readAfter, byte[] state)
   {
      Bit.arrayWriteBit(readAfter ? 1
                                  : 0, 3, CONTROL_OFFSET, state);

      // set bitmap field to indicate this clock register has changed
      Bit.arrayWriteBit(1, CONTROL_OFFSET, BITMAP_OFFSET, state);
   }

   /**
    * This method sets the Interval timer in automatic or manual.
    * If it is automatic then the interval counter will increment
    * while the devices I/O line is high after the delay select
    * period has elapsed (either 3.5 or 123 ms, see the
    * isAutomaticDelayLong() method). Use the method
    * 'writeClockRegisters()' with this data to finalize the
    * change to the device.
    *
    * @param  autoTimer - boolean, true if want the interval timer to operate in
    *                 automatic mode.  The interval timer will then run whenever
    *                 the IO pin is higth for at least the delay-select time.
    * @param  state - byte array of clock register page contents
    */
   public void setIntervalTimerAutomatic (boolean autoTimer, byte[] state)
   {
      Bit.arrayWriteBit(autoTimer ? 1
                                  : 0, 5, CONTROL_OFFSET, state);

      // set bitmap field to indicate this clock register has changed
      Bit.arrayWriteBit(1, CONTROL_OFFSET, BITMAP_OFFSET, state);
   }

   /**
    * This method sets the Interval timer run/stop mode.  This only
    * has meaning if the interval timer is in manual mode
    * (not isIntervalTimerAutomatic()).  Use the method
    * 'writeClockRegisters()' with this data to finalize the
    * change to the device.
    *
    * @param  runState - boolean, true if want the interval timer run.
    *                 The interval timer must be in manual mode for
    *                 this to have an effect.
    * @param  state - byte array of clock register page contents
    */
   public void setIntervalTimerRunState (boolean runState, byte[] state)
   {
      Bit.arrayWriteBit(runState ? 1
                                 : 0, 6, CONTROL_OFFSET, state);

      // set bitmap field to indicate this clock register has changed
      Bit.arrayWriteBit(1, CONTROL_OFFSET, BITMAP_OFFSET, state);
   }

   /**
    * This method sets the automatic delay for the Inteval Timer and the Cycle
    * counter is either 123ms (long) or 3.5ms (regular). Use the method
    * 'writeClockRegisters()' with this data to finalize the
    * change to the device.
    *
    * @param  delayLong - boolean, true if want the interval timer in
    *                 automatic mode and cycle counter to increment
    *                 after 123ms.  'false' if 3.5ms is desired.
    * @param  state - byte array of clock register page contents
    */
   public void setAutomaticDelayLong (boolean delayLong, byte[] state)
   {
      Bit.arrayWriteBit(delayLong ? 1
                                  : 0, 7, CONTROL_OFFSET, state);

      // set bitmap field to indicate this clock register has changed
      Bit.arrayWriteBit(1, CONTROL_OFFSET, BITMAP_OFFSET, state);
   }

   /**
    * This method sets the Interval Timer Alarm enable. Use the method
    * 'writeClockRegisters()' with this data to finalize the
    * change to the device.
    *
    * @param  alarmEnable - boolean, true if want the interval-timer-alarm
    *                 enabled.
    * @param  state - byte array of clock register page contents
    */
   public void setIntervalTimerAlarmEnable (boolean alarmEnable, byte[] state)
   {
      Bit.arrayWriteBit(alarmEnable ? 0
                                    : 1, 4, STATUS_OFFSET, state);

      // set bitmap field to indicate this clock register has changed
      Bit.arrayWriteBit(1, STATUS_OFFSET, BITMAP_OFFSET, state);
   }

   /**
    * This method sets the Cycle counter Alarm enable.  Use the method
    * 'writeClockRegisters()' with this data to finalize the
    * change to the device.
    *
    * @param  alarmEnable - boolean, true if want the cycle-counter-alarm
    *                 enabled.
    * @param  state - byte array of clock register page contents
    */
   public void setCycleCounterAlarmEnable (boolean alarmEnable, byte[] state)
   {
      Bit.arrayWriteBit(alarmEnable ? 0
                                    : 1, 5, STATUS_OFFSET, state);

      // set bitmap field to indicate this clock register has changed
      Bit.arrayWriteBit(1, STATUS_OFFSET, BITMAP_OFFSET, state);
   }

   //--------
   //-------- Private
   //--------

   /**
    * Create the memory bank interface to read/write the clock
    */
   private void initClock ()
   {

      // scratchpad
      scratch = new MemoryBankScratch(this);

      // clock
      clock                      = new MemoryBankNV(this, scratch);
      clock.numberPages          = 1;
      clock.startPhysicalAddress = 512;
      clock.size                 = 32;
      clock.generalPurposeMemory = false;
      clock.maxPacketDataLength  = 0;
      clock.bankDescription      = "Clock/alarm registers";
   }

   /**
    * This constructs a long from a LSByte byte array of specified length.
    *
    * @param  byteArray - byte array to convert to a long (LSByte first)
    * @param  offset - byte offset into the array where to start to convert
    * @param  len - number of bytes to use to convert to a long
    *
    * @returns <code>long<\code> value constructed from bytes
    */
   static private long byteArrayToLong (byte[] byteArray, int offset, int len)
   {
      long val = 0;

      // Concatanate the byte array into one variable.
      for (int i = (len - 1); i >= 0; i--)
      {
         val <<= 8;
         val |= (byteArray [offset + i] & 0x00FF);
      }

      return val;
   }

   /**
    * This constructs byte array from a long and places it in the provided byte
    * array at the specified offset.
    *
    * @param  byteArray - byte array to convert to get long (LSByte first)
    * @param  offset - byte offset into the array where to start to convert
    * @param  len - number of bytes to use to convert from long
    * @param  val - long to put into byte array LSByte first
    *
    * @throws IllegalArgumentException
    */
   static private void longToByteArray (byte[] byteArray, int offset,
                                        int len, long val)
      throws IllegalArgumentException
   {

      // Decipher the long variable Val into a 'len' byte array to set
      for (int i = 0; i < len; i++)
      {
         byteArray [offset + i] = ( byte ) val;
         val                    >>>= 8;
      }

      // check if too long
      if (val > 0)
         throw new IllegalArgumentException("Argument is larger then " + len
                                            + " bytes");
   }
}
