auto APU::Square1::runSweep(bool update) -> void {
  if(!sweep.enable) return;

  sweep.negate = sweep.direction;
  uint delta = shadowfrequency >> sweep.shift;
  int updatefrequency = shadowfrequency + (sweep.negate ? -delta : delta);

  if(updatefrequency > 2047) {
    enable = false;
  } else if(sweep.shift && update) {
    shadowfrequency = updatefrequency;
    frequency = updatefrequency;
    period = 2 * (2048 - frequency);
  }
}

auto APU::Square1::clockSweep() -> void {
  if(enable && sweep.frequency && --sweep.period == 0) {
    sweep.period = sweep.frequency;
    runSweep(1);
    runSweep(0);
  }
}

auto APU::Square1::read(uint addr) const -> uint8 {
  switch(addr) {
  case 0: return (sweep.shift << 0) | (sweep.direction << 3) | (sweep.frequency << 4);
  case 1: return (duty << 6);
  case 2: return (envelope.frequency << 0) | (envelope.direction << 3) | (envelope.volume << 4);
  case 3: return 0;
  case 4: return (counter << 6);
  }
  return 0;
}

auto APU::Square1::write(uint addr, uint8 byte) -> void {
  switch(addr) {
  case 0:  //NR10
    if(sweep.negate && sweep.direction && !(byte & 0x08)) enable = false;
    sweep.shift     = byte >> 0;
    sweep.direction = byte >> 3;
    sweep.frequency = byte >> 4;
    break;

  case 1:  //NR11
    length = byte >> 0;
    duty   = byte >> 6;
    break;

  case 2:  //NR12
    envelope.frequency = byte >> 0;
    envelope.direction = byte >> 3;
    envelope.volume    = byte >> 4;
    if(!envelope.dacEnable()) enable = false;
    break;

  case 3:  //NR13
    frequency = (frequency & 0xff00) | (byte << 0);
    break;

  case 4:  //NR14
    frequency = (frequency & 0x00ff) | (byte << 8);
    counter    = byte >> 6;
    initialize = byte >> 7;

    if(initialize) {
      enable = envelope.dacEnable();
      period = 2 * (2048 - frequency);
      envelope.period = envelope.frequency;
      volume = envelope.volume;
      shadowfrequency = frequency;
      sweep.period = sweep.frequency;
      sweep.enable = sweep.period || sweep.shift;
      sweep.negate = false;
      if(sweep.shift) runSweep(0);
    }

    break;
  }
}

auto APU::Square1::power() -> void {
  envelope.frequency = 0;
  envelope.direction = 0;
  envelope.direction = 0;
  envelope.period = 0;
  sweep.shift = 0;
  sweep.direction = 0;
  sweep.frequency = 0;
  sweep.enable = 0;
  sweep.negate = 0;
  sweep.period = 0;
  enable = 0;
  length = 0;
  duty = 0;
  frequency = 0;
  counter = 0;
  initialize = 0;
  shadowfrequency = 0;
  signal = 0;
  output = 0;
  period = 0;
  phase = 0;
  volume = 0;
}
