/*******************************************************************
   Wave-base Sound Generator (3ch. Polyphonic) a.k.a. "Namco-WSG"

          Written by Tsuyoshi HASEGAWA <t-haseg@lares.dti.ne.jp>
********************************************************************/
module WSG_3CH(

   input       DACCLK,        // 25MHz~ (for DSDAC)
   input       WSGCLKx4,      // 96KHz  (Output sampling rate: 24KHz)
   input       RESET,

   input       CPUCLK,        // CPU -> Registers interface
   input [4:0] ADRS,
   input [3:0] DATA,
   input       WR,

   output [7:0] WROMADR,      // Wave-ROM interface
   input  [3:0] WROMDAT,

   input  [7:0] AUX,          // AUX PCM input

   output      OUT_L,         // Analog stereo output (DSDAC output)
   output      OUT_R,

   input [5:0] VOLUME         // Master Volume (0~63)
);

/** Registers ************************************/

reg  [2:0] W0, W1, W2;
reg  [3:0] V0, V1, V2;

reg [19:0] F0;
reg [15:0] F1, F2;

always @ ( posedge CPUCLK or posedge RESET ) begin

   if ( RESET ) begin

      W0 <= 0;
      W1 <= 0;
      W2 <= 0;

      F0 <= 0;
      F1 <= 0;
      F2 <= 0;

      V0 <= 0;
      V1 <= 0;
      V2 <= 0;

   end
   else begin

      if ( WR ) case ( ADRS )

      5'h05: W0 <= DATA[2:0];
      5'h0A: W1 <= DATA[2:0];
      5'h0F: W2 <= DATA[2:0];

      5'h10: F0[3:0]   <= DATA;
      5'h11: F0[7:4]   <= DATA;
      5'h12: F0[11:8]  <= DATA;
      5'h13: F0[15:12] <= DATA;
      5'h14: F0[19:16] <= DATA;
      5'h15: V0 <= DATA;

      5'h16: F1[3:0]   <= DATA;
      5'h17: F1[7:4]   <= DATA;
      5'h18: F1[11:8]  <= DATA;
      5'h19: F1[15:12] <= DATA;
      5'h1A: V1 <= DATA;

      5'h1B: F2[3:0]   <= DATA;
      5'h1C: F2[7:4]   <= DATA;
      5'h1D: F2[11:8]  <= DATA;
      5'h1E: F2[15:12] <= DATA;
      5'h1F: V2 <= DATA;

      default:;

      endcase

   end

end


/** Sound Generator(s) ****************************/

reg  [1:0] phase;

reg [19:0] c0;
reg [15:0] c1, c2;

reg  [7:0] waveadr;
assign WROMADR = waveadr;

reg   [3:0] wavevol;
wire  [7:0] waveout = wavevol * WROMDAT;

reg   [9:0] sndmix;
wire [10:0] sndmixdown = { 1'b0, sndmix } + { 1'b0, AUX, 2'b00 };
reg   [7:0] sndout;

reg   [4:0] c11, c21;

always @ ( posedge WSGCLKx4 or posedge RESET ) begin

   if ( RESET ) begin

      phase  <= 0;
      sndout <= 0;

      c0 <= 0;
      c1 <= 0;
      c2 <= 0;

   end
   else begin

      phase <= phase+1;

      c0 <= c0 + F0;
      c1 <= c1 + F1;
      c2 <= c2 + F2;

      case ( phase )

      0: begin
            sndout  <= ( sndmixdown[9:2] | {8{sndmixdown[10]}} );
            sndmix  <= 0;

            c11 <= c1[15:11];
            c21 <= c2[15:11];

            waveadr <= { W0, c0[19:15] };
            wavevol <= V0;
         end

      1: begin
            sndmix <= sndmix + waveout;

            waveadr <= { W1, c11 };
            wavevol <= V1;
         end

      2: begin
            sndmix <= sndmix + waveout;

            waveadr <= { W2, c21 };
            wavevol <= V2;
         end

      3: begin
            sndmix <= sndmix + waveout;
         end

      default:;

      endcase

   end

end


/** Stereo Reverb & DAC *************************/

wire outclk = phase[1];

wire [7:0] delayI = sndout;
wire [7:0] delayO;

reg  [9:0] dacinL, dacinR;

always @ ( posedge outclk ) begin
   dacinL <= (({ sndout, 2'b00 }) * VOLUME ) / 64;
   dacinR <= (({{ 1'b0, sndout, 1'b0 } + { 2'b0, delayO[7:1] }}) * VOLUME ) / 64;
end

DELAY_FIFO snd_delay( outclk, delayI, delayO );

DSDAC dacL( RESET, DACCLK, dacinL, OUT_L );
DSDAC dacR( RESET, DACCLK, dacinR, OUT_R );


endmodule


//-----------------------------------------------
//  FIFO for Sound delay
//-----------------------------------------------
module DELAY_FIFO( CLKi, DATAi, DATAo );

input          CLKi;
input    [7:0] DATAi;
output   [7:0] DATAo;

parameter ADRSBITS = 11;

reg   [(ADRSBITS-1):0] ADRSi;
reg   [7:0] fifocore [0:((1<<ADRSBITS)-1)];
reg   [7:0] DATAo;

always @( posedge CLKi ) begin
   fifocore[ADRSi] <= DATAi;
   DATAo <= fifocore[ADRSi+8];
   ADRSi <= ADRSi + 1;
end

endmodule


//-----------------------------------------------
//  Delta-Sigma DAC
//-----------------------------------------------
module DSDAC( Reset, Clk, DACin, DACout );

`define MSBI 9              // Most significant Bit of DAC input

input  Reset;
input  Clk;
input [`MSBI:0] DACin;      // DAC input (excess 2**MSBI)
output DACout;              // DAC output
reg    DACout;

reg [`MSBI+2:0] DeltaAdder; // Output of Delta adder
reg [`MSBI+2:0] SigmaAdder; // Output of Sigma adder
reg [`MSBI+2:0] SigmaLatch; // Latches output of Sigma adder
reg [`MSBI+2:0] DeltaB;     // B input of Delta adder

always @(SigmaLatch)
   DeltaB = {SigmaLatch[`MSBI+2], SigmaLatch[`MSBI+2]} << (`MSBI+1);

always @(DACin or DeltaB)           DeltaAdder = DACin + DeltaB;
always @(DeltaAdder or SigmaLatch)  SigmaAdder = DeltaAdder + SigmaLatch;

always @( posedge Clk or posedge Reset ) begin
   if ( Reset ) begin
      SigmaLatch <= 1'b1 << (`MSBI+1);
      DACout     <= 1'b0;
   end else begin
      SigmaLatch <= SigmaAdder;
      DACout     <= SigmaLatch[`MSBI+2];
   end
end

endmodule
