content: /*
   * HalfNES by Andrew Hoffman
   * Licensed under the GNU GPL Version 3. See LICENSE file
   */
  package com.grapeshot.halfnes.audio;
  
  import com.grapeshot.halfnes.utils;
  
  /**
   *
   * @author Andrew
   */
  public class VRC6SoundChip implements ExpansionSoundChip {
      //to access sound test in Castlevania 3(J) - 
      //Hold down A+B while resetting, push Start twice
  
      private final Timer[] timers = {new SquareTimer(16), new SquareTimer(16)};
      private final boolean[] enable = {true, true, true};
      private final int[] volume = {0, 0, 0};
      private int sawdivider = 15;
      private int sawctr = 0;
      private int sawaccum = 0;
      private int sawseq = 0;
      private boolean clocknow = false;
  
      @Override
      public final void write(final int register, final int data) {
          switch (register) {
              case 0x9000:
                  volume[0] = data & 0xf;
                  //duty cycle is between 12.5% and 50% unless last bit set and then it's forced to 100%
                  timers[0].setduty(((data & (utils.BIT7)) != 0) ? 16 : (((data >> 4) & 7) + 1));
                  break;
              case 0x9001:
                  timers[0].setperiod((timers[0].getperiod() & 0xf00) + data);
                  break;
              case 0x9002:
                  timers[0].setperiod((timers[0].getperiod() & 0xff) + ((data & 0xf) << 8));
                  enable[0] = ((data & (utils.BIT7)) != 0);
                  break;
              case 0xa000:
                  volume[1] = data & 0xf;
                  timers[1].setduty(((data & (utils.BIT7)) != 0) ? 16 : (((data >> 4) & 7) + 1));
                  break;
              case 0xa001:
                  timers[1].setperiod((timers[1].getperiod() & 0xf00) + data);
                  break;
              case 0xa002:
                  timers[1].setperiod((timers[1].getperiod() & 0xff) + ((data & 0xf) << 8));
                  enable[1] = ((data & (utils.BIT7)) != 0);
                  break;
              case 0xb000:
                  //saw accumulator
                  sawaccum = data & 0x3f;
                  break;
              case 0xb001:
                  sawdivider &= 0xf00;
                  sawdivider += data;
                  break;
              case 0xb002:
                  sawdivider &= 0xff;
                  sawdivider += ((data & 0xf) << 8);
                  enable[2] = ((data & (utils.BIT7)) != 0);
                  break;
          }
      }
  
      @Override
      public final void clock(final int cycle) {
          timers[0].clock(cycle);
          timers[1].clock(cycle);
          for (int i = 0; i < cycle; ++i) {
              clocksaw();
          }
      }
  
      @Override
      public final int getval() {
          return 320 * (((enable[0] ? volume[0] : 0) * timers[0].getval()
                  + (enable[1] ? volume[1] : 0) * timers[1].getval())
                  + (enable[2] ? ((volume[2] & 0xff) >> 3) : 0));
      }
  
      private void clocksaw() {
          --sawctr;
          if (sawctr < 0) {
              sawctr = sawdivider;
              if (clocknow) {
                  clocknow = false;
                  volume[2] += sawaccum;
                  ++sawseq;
                  if (sawseq > 6) {
                      sawseq = 0;
                      volume[2] = 0;
                  }
              } else {
                  clocknow = true;
              }
          }
      }
  }
  