/*
Generates random sounds in the APU, printing the parameters
to the screen. Also shows an asterisk while each channel is
playing, i.e. while its length counter is active.
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "neslib.h"

#include "apu.h"
//#link "apu.c"

#include "vrambuf.h"
//#link "vrambuf.c"

// link the pattern table into CHR ROM
//#link "chr_generic.s"

typedef struct APUParam {
  byte chmask;
  const char* name;
  word valmask;
} APUParam;

#define APU_DEFCOUNT 20

const APUParam APU_DEFS[APU_DEFCOUNT] = {
  {0x01, "Pulse1 Period",	0x7ff },
  {0x01, "Pulse1 Duty",		0xc0 },
  {0x01, "Pulse1 Decay",	0x0f },
  {0x01, "Pulse1 Length",	0x0f },
  {0x01, "Pulse1 Sweep Period",	0x07 },
  {0x01, "Pulse1 Sweep Rate",	0x07 },
  {0x01, "Pulse1 Sweep Up?",	0x01 },
  {0x02, "Pulse2 Period",	0x7ff },
  {0x02, "Pulse2 Duty",		0xc0 },
  {0x02, "Pulse2 Decay",	0x0f },
  {0x02, "Pulse2 Length",	0x0f },
  {0x02, "Pulse2 Sweep Period",	0x07 },
  {0x02, "Pulse2 Sweep Rate",	0x07 },
  {0x02, "Pulse2 Sweep Up?",	0x01 },
  {0x04, "Triangle Period",	0x7ff },
  {0x04, "Triangle Length",	0x0f },
  {0x08, "Noise Period",	0x0f },
  {0x08, "Noise Decay",		0x0f },
  {0x08, "Noise Length",	0x0f },
  {0x08, "Noise Buzz",		NOISE_PERIOD_BUZZ },
};

word enmask;
word vals[APU_DEFCOUNT];

void random_sound() {
  byte i;
  
  do {
    enmask = rand() & 15; // all except DMC
  } while (enmask == 0);
  
  for (i=0; i<APU_DEFCOUNT; i++) {
    vals[i] = rand() & APU_DEFS[i].valmask;
  }
}

void print_sound() {
  byte i;
  char buf[32];
  for (i=0; i<APU_DEFCOUNT; i++) {
    memset(buf, 0, sizeof(buf));
    if (enmask & APU_DEFS[i].chmask) {
      sprintf(buf, "%2d %19s %5d", i, APU_DEFS[i].name, vals[i]);
    }
    vram_adr(NTADR_A(1,i+1));
    vram_write(buf, 32);
  }
}

void play_sound() {
  APU_ENABLE(enmask);
  APU_PULSE_DECAY(0, vals[0], vals[1], vals[2], vals[3]);
  APU_PULSE_SWEEP(0, vals[4], vals[5], vals[6]);
  APU_PULSE_DECAY(1, vals[7], vals[8], vals[9], vals[10]);
  APU_PULSE_SWEEP(1, vals[11], vals[12], vals[13]);
  APU_TRIANGLE_LENGTH(vals[14], vals[15]);
  APU_NOISE_DECAY(vals[16]|vals[19], vals[17], vals[18]);
}

void print_status() {
  byte i;
  vrambuf_clear();
  for (i=0; i<APU_DEFCOUNT; i++) {
    char ch = APU.status & APU_DEFS[i].chmask ? '*' : ' ';
    vrambuf_put(NTADR_A(29,i+1), &ch, 1);
  }
}


void main(void)
{
  pal_col(1,0x04);
  pal_col(2,0x20);
  pal_col(3,0x30);
  vram_adr(NTADR_A(0,26));
  vram_write(" Space=New Sound, Enter=Replay ", 32);
  apu_init();
  while(1) {
    ppu_off();
    if (!(pad_state(0) & PAD_START)) {
      random_sound();
    }
    print_sound();
    play_sound();
    vrambuf_clear();
    set_vram_update(updbuf);
    ppu_on_all();
    // wait for key
    while (!pad_trigger(0)) {
      ppu_wait_nmi();
      print_status();
    }
  }
}