class aDSP : public DSP {
private:
  uint8 dspram[128];
  uint8 *spcram;

  uint32 dsp_counter;

  enum { BRR_END = 1, BRR_LOOP = 2 };

  uint8  readb (uint16 addr);
  uint16 readw (uint16 addr);
  void   writeb(uint16 addr, uint8  data);
  void   writew(uint16 addr, uint16 data);

public:
  static const uint16 rate_table[32];
  static const int16  gaussian_table[512];

  enum EnvelopeStates {
    ATTACK,
    DECAY,
    SUSTAIN,
    RELEASE,
    SILENCE
  };

  enum EnvelopeModes {
    DIRECT,
    LINEAR_DEC,
    EXP_DEC,
    LINEAR_INC,
    BENT_INC,

    FAST_ATTACK,
    RELEASE_DEC
  };

private:
  struct Status {
    //$0c,$1c
    int8   MVOLL, MVOLR;
    //$2c,$3c
    int8   EVOLL, EVOLR;
    //$4c,$5c
    uint8  KON, KOFF;
    //$6c
    uint8  FLG;
    //$7c
    uint8  ENDX;
    //$0d
    int8   EFB;
    //$2d,$3d,$4d
    uint8  PMON, NON, EON;
    //$5d
    uint8  DIR;
    //$6d,$7d
    uint8  ESA, EDL;

    //$xf
    int8   FIR[8];

    //internal variables
    uint8  kon, esa;

    int16  noise_ctr, noise_rate;
    uint16 noise_sample;

    uint16 echo_index, echo_length;
    int16  fir_buffer[2][8];
    uint8  fir_buffer_index;

    //functions
    bool soft_reset() { return !!(FLG & 0x80); }
    bool mute()       { return !!(FLG & 0x40); }
    bool echo_write() { return  !(FLG & 0x20); }
  } status;

  struct Voice {
    //$x0-$x1
    int8   VOLL, VOLR;
    //$x2-$x3
    int16  PITCH;
    //$x4
    uint8  SRCN;
    //$x5-$x7
    uint8  ADSR1, ADSR2, GAIN;
    //$x8-$x9
    uint8  ENVX, OUTX;

    //internal variables
    int16  pitch_ctr;

    int8   brr_index;
    uint16 brr_ptr;
    uint8  brr_header;
    bool   brr_looped;

    int16  brr_data[4];
    uint8  brr_data_index;

    int16  envx;
    uint16 env_ctr, env_rate, env_sustain;
    uint32 env_state;
    uint32 env_mode;

    int16  outx;

    //functions
    int16 pitch_rate()        { return PITCH & 0x3fff; }

    uint8 brr_header_shift()  { return brr_header >> 4; }
    uint8 brr_header_filter() { return (brr_header >> 2) & 3; }
    uint8 brr_header_flags()  { return brr_header & 3; }

    bool  ADSR_enabled()      { return !!(ADSR1 & 0x80); }
    uint8 ADSR_decay()        { return (ADSR1 >> 4) & 7; }
    uint8 ADSR_attack()       { return ADSR1 & 15; }
    uint8 ADSR_sus_level()    { return ADSR2 >> 5; }
    uint8 ADSR_sus_rate()     { return ADSR2 & 31; }

    void AdjustEnvelope() {
      if(env_state == SILENCE) {
        env_mode = DIRECT;
        env_rate = 0;
        envx     = 0;
      } else if(env_state == RELEASE) {
        env_mode = RELEASE_DEC;
        env_rate = 0x7800;
      } else if(ADSR_enabled()) {
        switch(env_state) {
        case ATTACK:
          env_rate = rate_table[(ADSR_attack() << 1) + 1];
          env_mode = (env_rate == 0x7800) ? FAST_ATTACK : LINEAR_INC;
          break;
        case DECAY:
          env_rate = rate_table[(ADSR_decay() << 1) + 0x10];
          env_mode = EXP_DEC;
          break;
        case SUSTAIN:
          env_rate = rate_table[ADSR_sus_rate()];
          env_mode = (env_rate == 0) ? DIRECT : EXP_DEC;
          break;
        }
      } else if(GAIN & 0x80) {
        switch(GAIN & 0x60) {
        case 0x00: env_mode = LINEAR_DEC; break;
        case 0x20: env_mode = EXP_DEC;    break;
        case 0x40: env_mode = LINEAR_INC; break;
        case 0x60: env_mode = BENT_INC;   break;
        }
        env_rate = rate_table[GAIN & 0x1f];
      } else {
        env_mode = DIRECT;
        env_rate = 0;
        envx     = (GAIN & 0x7f) << 4;
      }
    }
  } voice[8];

public:
  void enter();
  void run();

  uint8 read (uint8 addr);
  void write(uint8 addr, uint8 data);

  void power();
  void reset();

  void serialize(serializer&);
  aDSP();
  ~aDSP();

  friend class aDSPDebug;
};

#if defined(DEBUGGER)
  #include "debugger/debugger.hpp"
  extern aDSPDebugger dsp;
#else
  extern aDSP dsp;
#endif
