module namco163_sound(
	input clk20,
	input m2,
	input enable,
	input wr,
	input [15:0] ain,
	input [7:0] din,
	output [7:0] dout,
	output reg [10:0] out,       //range is 0..0x708
	// savestates              
	input       [63:0]  SaveStateBus_Din,
	input       [ 9:0]  SaveStateBus_Adr,
	input               SaveStateBus_wren,
	input               SaveStateBus_rst,
	input               SaveStateBus_load,
	output      [63:0]  SaveStateBus_Dout,
	
	input             Savestate_MAPRAMactive,    
	input      [6:0]  Savestate_MAPRAMAddr,     
	input             Savestate_MAPRAMRdEn,    
	input             Savestate_MAPRAMWrEn,    
	input      [7:0]  Savestate_MAPRAMWriteData,
	output reg [7:0]  Savestate_MAPRAMReadData
);

reg carry;
reg autoinc;
reg [6:0] ram_ain;
reg [6:0] ram_aout;
wire [7:0] ram_dout;
reg [2:0] ch;
reg [7:0] cnt_L[7:0];
reg [7:0] cnt_M[7:0];
reg [1:0] cnt_H[7:0];
wire [2:0] sum_H=cnt_H[ch]+ram_dout[1:0]+carry;
reg [4:0] sample_pos[7:0];
reg [2:0] cycle;
reg [3:0] sample;
wire [7:0] chan_out=sample*ram_dout[3:0];   //sample*vol
reg [10:0] out_acc;
wire [10:0] sum=out_acc+chan_out;
reg addr_lsb;
wire [7:0] sample_addr=ram_dout+sample_pos[ch];
reg do_inc;

//ram in
always@(posedge clk20) begin
	if (SaveStateBus_load) begin
		do_inc  <= SS_MAP1[    0];
		ram_ain <= SS_MAP1[ 7: 1];
		autoinc <= SS_MAP1[    8];
	end else if (m2) begin
		do_inc<= 0;
		if (do_inc)
			ram_ain<=ram_ain+1'd1;
		if(wr & ain[15:11]==5'b11111)           //F800..FFFF
			{autoinc,ram_ain}<=din;
		else if(ain[15:11]==5'b01001 & autoinc) //4800..4FFF
			do_inc<=1;
	end
end

//mixer FSM
always @* begin
	case(cycle)
		0: ram_aout={1'b1,ch,3'd0};     //freq[7:0]
		1: ram_aout={1'b1,ch,3'd2};     //freq[15:8]
		2: ram_aout={1'b1,ch,3'd4};     //length, freq[17:16]
		3: ram_aout={1'b1,ch,3'd6};     //address
		4: ram_aout=sample_addr[7:1];   //sample address
		5: ram_aout={1'b1,ch,3'd7};     //volume
		default: ram_aout=7'bXXXXXXX;
	endcase
end

reg [3:0] count45,cnt45;
always@(posedge clk20) begin
	if (SaveStateBus_load) begin
		count45 <= SS_MAP1[12: 9];
	end else if (m2) begin
		count45<=(count45==14)?4'd0:count45+1'd1;
	end
end

always@(posedge clk20) begin
	if (SaveStateBus_load) begin
		cnt45         <= SS_MAP1[16:13];
		cycle         <= SS_MAP1[19:17];
		carry         <= SS_MAP1[   20];
		addr_lsb      <= SS_MAP1[   21];
		sample        <= SS_MAP1[25:22];
		ch            <= SS_MAP1[28:26];
		out_acc       <= SS_MAP1[39:29];
		cnt_L[0]      <= SS_MAP2[ 7: 0];
		cnt_L[1]      <= SS_MAP2[15: 8];
		cnt_L[2]      <= SS_MAP2[23:16];
		cnt_L[3]      <= SS_MAP2[31:24];
		cnt_L[4]      <= SS_MAP2[39:32];
		cnt_L[5]      <= SS_MAP2[47:40];
		cnt_L[6]      <= SS_MAP2[55:48];
		cnt_L[7]      <= SS_MAP2[63:56];
		cnt_M[0]      <= SS_MAP3[ 7: 0];
		cnt_M[1]      <= SS_MAP3[15: 8];
		cnt_M[2]      <= SS_MAP3[23:16];
		cnt_M[3]      <= SS_MAP3[31:24];
		cnt_M[4]      <= SS_MAP3[39:32];
		cnt_M[5]      <= SS_MAP3[47:40];
		cnt_M[6]      <= SS_MAP3[55:48];
		cnt_M[7]      <= SS_MAP3[63:56];
		cnt_H[0]      <= SS_MAP4[ 1: 0];
		cnt_H[1]      <= SS_MAP4[ 3: 2];
		cnt_H[2]      <= SS_MAP4[ 5: 4];
		cnt_H[3]      <= SS_MAP4[ 7: 6];
		cnt_H[4]      <= SS_MAP4[ 9: 8];
		cnt_H[5]      <= SS_MAP4[11:10];
		cnt_H[6]      <= SS_MAP4[13:12];
		cnt_H[7]      <= SS_MAP4[15:14];
		sample_pos[0] <= SS_MAP4[20:16];
		sample_pos[1] <= SS_MAP4[25:21];
		sample_pos[2] <= SS_MAP4[30:26];
		sample_pos[3] <= SS_MAP4[35:31];
		sample_pos[4] <= SS_MAP4[40:36];
		sample_pos[5] <= SS_MAP4[45:41];
		sample_pos[6] <= SS_MAP4[50:46];
		sample_pos[7] <= SS_MAP4[55:51];
	end else begin
		cnt45<=count45;
		if(cnt45[1:0]==0) cycle<=0;             // this gives 45 21.4M clocks per channel
		else if(cycle!=7) cycle<=cycle+1'd1;
		case(cycle)
			1: {carry, cnt_L[ch]}<=cnt_L[ch][7:0]+ram_dout;
			2: {carry, cnt_M[ch]}<=cnt_M[ch][7:0]+ram_dout+carry;
			3: begin
				cnt_H[ch]<=sum_H[1:0];
				if(sum_H[2])
					sample_pos[ch]<=(sample_pos[ch]=={ram_dout[4:2]^3'b111,2'b11})?5'd0:(sample_pos[ch]+1'd1);
			end
			4: addr_lsb<=sample_addr[0];
			5: sample<=addr_lsb?ram_dout[7:4]:ram_dout[3:0];
			6: begin
				if(ch==7) begin
					ch<=~ram_dout[6:4];
					out_acc<=0;
					out<=sum;
				end else begin
					ch<=ch+1'd1;
					out_acc<=sum;
				end
			end
		endcase
	end
end

wire [6:0] ram_addrB = Savestate_MAPRAMactive ? Savestate_MAPRAMAddr : ram_aout;
wire       ram_wrenB = Savestate_MAPRAMactive ? Savestate_MAPRAMWrEn : 1'b0;
wire [7:0] ram_dataB = Savestate_MAPRAMactive ? Savestate_MAPRAMWriteData : 8'b0;

dpram #(.widthad_a(7)) modtable
(
	.clock_a   (clk20),
	.address_a (ram_ain),
	.wren_a    (wr & ain[15:11]==5'b01001),
	.byteena_a (1),
	.data_a    (din),
	.q_a       (dout),

	.clock_b   (clk20),
	.address_b (ram_addrB),
	.wren_b    (ram_wrenB),
	.byteena_b (1),
	.data_b    (ram_dataB),
	.q_b       (ram_dout)
);

// savestate
always@(posedge clk20) begin
	if (enable) begin
		if (Savestate_MAPRAMRdEn) begin
			Savestate_MAPRAMReadData <= ram_dout;
		end
	end else begin
		Savestate_MAPRAMReadData <= 8'd0;
	end
end

assign SS_MAP1_BACK[16:13] = cnt45;
assign SS_MAP1_BACK[19:17] = cycle;
assign SS_MAP1_BACK[   20] = carry;
assign SS_MAP1_BACK[   21] = addr_lsb;
assign SS_MAP1_BACK[25:22] = sample;
assign SS_MAP1_BACK[28:26] = ch;
assign SS_MAP1_BACK[39:29] = out_acc;
assign SS_MAP1_BACK[63:40] = 24'b0; // free to be used

assign SS_MAP2_BACK[ 7: 0] = cnt_L[0];
assign SS_MAP2_BACK[15: 8] = cnt_L[1];
assign SS_MAP2_BACK[23:16] = cnt_L[2];
assign SS_MAP2_BACK[31:24] = cnt_L[3];
assign SS_MAP2_BACK[39:32] = cnt_L[4];
assign SS_MAP2_BACK[47:40] = cnt_L[5];
assign SS_MAP2_BACK[55:48] = cnt_L[6];
assign SS_MAP2_BACK[63:56] = cnt_L[7];
assign SS_MAP3_BACK[ 7: 0] = cnt_M[0];
assign SS_MAP3_BACK[15: 8] = cnt_M[1];
assign SS_MAP3_BACK[23:16] = cnt_M[2];
assign SS_MAP3_BACK[31:24] = cnt_M[3];
assign SS_MAP3_BACK[39:32] = cnt_M[4];
assign SS_MAP3_BACK[47:40] = cnt_M[5];
assign SS_MAP3_BACK[55:48] = cnt_M[6];
assign SS_MAP3_BACK[63:56] = cnt_M[7];
assign SS_MAP4_BACK[ 1: 0] = cnt_H[0];
assign SS_MAP4_BACK[ 3: 2] = cnt_H[1];
assign SS_MAP4_BACK[ 5: 4] = cnt_H[2];
assign SS_MAP4_BACK[ 7: 6] = cnt_H[3];
assign SS_MAP4_BACK[ 9: 8] = cnt_H[4];
assign SS_MAP4_BACK[11:10] = cnt_H[5];
assign SS_MAP4_BACK[13:12] = cnt_H[6];
assign SS_MAP4_BACK[15:14] = cnt_H[7];
assign SS_MAP4_BACK[20:16] = sample_pos[0];
assign SS_MAP4_BACK[25:21] = sample_pos[1];
assign SS_MAP4_BACK[30:26] = sample_pos[2];
assign SS_MAP4_BACK[35:31] = sample_pos[3];
assign SS_MAP4_BACK[40:36] = sample_pos[4];
assign SS_MAP4_BACK[45:41] = sample_pos[5];
assign SS_MAP4_BACK[50:46] = sample_pos[6];
assign SS_MAP4_BACK[55:51] = sample_pos[7];
assign SS_MAP4_BACK[63:56] = 8'b0; // free to be used

localparam SAVESTATE_MODULES    = 4;
wire [63:0] SaveStateBus_wired_or[0:SAVESTATE_MODULES-1];
wire [63:0] SS_MAP1, SS_MAP2, SS_MAP3, SS_MAP4;
wire [63:0] SS_MAP1_BACK, SS_MAP2_BACK, SS_MAP3_BACK, SS_MAP4_BACK;	
wire [63:0] SaveStateBus_Dout_active = SaveStateBus_wired_or[0] | SaveStateBus_wired_or[1] | SaveStateBus_wired_or[2] | SaveStateBus_wired_or[3];
	
eReg_SavestateV #(SSREG_INDEX_SNDMAP1, 64'h0000000000000000) iREG_SAVESTATE_MAP1 (clk20, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_wired_or[0], SS_MAP1_BACK, SS_MAP1);  
eReg_SavestateV #(SSREG_INDEX_SNDMAP2, 64'h0000000000000000) iREG_SAVESTATE_MAP2 (clk20, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_wired_or[1], SS_MAP2_BACK, SS_MAP2);  
eReg_SavestateV #(SSREG_INDEX_SNDMAP3, 64'h0000000000000000) iREG_SAVESTATE_MAP3 (clk20, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_wired_or[2], SS_MAP3_BACK, SS_MAP3);  
eReg_SavestateV #(SSREG_INDEX_SNDMAP4, 64'h0000000000000000) iREG_SAVESTATE_MAP4 (clk20, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_wired_or[3], SS_MAP4_BACK, SS_MAP4);  

assign SaveStateBus_Dout = enable ? SaveStateBus_Dout_active : 64'h0000000000000000;

endmodule