////////////////////////////////////////////////////////////////////////////////
// SID6581.h -- this file is part of the Emulator Developers Kit
// available at http://ourworld.compuserve.com/homepages/pc64/develop.htm


class SID6581 : public Chip {

  enum { fKey = 0x01 };
  enum { fSync = 0x02 };
  enum { fRing = 0x04 };
  enum { fTest = 0x08 };
  enum { fTriangle = 0x10 };
  enum { fSawtooth = 0x20 };
  enum { fPulse = 0x40 };
  enum { fNoise = 0x80 };
  byte abControl[3];
  byte abControlFill[1];
  byte abOutput[3];
  byte abOutputFill[1];
  int aiCounter[3];
  int aiOldCounter[3];
  int aiFreq[3];
  int aiPulseWidth[3];
  int aiNoise[3];

  inline void ResetVoice(int i) {
    abControl[i] = 0;
    abOutput[i] = 0;
    aiCounter[i] = 0;
    aiOldCounter[i] = 0;
    aiFreq[i] = 0;
    aiPulseWidth[i] = 0;
    aiNoise[i] = -1;
  }

  int aiAttack[3];
  int aiDecay[3];
  int aiSustain[3];
  int aiRelease[3];
  flag afEnvIncrement[3];
  int aiEnvCounter[3];
  int aiEnvelope[3];
  static const int gaiStart[16];

  inline void ResetEnvelope(int i) {
    aiAttack[i] = gaiStart[0];
    aiDecay[i] = gaiStart[0];
    aiSustain[i] = 0;
    aiRelease[i] = gaiStart[0];
    afEnvIncrement[i] = false;
    aiEnvCounter[i] = 0;
    aiEnvelope[i] = 0;
  }

  byte bResFilt;
  enum { fLowPass = 0x10 };
  enum { fBandPass = 0x20 };
  enum { fHighPass = 0x40 };
  enum { fMute3 = 0x80 };
  byte bModeVol;
  byte abFill[2];
  int iVolume;

  inline void ResetGeneral() {
    bResFilt = 0;
    bModeVol = 0;
    iVolume = 0;
  }

  void OnReset() {
    for (int i = 0; i < 3; i++) {
      ResetVoice(i);
      ResetEnvelope(i);
    }
    ResetGeneral();
  }

  flag fNoSoundMode;

  // register access functions
  byte Read00() { return 0; }
  void Write1FreqLow(byte bValue);
  void Write1FreqHigh(byte bValue);
  void Write1PulseWidthLow(byte bValue);
  void Write1PulseWidthHigh(byte bValue);
  void Write1Control(byte bValue);
  void Write1AttackDecay(byte bValue);
  void Write1SustainRelease(byte bValue);
  void Write2FreqLow(byte bValue);
  void Write2FreqHigh(byte bValue);
  void Write2PulseWidthLow(byte bValue);
  void Write2PulseWidthHigh(byte bValue);
  void Write2Control(byte bValue);
  void Write2AttackDecay(byte bValue);
  void Write2SustainRelease(byte bValue);
  void Write3FreqLow(byte bValue);
  void Write3FreqHigh(byte bValue);
  void Write3PulseWidthLow(byte bValue);
  void Write3PulseWidthHigh(byte bValue);
  void Write3Control(byte bValue);
  void Write3AttackDecay(byte bValue);
  void Write3SustainRelease(byte bValue);

  void WriteResFilt(byte bValue);
  void WriteModeVol(byte bValue);

  byte Read3Output();
  byte Read3Envelope();

  // initialisation
  virtual void DoInit();

public:

  // constructor
  global SID6581() {
    static const Register aRegs[32] = {
      (bpfn)Read00, (pfnb)Write1FreqLow, 0,
      (bpfn)Read00, (pfnb)Write1FreqHigh, 0,
      (bpfn)Read00, (pfnb)Write1PulseWidthLow, 0,
      (bpfn)Read00, (pfnb)Write1PulseWidthHigh, 0,
      (bpfn)Read00, (pfnb)Write1Control, 0,
      (bpfn)Read00, (pfnb)Write1AttackDecay, 0,
      (bpfn)Read00, (pfnb)Write1SustainRelease, 0,
      (bpfn)Read00, (pfnb)Write2FreqLow, 0,
      (bpfn)Read00, (pfnb)Write2FreqHigh, 0,
      (bpfn)Read00, (pfnb)Write2PulseWidthLow, 0,
      (bpfn)Read00, (pfnb)Write2PulseWidthHigh, 0,
      (bpfn)Read00, (pfnb)Write2Control, 0,
      (bpfn)Read00, (pfnb)Write2AttackDecay, 0,
      (bpfn)Read00, (pfnb)Write2SustainRelease, 0,
      (bpfn)Read00, (pfnb)Write3FreqLow, 0,
      (bpfn)Read00, (pfnb)Write3FreqHigh, 0,
      (bpfn)Read00, (pfnb)Write3PulseWidthLow, 0,
      (bpfn)Read00, (pfnb)Write3PulseWidthHigh, 0,
      (bpfn)Read00, (pfnb)Write3Control, 0,
      (bpfn)Read00, (pfnb)Write3AttackDecay, 0,
      (bpfn)Read00, (pfnb)Write3SustainRelease, 0,
      (bpfn)Read00, NULL, 0,
      (bpfn)Read00, NULL, DummyReads,
      (bpfn)Read00, (pfnb)WriteResFilt, 0,
      (bpfn)Read00, (pfnb)WriteModeVol, 0,
      NULL, NULL, 0,
      NULL, NULL, 0,
      (bpfn)Read3Output, NULL, 0,
      (bpfn)Read3Envelope, NULL, 0,
      (bpfn)Read00, NULL, 0, // TODO: really reads 0? Writes?
      (bpfn)Read00, NULL, 0,
      (bpfn)Read00, NULL, 0
    };
    pRegisters = aRegs;
    iRegisterCount = 32;
    OnReset();  
    fNoSoundMode = false;
  }

  // EDK interfaces
  Line Reset;

  // calculate the next 16 bit sample
  global word GetNextSample(int iClocksSinceLastSample);

  // workaround for OSC3 random values as NoSound doesn't call GetNextSample()
  inline void SetNoSoundMode(flag fNewNoSoundMode)                                                 { fNoSoundMode = fNewNoSoundMode; }

};
