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;
  enum   EnvelopeStates env_state;
  enum   EnvelopeModes  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();

  aDSP();
  ~aDSP();
};
