#include <gb.h>
#include <stdio.h>
#include <console.h>

#define ARROW_CHAR 3
#define SPACE_CHAR ' '

#define ARROW_X    0
#define VAL_X      15
#define TITLE_Y    0
#define FIRST_X    (ARROW_X+1)
#define FIRST_Y    (TITLE_Y+2)

#define PLAY       0x20
#define FREQUENCY  0x21

#define MIN(x,y) ((x) > (y) ? (y) : (x))
#define MAX(x,y) ((x) < (y) ? (y) : (x))

#define NB_MODES   5

enum notes {
  C0, Cd0, D0, Dd0, E0, F0, Fd0, G0, Gd0, A0, Ad0, B0,
  C1, Cd1, D1, Dd1, E1, F1, Fd1, G1, Gd1, A1, Ad1, B1,
  C2, Cd2, D2, Dd2, E2, F2, Fd2, G2, Gd2, A2, Ad2, B2,
  C3, Cd3, D3, Dd3, E3, F3, Fd3, G3, Gd3, A3, Ad3, B3,
  C4, Cd4, D4, Dd4, E4, F4, Fd4, G4, Gd4, A4, Ad4, B4,
  C5, Cd5, D5, Dd5, E5, F5, Fd5, G5, Gd5, A5, Ad5, B5,
  SILENCE, END
};

UWORD frequencies[] = {
  44, 156, 262, 363, 457, 547, 631, 710, 786, 854, 923, 986,
  1046, 1102, 1155, 1205, 1253, 1297, 1339, 1379, 1417, 1452, 1486, 1517,
  1546, 1575, 1602, 1627, 1650, 1673, 1694, 1714, 1732, 1750, 1767, 1783,
  1798, 1812, 1825, 1837, 1849, 1860, 1871, 1881, 1890, 1899, 1907, 1915,
  1923, 1930, 1936, 1943, 1949, 1954, 1959, 1964, 1969, 1974, 1978, 1982,
  1985, 1988, 1992, 1995, 1998, 2001, 2004, 2006, 2009, 2011, 2013, 2015
};

UBYTE music[] = {
  C3, C3, G3, G3, A3, A3, G3, SILENCE,
  F3, F3, E3, E3, D3, D3, C3, SILENCE,
  G3, G3, F3, F3, E3, E3, D3, D3,
  G3, G3, F3, F3, E3, E3, D3, D3,
  C3, C3, G3, G3, A3, A3, G3, SILENCE,
  F3, F3, E3, E3, D3, D3, C3, SILENCE,
  END
};

struct Params {
  char *name;
  UWORD max;
};

struct Params params_0[] = {
  { "Main Controls" , 0    },
  { "All On/Off"    , 1    },
  { "Vin->SO1"      , 1    },
  { "Vin->SO2"      , 1    },
  { "SO1 Volume"    , 7    },
  { "SO2 Volume"    , 7    },
  { NULL            , 0    }
};

struct Params params_1[] = {
  { "Sound Mode #1" , 0    },
  { "Swp Time"      , 7    },
  { "Swp Mode"      , 1    },
  { "Swp Shifts"    , 7    },
  { "Pat Duty"      , 3    },
  { "Sound Len"     , 63   },
  { "Env Init"      , 15   },
  { "Env Mode"      , 1    },
  { "Env Nb Swp"    , 7    },
  { "Frequency"     , 2047 },
  { "Cons Sel"      , 1    },
  { "Out to SO1"    , 1    },
  { "Out to SO2"    , 1    },
  { "On/Off"        , 1    },
  { NULL            , 0    }
};

struct Params params_2[] = {
  { "Sound Mode #2" , 0    },
  { "Pat Duty"      , 3    },
  { "Sound Len"     , 63   },
  { "Env Init"      , 15   },
  { "Env Mode"      , 1    },
  { "Env Nb Step"   , 7    },
  { "Frequency"     , 2047 },
  { "Cons Sel"      , 1    },
  { "Out to SO1"    , 1    },
  { "Out to SO2"    , 1    },
  { "On/Off"        , 1    },
  { NULL            , 0    }
};

struct Params params_3[] = {
  { "Sound Mode #3" , 0    },
  { "Sound On/Off"  , 1    },
  { "Sound Len"     , 255  },
  { "Sel Out Level" , 3    },
  { "Frequency"     , 2047 },
  { "Cons Sel"      , 1    },
  { "Out to SO1"    , 1    },
  { "Out to SO2"    , 1    },
  { "On/Off"        , 1    },
  { NULL            , 0    }
};

struct Params params_4[] = {
  { "Sound Mode #4" , 0    },
  { "Sound Len"     , 63   },
  { "Env Init"      , 15   },
  { "Env Mode"      , 1    },
  { "Env Nb Step"   , 7    },
  { "Poly Cnt Freq" , 15   },
  { "Poly Cnt Step" , 1    },
  { "Poly Cnt Div"  , 7    },
  { "Cons Sel"      , 1    },
  { "Out to SO1"    , 1    },
  { "Out to SO2"    , 1    },
  { "On/Off"        , 1    },
  { NULL            , 0    }
};

struct Params *params_array[] = {
  params_0,
  params_1,
  params_2,
  params_3,
  params_4,
};

struct Params *params;

struct SoundReg {
  struct {
    /* 0xFF10 */
    UBYTE sweepShifts     : 3;
    UBYTE sweepMode       : 1;
    UBYTE sweepTime       : 3;
    UBYTE unused_1        : 1;

    /* 0xFF11 */
    UBYTE soundLength     : 6;
    UBYTE patternDuty     : 2;

    /* 0xFF12 */
    UBYTE envNbSweep      : 3;
    UBYTE envMode         : 1;
    UBYTE envInitialValue : 4;

    /* 0xFF13 */
    UBYTE frequencyLow;

    /* 0xFF14 */
    UBYTE frequencyHigh   : 3;
    UBYTE unused_2        : 3;
    UBYTE counter_ConsSel : 1;
    UBYTE restart         : 1;
  } mode1;
  struct {
    /* 0xFF15 */
    UBYTE unused_1;

    /* 0xFF16 */
    UBYTE soundLength     : 6;
    UBYTE patternDuty     : 2;

    /* 0xFF17 */
    UBYTE envNbStep       : 3;
    UBYTE envMode         : 1;
    UBYTE envInitialValue : 4;

    /* 0xFF18 */
    UBYTE frequencyLow;

    /* 0xFF19 */
    UBYTE frequencyHigh   : 3;
    UBYTE unused_2        : 3;
    UBYTE counter_ConsSel : 1;
    UBYTE restart         : 1;
  } mode2;
  struct {
    /* 0xFF1A */
    UBYTE unused_1        : 7;
    UBYTE on_Off          : 1;

    /* 0xFF1B */
    UBYTE soundLength;

    /* 0xFF1C */
    UBYTE unused_2        : 5;
    UBYTE selOutputLevel  : 2;
    UBYTE unused_3        : 1;

    /* 0xFF1D */
    UBYTE frequencyLow;

    /* 0xFF1E */
    UBYTE frequencyHigh   : 3;
    UBYTE unused_4        : 3;
    UBYTE counter_ConsSel : 1;
    UBYTE restart         : 1;
  } mode3;
  struct {
    /* 0xFF1F */
    UBYTE unused_1;

    /* 0xFF20 */
    UBYTE soundLength     : 6;
    UBYTE unused_2        : 2;

    /* 0xFF21 */
    UBYTE envNbStep       : 3;
    UBYTE envMode         : 1;
    UBYTE envInitialValue : 4;

    /* 0xFF22 */
    UBYTE polyCounterDiv  : 3;
    UBYTE polyCounterStep : 1;
    UBYTE polyCounterFreq : 4;

    /* 0xFF23 */
    UBYTE unused_3        : 6;
    UBYTE counter_ConsSel : 1;
    UBYTE restart         : 1;
  } mode4;
  struct {
    /* 0xFF24 */
    UBYTE SO1_OutputLevel : 3;
    UBYTE Vin_SO1         : 1;
    UBYTE SO2_OutputLevel : 3;
    UBYTE Vin_SO2         : 1;

    /* 0xFF25 */
    UBYTE Sound1_To_SO1   : 1;
    UBYTE Sound2_To_SO1   : 1;
    UBYTE Sound3_To_SO1   : 1;
    UBYTE Sound4_To_SO1   : 1;
    UBYTE Sound1_To_SO2   : 1;
    UBYTE Sound2_To_SO2   : 1;
    UBYTE Sound3_To_SO2   : 1;
    UBYTE Sound4_To_SO2   : 1;

    /* 0xFF26 */
    UBYTE Sound1_On_Off   : 1;
    UBYTE Sound2_On_Off   : 1;
    UBYTE Sound3_On_Off   : 1;
    UBYTE Sound4_On_Off   : 1;
    UBYTE unused_1        : 3;
    UBYTE global_On_Off   : 1;
  } control;
};

struct SoundReg *soundReg;

UWORD current_value(UBYTE mode, UBYTE line)
{
  if(mode == 0) {
    switch(line)
      {
      case 0: /* global_On_Off */
	return soundReg->control.global_On_Off;
      case 1: /* Vin_SO1 */
	return soundReg->control.Vin_SO1;
      case 2: /* Vin_SO2 */
	return soundReg->control.Vin_SO2;
      case 3: /* SO1_OutputLevel */
	return soundReg->control.SO1_OutputLevel;
      case 4: /* SO2_OutputLevel */
	return soundReg->control.SO2_OutputLevel;
      }
  } else if(mode == 1) {
    switch(line)
      {
      case 0: /* sweepTime */
	return soundReg->mode1.sweepTime;
      case 1: /* sweepMode */
	return soundReg->mode1.sweepMode;
      case 2: /* sweepShifts */
	return soundReg->mode1.sweepShifts;
      case 3: /* patternDuty */
	return soundReg->mode1.patternDuty;
      case 4: /* soundLength */
	return soundReg->mode1.soundLength;
      case 5: /* envInitialValue */
	return soundReg->mode1.envInitialValue;
      case 6: /* envMode */
	return soundReg->mode1.envMode;
      case 7: /* envNbSweep */
	return soundReg->mode1.envNbSweep;
      case 8: /* frequency */
      case FREQUENCY:
	return ((UWORD)soundReg->mode1.frequencyHigh << 8) +
	  (UWORD)soundReg->mode1.frequencyLow;
      case 9: /* counter_ConsSel */
	return soundReg->mode1.counter_ConsSel;
      case 10: /* Sound1_To_SO1 */
	return soundReg->control.Sound1_To_SO1;
      case 11: /* Sound1_To_SO2 */
	return soundReg->control.Sound1_To_SO2;
      case 12: /* Sound1_On_Off */
	return soundReg->control.Sound1_On_Off;
      }
  } else if(mode == 2) {
    switch(line)
      {
      case 0: /* patternDuty */
	return soundReg->mode2.patternDuty;
      case 1: /* soundLength */
	return soundReg->mode2.soundLength;
      case 2: /* envInitialValue */
	return soundReg->mode2.envInitialValue;
      case 3: /* envMode */
	return soundReg->mode2.envMode;
      case 4: /* envNbStep */
	return soundReg->mode2.envNbStep;
      case 5: /* frequency */
      case FREQUENCY:
	return ((UWORD)soundReg->mode2.frequencyHigh << 8) +
	  (UWORD)soundReg->mode2.frequencyLow;
      case 6: /* counter_ConsSel */
	return soundReg->mode2.counter_ConsSel;
      case 7: /* Sound2_To_SO1 */
	return soundReg->control.Sound2_To_SO1;
      case 8: /* Sound2_To_SO2 */
	return soundReg->control.Sound2_To_SO2;
      case 9: /* Sound2_On_Off */
	return soundReg->control.Sound2_On_Off;
      }
  } else if(mode == 3) {
    switch(line)
      {
      case 0: /* on_Off */
	return soundReg->mode3.on_Off;
      case 1: /* soundLength */
	return soundReg->mode3.soundLength;
      case 2: /* selOutputLevel */
	return soundReg->mode3.selOutputLevel;
      case 3: /* frequency */
      case FREQUENCY:
	return ((UWORD)soundReg->mode3.frequencyHigh << 8) +
	  (UWORD)soundReg->mode3.frequencyLow;
      case 4: /* counter_ConsSel */
	return soundReg->mode3.counter_ConsSel;
      case 5: /* Sound3_To_SO1 */
	return soundReg->control.Sound3_To_SO1;
      case 6: /* Sound3_To_SO2 */
	return soundReg->control.Sound3_To_SO2;
      case 7: /* Sound3_On_Off */
	return soundReg->control.Sound3_On_Off;
      }
  } else if(mode == 4) {
    switch(line)
      {
      case 0: /* soundLength */
	return soundReg->mode4.soundLength;
      case 1: /* envInitialValue */
	return soundReg->mode4.envInitialValue;
      case 2: /* envMode */
	return soundReg->mode4.envMode;
      case 3: /* envNbStep */
	return soundReg->mode4.envNbStep;
      case 4: /* polyCounterFreq */
	return soundReg->mode4.polyCounterFreq;
      case 5: /* polyCounterStep */
	return soundReg->mode4.polyCounterStep;
      case 6: /* polyCounterDiv */
	return soundReg->mode4.polyCounterDiv;
      case 7: /* counter_ConsSel */
	return soundReg->mode4.counter_ConsSel;
      case 8: /* Sound4_To_SO1 */
	return soundReg->control.Sound4_To_SO1;
      case 9: /* Sound4_To_SO2 */
	return soundReg->control.Sound4_To_SO2;
      case 10: /* Sound4_On_Off */
	return soundReg->control.Sound4_On_Off;
      }
  }
  return 0;
}

void update_value(UBYTE mode, UBYTE line, UWORD value)
{
  if(mode == 0) {
    switch(line)
      {
      case 0: /* global_On_Off */
	soundReg->control.global_On_Off = value;
	NR52_REG = ((UBYTE *)soundReg)[0x16];
	break;
      case 1: /* Vin_SO1 */
	soundReg->control.Vin_SO1 = value;
	NR50_REG = ((UBYTE *)soundReg)[0x14];
	break;
      case 2: /* Vin_SO2 */
	soundReg->control.Vin_SO2 = value;
	NR50_REG = ((UBYTE *)soundReg)[0x14];
	break;
      case 3: /* SO1_OutputLevel */
	soundReg->control.SO1_OutputLevel = value;
	NR50_REG = ((UBYTE *)soundReg)[0x14];
	break;
      case 4: /* SO2_OutputLevel */
	soundReg->control.SO2_OutputLevel = value;
	NR50_REG = ((UBYTE *)soundReg)[0x14];
	break;
      case FREQUENCY:
	update_value(1, FREQUENCY, value);
	update_value(2, FREQUENCY, value);
	update_value(3, FREQUENCY, value);
	break;
      case PLAY: /* restart */
	update_value(1, FREQUENCY, current_value(1, FREQUENCY));
	update_value(2, FREQUENCY, current_value(2, FREQUENCY));
	update_value(3, FREQUENCY, current_value(3, FREQUENCY));
	soundReg->mode1.restart = value;
	soundReg->mode2.restart = value;
	soundReg->mode3.restart = value;
	soundReg->mode4.restart = value;
	NR14_REG = ((UBYTE *)soundReg)[0x04];
	NR24_REG = ((UBYTE *)soundReg)[0x09];
	NR34_REG = ((UBYTE *)soundReg)[0x0E];
	NR44_REG = ((UBYTE *)soundReg)[0x13];
	soundReg->mode1.restart = 0;
	soundReg->mode2.restart = 0;
	soundReg->mode3.restart = 0;
	soundReg->mode4.restart = 0;
	break;
      }
  } else if(mode == 1) {
    switch(line)
      {
      case 0: /* sweepTime */
	soundReg->mode1.sweepTime = value;
	NR10_REG = ((UBYTE *)soundReg)[0x00];
	break;
      case 1: /* sweepMode */
	soundReg->mode1.sweepMode = value;
	NR10_REG = ((UBYTE *)soundReg)[0x00];
	break;
      case 2: /* sweepShifts */
	soundReg->mode1.sweepShifts = value;
	NR10_REG = ((UBYTE *)soundReg)[0x00];
	break;
      case 3: /* patternDuty */
	soundReg->mode1.patternDuty = value;
	NR11_REG = ((UBYTE *)soundReg)[0x01];
	break;
      case 4: /* soundLength */
	soundReg->mode1.soundLength = value;
	NR11_REG = ((UBYTE *)soundReg)[0x01];
	break;
      case 5: /* envInitialValue */
	soundReg->mode1.envInitialValue = value;
	NR12_REG = ((UBYTE *)soundReg)[0x02];
	break;
      case 6: /* envMode */
	soundReg->mode1.envMode = value;
	NR12_REG = ((UBYTE *)soundReg)[0x02];
	break;
      case 7: /* envNbSweep */
	soundReg->mode1.envNbSweep = value;
	NR12_REG = ((UBYTE *)soundReg)[0x02];
	break;
      case 8: /* frequency */
      case FREQUENCY:
	soundReg->mode1.frequencyHigh = value >> 8;
	soundReg->mode1.frequencyLow  = value;
	NR13_REG = ((UBYTE *)soundReg)[0x03];
	NR14_REG = ((UBYTE *)soundReg)[0x04];
	break;
      case 9: /* counter_ConsSel */
	soundReg->mode1.counter_ConsSel = value;
	NR14_REG = ((UBYTE *)soundReg)[0x04];
	break;
      case 10: /* Sound1_To_SO1 */
	soundReg->control.Sound1_To_SO1 = value;
	NR51_REG = ((UBYTE *)soundReg)[0x15];
	break;
      case 11: /* Sound1_To_SO2 */
	soundReg->control.Sound1_To_SO2 = value;
	NR51_REG = ((UBYTE *)soundReg)[0x15];
	break;
      case 12: /* Sound1_On_Off */
	soundReg->control.Sound1_On_Off = value;
	NR52_REG = ((UBYTE *)soundReg)[0x16];
	break;
      case PLAY: /* restart */
	update_value(mode, FREQUENCY, current_value(mode, FREQUENCY));
	soundReg->mode1.restart = value;
	NR14_REG = ((UBYTE *)soundReg)[0x04];
	soundReg->mode1.restart = 0;
	break;
      }
  } else if(mode == 2) {
    switch(line)
      {
      case 0: /* patternDuty */
	soundReg->mode2.patternDuty = value;
	NR21_REG = ((UBYTE *)soundReg)[0x06];
	break;
      case 1: /* soundLength */
	soundReg->mode2.soundLength = value;
	NR21_REG = ((UBYTE *)soundReg)[0x06];
	break;
      case 2: /* envInitialValue */
	soundReg->mode2.envInitialValue = value;
	NR22_REG = ((UBYTE *)soundReg)[0x07];
	break;
      case 3: /* envMode */
	soundReg->mode2.envMode = value;
	NR22_REG = ((UBYTE *)soundReg)[0x07];
	break;
      case 4: /* envNbStep */
	soundReg->mode2.envNbStep = value;
	NR22_REG = ((UBYTE *)soundReg)[0x07];
	break;
      case 5: /* frequency */
      case FREQUENCY:
	soundReg->mode2.frequencyHigh = value >> 8;
	soundReg->mode2.frequencyLow  = value;
	NR23_REG = ((UBYTE *)soundReg)[0x08];
	NR24_REG = ((UBYTE *)soundReg)[0x09];
	break;
      case 6: /* counter_ConsSel */
	soundReg->mode2.counter_ConsSel = value;
	NR24_REG = ((UBYTE *)soundReg)[0x09];
	break;
      case 7: /* Sound2_To_SO1 */
	soundReg->control.Sound2_To_SO1 = value;
	NR51_REG = ((UBYTE *)soundReg)[0x15];
	break;
      case 8: /* Sound2_To_SO2 */
	soundReg->control.Sound2_To_SO2 = value;
	NR51_REG = ((UBYTE *)soundReg)[0x15];
	break;
      case 9: /* Sound2_On_Off */
	soundReg->control.Sound2_On_Off = value;
	NR52_REG = ((UBYTE *)soundReg)[0x16];
	break;
      case PLAY: /* restart */
	update_value(mode, FREQUENCY, current_value(mode, FREQUENCY));
	soundReg->mode2.restart = value;
	NR24_REG = ((UBYTE *)soundReg)[0x09];
	soundReg->mode2.restart = 0;
	break;
      }
  } else if(mode == 3) {
    switch(line)
      {
      case 0: /* on_Off */
	soundReg->mode3.on_Off = value;
	NR30_REG = ((UBYTE *)soundReg)[0x0A];
	break;
      case 1: /* soundLength */
	soundReg->mode3.soundLength = value;
	NR31_REG = ((UBYTE *)soundReg)[0x0B];
	break;
      case 2: /* selOutputLevel */
	soundReg->mode3.selOutputLevel = value;
	NR32_REG = ((UBYTE *)soundReg)[0x0C];
	break;
      case 3: /* frequency */
      case FREQUENCY:
	soundReg->mode3.frequencyHigh = value >> 8;
	soundReg->mode3.frequencyLow  = value;
	NR33_REG = ((UBYTE *)soundReg)[0x0D];
	NR34_REG = ((UBYTE *)soundReg)[0x0E];
	break;
      case 4: /* counter_ConsSel */
	soundReg->mode3.counter_ConsSel = value;
	NR34_REG = ((UBYTE *)soundReg)[0x0E];
	break;
      case 5: /* Sound3_To_SO1 */
	soundReg->control.Sound3_To_SO1 = value;
	NR51_REG = ((UBYTE *)soundReg)[0x15];
	break;
      case 6: /* Sound3_To_SO2 */
	soundReg->control.Sound3_To_SO2 = value;
	NR51_REG = ((UBYTE *)soundReg)[0x15];
	break;
      case 7: /* Sound3_On_Off */
	soundReg->control.Sound3_On_Off = value;
	NR52_REG = ((UBYTE *)soundReg)[0x16];
	break;
      case PLAY: /* restart */
	update_value(mode, FREQUENCY, current_value(mode, FREQUENCY));
	soundReg->mode3.restart = value;
	NR34_REG = ((UBYTE *)soundReg)[0x0E];
	soundReg->mode3.restart = 0;
	break;
      }
  } else if(mode == 4) {
    switch(line)
      {
      case 0: /* soundLength */
	soundReg->mode4.soundLength = value;
	NR41_REG = ((UBYTE *)soundReg)[0x10];
	break;
      case 1: /* envInitialValue */
	soundReg->mode4.envInitialValue = value;
	NR42_REG = ((UBYTE *)soundReg)[0x11];
	break;
      case 2: /* envMode */
	soundReg->mode4.envMode = value;
	NR42_REG = ((UBYTE *)soundReg)[0x11];
	break;
      case 3: /* envNbStep */
	soundReg->mode4.envNbStep = value;
	NR42_REG = ((UBYTE *)soundReg)[0x11];
	break;
      case 4: /* polyCounterFreq */
	soundReg->mode4.polyCounterFreq = value;
	NR43_REG = ((UBYTE *)soundReg)[0x12];
	break;
      case 5: /* polyCounterStep */
	soundReg->mode4.polyCounterStep = value;
	NR43_REG = ((UBYTE *)soundReg)[0x12];
	break;
      case 6: /* polyCounterDiv */
	soundReg->mode4.polyCounterDiv = value;
	NR43_REG = ((UBYTE *)soundReg)[0x12];
	break;
      case 7: /* counter_ConsSel */
	soundReg->mode4.counter_ConsSel = value;
	NR44_REG = ((UBYTE *)soundReg)[0x13];
	break;
      case 8: /* Sound4_To_SO1 */
	soundReg->control.Sound4_To_SO1 = value;
	NR51_REG = ((UBYTE *)soundReg)[0x15];
	break;
      case 9: /* Sound4_To_SO2 */
	soundReg->control.Sound4_To_SO2 = value;
	NR51_REG = ((UBYTE *)soundReg)[0x15];
	break;
      case 10: /* Sound4_On_Off */
	soundReg->control.Sound4_On_Off = value;
	NR52_REG = ((UBYTE *)soundReg)[0x16];
	break;
      case PLAY: /* restart */
	soundReg->mode4.restart = value;
	NR44_REG = ((UBYTE *)soundReg)[0x13];
	soundReg->mode4.restart = 0;
	break;
      }
  }
}

UBYTE draw_screen(UBYTE mode)
{
  UBYTE i;

  cls();
  gotoxy(FIRST_X, TITLE_Y);
  print(params[0].name);

  for(i = 0; params[i+1].name; i++) {
    gotoxy(FIRST_X, FIRST_Y+i);
    print(params[i+1].name);
    gotoxy(VAL_X, FIRST_Y+i);
    println(current_value(mode, i), 10, UNSIGNED);
  }
  return i-1;
}


void play_music(UBYTE mode)
{
  UBYTE i = 0;

  while(music[i] != END) {
    if(music[i] != SILENCE) {
      update_value(mode, FREQUENCY, frequencies[music[i]]);
      update_value(mode, PLAY, 1);
    }
    delay(500);
    i++;
  }
}

void dump_registers()
{
  UBYTE reg;
  UBYTE i, j;

  cls();
  gotoxy(FIRST_X, TITLE_Y);
  print("Register Dump");

  for(i = 0, j = 0; i <= 0x16; i++, j++) {
    if(i == 0x05 || i == 0x0F)
      i++;
    if(j%2 == 0) {
      gotoxy(FIRST_X, FIRST_Y+j/2);
      print("0xFF");
    } else {
      gotoxy(FIRST_X+6, FIRST_Y+j/2);
      putchar('-');
    }
    printn(i+0x10, 16, UNSIGNED);
    if(j%2 == 0) {
      gotoxy(VAL_X, FIRST_Y+j/2);
    } else {
      gotoxy(VAL_X+2, FIRST_Y+j/2);
      putchar('-');
    }
    reg = ((UBYTE *)soundReg)[i];
    if(!(reg & 0xF0U)) putchar('0');
    printn(reg, 16, UNSIGNED);
  }
}

void wait_event(UBYTE mode)
{
  UBYTE input, y, last_y;
  UWORD l;

  while(1) {
    params = params_array[mode];
    last_y = draw_screen(mode) + FIRST_Y;
    y = FIRST_Y;
    gotoxy(ARROW_X, y);
    setchar(ARROW_CHAR);

    while(1) {
      input = joypad();
      if(input & J_UP) {
	gotoxy(ARROW_X, y); setchar(SPACE_CHAR);
	if(--y < FIRST_Y)
	  y = last_y;
	gotoxy(ARROW_X, y); setchar(ARROW_CHAR);
      } else if(input & J_DOWN) {
	gotoxy(ARROW_X, y); setchar(SPACE_CHAR);
	if(++y > last_y)
	  y = FIRST_Y;
	gotoxy(ARROW_X, y); setchar(ARROW_CHAR);
      } else if(input & J_LEFT) {
	l = current_value(mode, y-FIRST_Y);
	if(l > 0) {
	  if(input & J_A)
	    l = MAX(l, 10) - 10;
	  else if(input & J_B)
	    l = 0;
	  else
	    l--;
	  update_value(mode, y-FIRST_Y, l);
	  gotoxy(VAL_X, y); print("    ");
	  gotoxy(VAL_X, y); println(l, 10, UNSIGNED);
	}
      } else if(input & J_RIGHT) {
	l = current_value(mode, y-FIRST_Y);
	if(l < params[y-(FIRST_Y-1)].max) {
	  if(input & J_A)
	    l = MIN(l + 10, params[y-(FIRST_Y-1)].max);
	  else if(input & J_B)
	    l = params[y-(FIRST_Y-1)].max;
	  else
	    l++;
	  update_value(mode, y-FIRST_Y, l);
	  gotoxy(VAL_X, y); print("    ");
	  gotoxy(VAL_X, y); println(l, 10, UNSIGNED);
	}
      } else if(input & J_START) {
	if(input & J_A)
	  play_music(mode);
	else
	  update_value(mode, PLAY, 1);
	waitpadup();
      } else if(input & J_SELECT) {
	if(input & J_A)
	  dump_registers();
	else
	  mode = (mode+1) % NB_MODES;
	waitpadup();
	break;
      }
      delay(250);
    }
  }
}

void main()
{
  struct SoundReg s = {
    { 0, 0, 0, 0,
      1, 2,
      3, 0, 4,
      0x73U,
      6, 0, 0, 0 },
    { 0,
      1, 2,
      4, 0, 8,
      0xD7U,
      6, 0, 0, 0 },
    { 0, 1,
      0,
      0, 1, 0,
      0xD6U,
      6, 0, 0, 0 },
    { 0,
      58, 0,
      1, 0, 10,
      0, 0, 0,
      0, 1, 0 },
    { 7, 0, 7, 0,
      1, 1, 1, 1, 1, 1, 1, 1,
      0, 0, 0, 0, 0, 1 }
  };

  /*
   * Before modifying any sound register, sound must be turned on!
   * (it is turned off by default to save batteries)
   */
  NR52_REG = 0x80;

  soundReg = &s;
  NR10_REG = ((UBYTE *)soundReg)[0x00];
  NR11_REG = ((UBYTE *)soundReg)[0x01];
  NR12_REG = ((UBYTE *)soundReg)[0x02];
  NR13_REG = ((UBYTE *)soundReg)[0x03];
  NR14_REG = ((UBYTE *)soundReg)[0x04];

  NR21_REG = ((UBYTE *)soundReg)[0x06];
  NR22_REG = ((UBYTE *)soundReg)[0x07];
  NR23_REG = ((UBYTE *)soundReg)[0x08];
  NR24_REG = ((UBYTE *)soundReg)[0x09];

  NR30_REG = ((UBYTE *)soundReg)[0x0A];
  NR31_REG = ((UBYTE *)soundReg)[0x0B];
  NR32_REG = ((UBYTE *)soundReg)[0x0C];
  NR33_REG = ((UBYTE *)soundReg)[0x0D];
  NR34_REG = ((UBYTE *)soundReg)[0x0E];

  NR41_REG = ((UBYTE *)soundReg)[0x10];
  NR42_REG = ((UBYTE *)soundReg)[0x11];
  NR43_REG = ((UBYTE *)soundReg)[0x12];
  NR44_REG = ((UBYTE *)soundReg)[0x13];

  NR50_REG = ((UBYTE *)soundReg)[0x14];
  NR51_REG = ((UBYTE *)soundReg)[0x15];
  NR52_REG = ((UBYTE *)soundReg)[0x16];

  cls();

  wait_event(1);
}
