//============================================================================
// 
//  SAA1099 sound generator
//  Copyright (C) 2016 Sorgelig
//
//  Based on SAA1099.v code from Miguel Angel Rodriguez Jodar
//  Based on SAASound code  from Dave Hooper
//
//  This program is free software; you can redistribute it and/or modify it
//  under the terms of the GNU General Public License as published by the Free
//  Software Foundation; either version 2 of the License, or (at your option)
//  any later version.
//
//  This program is distributed in the hope that it will be useful, but WITHOUT
//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
//  more details.
//
//  You should have received a copy of the GNU General Public License along
//  with this program; if not, write to the Free Software Foundation, Inc.,
//  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
//============================================================================
`timescale 1ns / 1ps
`default_nettype none


module saa1099
(
	input        clk_sys,
	input        ce,      // 8 MHz
	input        rst_n,
	input        cs_n,
	input        a0,      // 0=data, 1=address
	input        wr_n,
	input  [7:0] din,
	output [7:0] out_l,
	output [7:0] out_r
);

reg [7:0] amplit0, amplit1, amplit2, amplit3, amplit4, amplit5;
reg [7:0] freq0, freq1, freq2, freq3, freq4, freq5;
reg [7:0] oct10, oct32, oct54;
reg [7:0] freqenable;
reg [7:0] noiseenable;
reg [7:0] noisegen;
reg [7:0] envelope0, envelope1;
reg [7:0] ctrl;

reg [4:0] addr;
wire      rst = ~rst_n | ctrl[1];
reg       wr;

always @(posedge clk_sys) begin
	reg old_wr;
	old_wr <= wr_n;

	wr <= 0;
	if(~rst_n) begin
		addr <= 0;
		{amplit0, amplit1, amplit2, amplit3, amplit4, amplit5} <= 0;
		{freq0, freq1, freq2, freq3, freq4, freq5} <= 0;
		{oct10, oct32, oct54} <= 0;
		{freqenable, noiseenable, noisegen} <= 0;
		{envelope0, envelope1} <= 0;
		ctrl <= 0;
	end
	else begin
		if(!cs_n & old_wr & !wr_n) begin
			wr <= 1;
			if(a0) addr <= din[4:0];
			else begin
				case (addr)
					'h00: amplit0 <= din;
					'h01: amplit1 <= din;
					'h02: amplit2 <= din;
					'h03: amplit3 <= din;
					'h04: amplit4 <= din;
					'h05: amplit5 <= din;

					'h08: freq0   <= din;
					'h09: freq1   <= din;
					'h0A: freq2   <= din;
					'h0B: freq3   <= din;
					'h0C: freq4   <= din;
					'h0D: freq5   <= din;

					'h10: oct10   <= din;
					'h11: oct32   <= din;
					'h12: oct54   <= din;

					'h14: freqenable <= din;
					'h15: noiseenable<= din;
					'h16: noisegen   <= din;

					'h18: envelope0  <= din;
					'h19: envelope1  <= din;

					'h1C: ctrl       <= din;
				endcase
			end
		end
	end
end

wire [21:0] out0;
saa1099_triplet top
(
	.*,
	.vol('{amplit0, amplit1, amplit2}),
	.env(envelope0),

	.freq('{freq0, freq1, freq2}),
	.octave('{oct10[2:0], oct10[6:4], oct32[2:0]}),
	.freq_en(freqenable[2:0]),

	.noise_en(noiseenable[2:0]),
	.noise_freq(noisegen[1:0]),

	.wr_addr(wr &  a0 & (din[4:0] == 'h18)),
	.wr_data(wr & !a0 & (addr == 'h18)),

	.out(out0)
);

wire [21:0] out1;
saa1099_triplet bottom
(
	.*,
	.vol('{amplit3, amplit4, amplit5}),
	.env(envelope1),

	.freq('{freq3, freq4, freq5}),
	.octave('{oct32[6:4], oct54[2:0], oct54[6:4]}),
	.freq_en(freqenable[5:3]),

	.noise_en(noiseenable[5:3]),
	.noise_freq(noisegen[5:4]),

	.wr_addr(wr &  a0 & (din[4:0] == 'h19)),
	.wr_data(wr & !a0 & (addr == 'h19)),

	.out(out1)
);

saa1099_output_mixer outmix_l(.*, .en(ctrl[0]), .in0(out0[10:0]),  .in1(out1[10:0]),  .out(out_l));
saa1099_output_mixer outmix_r(.*, .en(ctrl[0]), .in0(out0[21:11]), .in1(out1[21:11]), .out(out_r));

endmodule

/////////////////////////////////////////////////////////////////////////////////
module saa1099_triplet
(
	input        rst,
	input        clk_sys,
	input        ce,

	input  [7:0] vol[3],
	input  [7:0] env,

	input  [7:0] freq[3],
	input  [2:0] octave[3],
	input  [2:0] freq_en,

	input  [2:0] noise_en,
	input  [1:0] noise_freq,

	input        wr_addr,
	input        wr_data,

	output[21:0] out
);

wire       tone0, tone1, tone2, noise;
wire       pulse_noise, pulse_envelope;
wire[21:0] out0, out1, out2;

saa1099_tone  freq_gen0(.*, .out(tone0), .octave(octave[0]), .freq(freq[0]), .pulse(pulse_noise));
saa1099_tone  freq_gen1(.*, .out(tone1), .octave(octave[1]), .freq(freq[1]), .pulse(pulse_envelope));
saa1099_tone  freq_gen2(.*, .out(tone2), .octave(octave[2]), .freq(freq[2]), .pulse());
saa1099_noise noise_gen(.*, .out(noise));

saa1099_amp amp0(.*, .mixmode({noise_en[0], freq_en[0]}), .tone(tone0), .envreg(0),   .vol(vol[0]), .out(out0));
saa1099_amp amp1(.*, .mixmode({noise_en[1], freq_en[1]}), .tone(tone1), .envreg(0),   .vol(vol[1]), .out(out1));
saa1099_amp amp2(.*, .mixmode({noise_en[2], freq_en[2]}), .tone(tone2), .envreg(env), .vol(vol[2]), .out(out2));

assign out[10:0]  = out0[8:0]  + out1[8:0]  + out2[8:0];
assign out[21:11] = out0[17:9] + out1[17:9] + out2[17:9];

endmodule

/////////////////////////////////////////////////////////////////////////////////

module saa1099_tone
(
	input       rst,
	input       clk_sys,
	input       ce,
	input [2:0] octave,
	input [7:0] freq,
	output reg  out,
	output reg  pulse
);

wire [16:0] fcount = ((17'd511 - freq) << (4'd8 - octave)) - 1'd1;
always @(posedge clk_sys) begin
	reg [16:0] count;

	pulse <= 0;
	if(rst) begin
		count <= fcount;
		out   <= 0;
	end else if(ce) begin
		if(!count) begin
			count <= fcount;
			pulse <= 1;
			out <= ~out;
		end else begin
			count <= count - 1'd1;
		end
	end
end

endmodule

/////////////////////////////////////////////////////////////////////////////////

module saa1099_noise
(
	input       rst,
	input       clk_sys,
	input       ce,
	input       pulse_noise,
	input [1:0] noise_freq,
	output      out
);

reg  [16:0] lfsr = 0;
wire [16:0] new_lfsr = {(lfsr[0] ^ lfsr[2] ^ !lfsr), lfsr[16:1]};
wire [10:0] fcount = (11'd256 << noise_freq) - 1'b1;

always @(posedge clk_sys) begin
	reg [10:0] count;

	if(rst) begin
		count <= fcount;
	end else
	if(noise_freq != 3) begin
		if(ce) begin
			if(!count) begin
				count <= fcount;
				lfsr <= new_lfsr;
			end else begin
				count <= count - 1'd1;
			end
		end
	end else if(pulse_noise) begin
		lfsr <= new_lfsr;
	end
end

assign out = lfsr[0];

endmodule

/////////////////////////////////////////////////////////////////////////////////

module saa1099_amp
(
	input             rst,
	input             clk_sys,
	input       [7:0] envreg,
	input       [1:0] mixmode,
	input             tone,
	input             noise,
	input             wr_addr,
	input             wr_data,
	input             pulse_envelope,
	input       [7:0] vol,
	output reg [17:0] out
);

wire       phases[8] = '{0,0,0,0,1,1,0,0};
wire [1:0] env[8][2] = '{'{0,0}, '{1,1}, '{2,0}, '{2,0}, '{3,2}, '{3,2}, '{3,0}, '{3,0}};
wire [3:0] levels[4][16] = 
'{
	'{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	'{15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15},
	'{15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
	'{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15}
};

reg [2:0] shape;
reg       stereo;
wire      resolution = envreg[4];
wire      enable     = envreg[7];
reg [3:0] counter;
reg       phase;
wire[3:0] mask = {3'b000, resolution};

always @(posedge clk_sys) begin
	reg clock;
	reg new_data;

	if(rst | ~enable) begin
		new_data <= 0;
		stereo   <= envreg[0];
		shape    <= envreg[3:1];
		clock    <= envreg[5];
		phase    <= 0;
		counter  <= 0;
	end
	else begin
		if(wr_data) new_data <= 1;
		if(clock ? wr_addr : pulse_envelope) begin  // pulse from internal or external clock?
			counter <= counter + resolution + 1'd1;
			if((counter | mask) == 15) begin
				if(phase >= phases[shape]) begin
					if(~shape[0]) counter <= 15;
					if(new_data | shape[0]) begin // if we reached one of the designated points (3) or (4) and there is pending data, load it
						new_data <= 0;
						stereo   <= envreg[0];
						shape    <= envreg[3:1];
						clock    <= envreg[5];
						phase    <= 0;
						if(new_data) counter <= 0;
					end
				end else begin
					phase <= 1;
				end
			end
		end
	end
end

wire [3:0] env_l = levels[env[shape][phase]][counter] & ~mask;
wire [3:0] env_r = stereo ? (4'd15 & ~mask) - env_l : env_l; // bit 0 of envreg inverts envelope shape

reg  [1:0] outmix;
always_comb begin
	case(mixmode)
		0: outmix <= 0;
		1: outmix <= {tone,  1'b0};
		2: outmix <= {noise, 1'b0};
		3: outmix <= {tone & ~noise, tone & noise};
	endcase
end

wire [8:0] vol_mix_l = {vol[3:1], vol[0] & ~enable, 5'b00000} >> outmix[0];
wire [8:0] vol_mix_r = {vol[7:5], vol[4] & ~enable, 5'b00000} >> outmix[0];
wire [8:0] env_out_l;
wire [8:0] env_out_r;
saa1099_mul_env mod_l(.vol(vol_mix_l[8:4]), .env(env_l), .out(env_out_l));
saa1099_mul_env mod_r(.vol(vol_mix_r[8:4]), .env(env_r), .out(env_out_r));

always_comb begin
	case({enable, outmix})
		'b100, 'b101: out = {env_out_r, env_out_l};
		'b001, 'b010: out = {vol_mix_r, vol_mix_l};
		     default: out = 0;
	endcase
end

endmodule

/////////////////////////////////////////////////////////////////////////////////

module saa1099_mul_env
(
	input  [4:0] vol,
	input  [3:0] env,
	output [8:0] out
);

assign out = (env[0] ?      vol     : 9'd0)+
             (env[1] ? {  vol,1'b0} : 9'd0)+
             (env[2] ? { vol,2'b00} : 9'd0)+
             (env[3] ? {vol,3'b000} : 9'd0);

endmodule

/////////////////////////////////////////////////////////////////////////////////

module saa1099_output_mixer
(
	input            clk_sys,
	input            ce,
	input            en,
	input     [10:0] in0,
	input     [10:0] in1,
	output reg [7:0] out
);

wire [17:0] o = 18'd91 * ({1'b0,in0} + {1'b0,in1});

// Clean the audio.
always @(posedge clk_sys) begin
	reg ced;
	ced <= ce;
	if(ced) out <= ~en ? 8'h00 : o[17:10];
end

endmodule
