/**
 * This file is part of reSID, a MOS6581 SID emulator engine.
 * Copyright (C) 2004  Dag Lem <resid@nimrod.no>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * @author Ken Handel
 *
 */
package ucesoft.cbm.peripheral.sid.resid;

/**
 * A 15 bit counter is used to implement the envelope rates, in effect dividing
 * the clock to the envelope counter by the currently selected rate period.
 * <P>
 * In addition, another counter is used to implement the exponential envelope
 * decay, in effect further dividing the clock to the envelope counter. The
 * period of this counter is set to 1, 2, 4, 8, 16, 30 at the envelope counter
 * values 255, 93, 54, 26, 14, 6, respectively.
 * 
 * @author Ken Handel
 * 
 */
public class EnvelopeGenerator {

  public static enum State {
    ATTACK, DECAY_SUSTAIN, RELEASE
  };

  protected int /* reg16 */rate_counter;

  protected int /* reg16 */rate_period;

  protected int /* reg8 */exponential_counter;

  protected int /* reg8 */exponential_counter_period;

  protected int /* reg8 */envelope_counter;

  protected boolean hold_zero;

  protected int /* reg4 */attack;

  protected int /* reg4 */decay;

  protected int /* reg4 */sustain;

  protected int /* reg4 */release;

  protected int /* reg8 */gate;

  /**
   * ATTACK/DECAY_SUSTAIN/RELEASE
   */
  protected State state;

  /**
   * Lookup table to convert from attack, decay, or release value to rate
   * counter period.
   * <P>
   * Rate counter periods are calculated from the Envelope Rates table in the
   * Programmer's Reference Guide. The rate counter period is the number of
   * cycles between each increment of the envelope counter. The rates have
   * been verified by sampling ENV3.
   * <P>
   * The rate counter is a 16 bit register which is incremented each cycle.
   * When the counter reaches a specific comparison value, the envelope
   * counter is incremented (attack) or decremented (decay/release) and the
   * counter is zeroed.
   * <P>
   * NB! Sampling ENV3 shows that the calculated values are not exact. It may
   * seem like most calculated values have been rounded (.5 is rounded down)
   * and 1 has beed added to the result. A possible explanation for this is
   * that the SID designers have used the calculated values directly as rate
   * counter comparison values, not considering a one cycle delay to zero the
   * counter. This would yield an actual period of comparison value + 1.
   * <P>
   * The time of the first envelope count can not be exactly controlled,
   * except possibly by resetting the chip. Because of this we cannot do cycle
   * exact sampling and must devise another method to calculate the rate
   * counter periods.
   * <P>
   * The exact rate counter periods can be determined e.g. by counting the
   * number of cycles from envelope level 1 to envelope level 129, and
   * dividing the number of cycles by 128. CIA1 timer A and B in linked mode
   * can perform the cycle count. This is the method used to find the rates
   * below.
   * <P>
   * To avoid the ADSR delay bug, sampling of ENV3 should be done using
   * sustain = release = 0. This ensures that the attack state will not lower
   * the current rate counter period.
   * <P>
   * The ENV3 sampling code below yields a maximum timing error of 14 cycles.
   * 
   * <pre>
   *      lda #$01
   *  l1: cmp $d41c
   *      bne l1
   *      ...
   *      lda #$ff
   *  l2: cmp $d41c
   *      bne l2
   * </pre>
   * 
   * This yields a maximum error for the calculated rate period of 14/128
   * cycles. The described method is thus sufficient for exact calculation of
   * the rate periods.
   */
  protected static int /* reg16 */rate_counter_period[] = {
    9, // 2ms*1.0MHz/256 = 7.81
    32, // 8ms*1.0MHz/256 = 31.25
    63, // 16ms*1.0MHz/256 = 62.50
    95, // 24ms*1.0MHz/256 = 93.75
    149, // 38ms*1.0MHz/256 = 148.44
    220, // 56ms*1.0MHz/256 = 218.75
    267, // 68ms*1.0MHz/256 = 265.63
    313, // 80ms*1.0MHz/256 = 312.50
    392, // 100ms*1.0MHz/256 = 390.63
    977, // 250ms*1.0MHz/256 = 976.56
    1954, // 500ms*1.0MHz/256 = 1953.13
    3126, // 800ms*1.0MHz/256 = 3125.00
    3907, // 1 s*1.0MHz/256 = 3906.25
    11720, // 3 s*1.0MHz/256 = 11718.75
    19532, // 5 s*1.0MHz/256 = 19531.25
    31251 // 8 s*1.0MHz/256 = 31250.00
  };

  /**
   * The 16 selectable sustain levels.
   * <P>
   * For decay and release, the clock to the envelope counter is sequentially
   * divided by 1, 2, 4, 8, 16, 30, 1 to create a piece-wise linear
   * approximation of an exponential. The exponential counter period is loaded
   * at the envelope counter values 255, 93, 54, 26, 14, 6, 0. The period can
   * be different for the same envelope counter value, depending on whether
   * the envelope has been rising (attack -> release) or sinking
   * (decay/release).
   * <P>
   * Since it is not possible to reset the rate counter (the test bit has no
   * influence on the envelope generator whatsoever) a method must be devised
   * to do cycle exact sampling of ENV3 to do the investigation. This is
   * possible with knowledge of the rate period for A=0, found above.
   * <P>
   * The CPU can be synchronized with ENV3 by first synchronizing with the
   * rate counter by setting A=0 and wait in a carefully timed loop for the
   * envelope counter _not_ to change for 9 cycles. We can then wait for a
   * specific value of ENV3 with another timed loop to fully synchronize with
   * ENV3.
   * <P>
   * At the first period when an exponential counter period larger than one is
   * used (decay or relase), one extra cycle is spent before the envelope is
   * decremented. The envelope output is then delayed one cycle until the
   * state is changed to attack. Now one cycle less will be spent before the
   * envelope is incremented, and the situation is normalized.
   * <P>
   * The delay is probably caused by the comparison with the exponential
   * counter, and does not seem to affect the rate counter. This has been
   * verified by timing 256 consecutive complete envelopes with A = D = R = 1,
   * S = 0, using CIA1 timer A and B in linked mode. If the rate counter is
   * not affected the period of each complete envelope is
   * <P>
   * (255 + 162*1 + 39*2 + 28*4 + 12*8 + 8*16 + 6*30)*32 = 756*32 = 32352
   * <P>
   * which corresponds exactly to the timed value divided by the number of
   * complete envelopes.
   * <P>
   * NB! This one cycle delay is not modeled.
   * <P>
   * From the sustain levels it follows that both the low and high 4 bits of
   * the envelope counter are compared to the 4-bit sustain value. This has
   * been verified by sampling ENV3.
   */
  protected static int /* reg8 */sustain_level[] = {
    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa,
    0xbb, 0xcc, 0xdd, 0xee, 0xff, };

  // ----------------------------------------------------------------------------
  // Inline functions.
  // The following functions are defined inline because they are called every
  // time a sample is calculated.
  // ----------------------------------------------------------------------------

  /**
   * SID clocking - 1 cycle.
   */
  public void clock() {
    // Check for ADSR delay bug.
    // If the rate counter comparison value is set below the current value
    // of the rate counter, the counter will continue counting up until it
    // wraps
    // around to zero at 2^15 = 0x8000, and then count rate_period - 1
    // before the
    // envelope can finally be stepped.
    // This has been verified by sampling ENV3.
    //
    if ((++rate_counter & 0x8000) != 0) {
      ++rate_counter;
      rate_counter &= 0x7fff;
    }

    if (rate_counter != rate_period) {
      return;
    }

    rate_counter = 0;

    // The first envelope step in the attack state also resets the
    // exponential counter. This has been verified by sampling ENV3.
    //
    if (state == State.ATTACK
        || ++exponential_counter == exponential_counter_period) {
      exponential_counter = 0;

      // Check whether the envelope counter is frozen at zero.
      if (hold_zero) {
        return;
      }

      if (state == State.ATTACK) {
        // The envelope counter can flip from 0xff to 0x00 by changing
        // state to release, then to attack. The envelope counter is
        // then frozen
        // at zero; to unlock this situation the state must be changed
        // to
        // release, then to attack. This has been verified by sampling
        // ENV3.
        //
        ++envelope_counter;
        envelope_counter &= 0xff;
        if (envelope_counter == 0xff) {
          state = State.DECAY_SUSTAIN;
          rate_period = rate_counter_period[decay];
        }
      } else if (state == State.DECAY_SUSTAIN) {
        if (envelope_counter != sustain_level[sustain]) {
          --envelope_counter;
        }
      } else if (state == State.RELEASE) {
        // The envelope counter can flip from 0x00 to 0xff by changing
        // state to
        // attack, then to release. The envelope counter will then
        // continue
        // counting down in the release state.
        // This has been verified by sampling ENV3.
        // NB! The operation below requires two's complement integer.
        //
        --envelope_counter;
        envelope_counter &= 0xff;
      }

      // Check for change of exponential counter period.
      switch (envelope_counter) {
      case 0xff:
        exponential_counter_period = 1;
        break;
      case 0x5d:
        exponential_counter_period = 2;
        break;
      case 0x36:
        exponential_counter_period = 4;
        break;
      case 0x1a:
        exponential_counter_period = 8;
        break;
      case 0x0e:
        exponential_counter_period = 16;
        break;
      case 0x06:
        exponential_counter_period = 30;
        break;
      case 0x00:
        exponential_counter_period = 1;

        // When the envelope counter is changed to zero, it is frozen at
        // zero.
        // This has been verified by sampling ENV3.
        hold_zero = true;
        break;
      }
    }
  }

  /**
   * SID clocking - delta_t cycles.
   */
  public void clock(int /* cycle_count */delta_t) {
    // Check for ADSR delay bug.
    // If the rate counter comparison value is set below the current value
    // of the
    // rate counter, the counter will continue counting up until it wraps
    // around
    // to zero at 2^15 = 0x8000, and then count rate_period - 1 before the
    // envelope can finally be stepped.
    // This has been verified by sampling ENV3.
    //

    // NB! This requires two's complement integer.
    int rate_step = rate_period - rate_counter;
    if (rate_step <= 0) {
      rate_step += 0x7fff;
    }

    while (delta_t != 0) {
      if (delta_t < rate_step) {
        rate_counter += delta_t;
        if ((rate_counter & 0x8000) != 0) {
          ++rate_counter;
          rate_counter &= 0x7fff;
        }
        return;
      }

      rate_counter = 0;
      delta_t -= rate_step;

      // The first envelope step in the attack state also resets the
      // exponential
      // counter. This has been verified by sampling ENV3.
      //
      if (state == State.ATTACK
          || ++exponential_counter == exponential_counter_period) {
        exponential_counter = 0;

        // Check whether the envelope counter is frozen at zero.
        if (hold_zero) {
          rate_step = rate_period;
          continue;
        }

        if (state == State.ATTACK) {
          // The envelope counter can flip from 0xff to 0x00 by
          // changing state to
          // release, then to attack. The envelope counter is then
          // frozen at
          // zero; to unlock this situation the state must be changed
          // to release,
          // then to attack. This has been verified by sampling ENV3.
          //
          ++envelope_counter;
          envelope_counter &= 0xff;
          if (envelope_counter == 0xff) {
            state = State.DECAY_SUSTAIN;
            rate_period = rate_counter_period[decay];
          }
        } else if (state == State.DECAY_SUSTAIN) {
          if (envelope_counter != sustain_level[sustain]) {
            --envelope_counter;
          }
        } else if (state == State.RELEASE) {
          // The envelope counter can flip from 0x00 to 0xff by
          // changing state to
          // attack, then to release. The envelope counter will then
          // continue
          // counting down in the release state.
          // This has been verified by sampling ENV3.
          // NB! The operation below requires two's complement
          // integer.
          //
          --envelope_counter;
          envelope_counter &= 0xff;
        }

        // Check for change of exponential counter period.
        switch (envelope_counter) {
        case 0xff:
          exponential_counter_period = 1;
          break;
        case 0x5d:
          exponential_counter_period = 2;
          break;
        case 0x36:
          exponential_counter_period = 4;
          break;
        case 0x1a:
          exponential_counter_period = 8;
          break;
        case 0x0e:
          exponential_counter_period = 16;
          break;
        case 0x06:
          exponential_counter_period = 30;
          break;
        case 0x00:
          exponential_counter_period = 1;

          // When the envelope counter is changed to zero, it is
          // frozen at zero.
          // This has been verified by sampling ENV3.
          hold_zero = true;
          break;
        }
      }

      rate_step = rate_period;
    }
  }

  /**
   * 8-bit envelope output.
   * <P>
   * Read the envelope generator output.
   * 
   * @return envelope_counter
   */
  public int /* reg8 */output() {
    return envelope_counter;
  }

  // ----------------------------------------------------------------------------
  // END Inline functions.
  // ----------------------------------------------------------------------------

  /**
   * Constructor.
   */
  public EnvelopeGenerator() {
    reset();
  }

  /**
   * SID reset.
   */
  public void reset() {
    envelope_counter = 0;

    attack = 0;
    decay = 0;
    sustain = 0;
    release = 0;

    gate = 0;

    rate_counter = 0;
    exponential_counter = 0;
    exponential_counter_period = 1;

    state = State.RELEASE;
    rate_period = rate_counter_period[release];
    hold_zero = true;
  }

  /**
   * Register functions.
   * 
   * @param control
   * control register
   */
  public void writeCONTROL_REG(int /* reg8 */control) {
    int /* reg8 */gate_next = control & 0x01;

    // The rate counter is never reset, thus there will be a delay before
    // the
    // envelope counter starts counting up (attack) or down (release).

    // Gate bit on: Start attack, decay, sustain.
    if ((gate == 0) && (gate_next != 0)) {
      state = State.ATTACK;
      rate_period = rate_counter_period[attack];

      // Switching to attack state unlocks the zero freeze.
      hold_zero = false;
    }
    // Gate bit off: Start release.
    else if ((gate != 0) && (gate_next == 0)) {
      state = State.RELEASE;
      rate_period = rate_counter_period[release];
    }

    gate = gate_next;
  }

  /**
   * @param attack_decay
   * attack/decay value
   */
  public void writeATTACK_DECAY(int /* reg8 */attack_decay) {
    attack = (attack_decay >> 4) & 0x0f;
    decay = attack_decay & 0x0f;
    if (state == State.ATTACK) {
      rate_period = rate_counter_period[attack];
    } else if (state == State.DECAY_SUSTAIN) {
      rate_period = rate_counter_period[decay];
    }
  }

  /**
   * @param sustain_release
   * sustain/release value
   */
  public void writeSUSTAIN_RELEASE(int /* reg8 */sustain_release) {
    sustain = (sustain_release >> 4) & 0x0f;
    release = sustain_release & 0x0f;
    if (state == State.RELEASE) {
      rate_period = rate_counter_period[release];
    }
  }

  /**
   * @return envelope counter
   */
  public int /* reg8 */readENV() {
    return output();
  }

}
