----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    14:28:08 04/05/2010 
-- Design Name: 
-- Module Name:    audiogen - RTL 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 	 Emulator PSG SN76489AN + audio mixer pro CTC0
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity audiogen is
    Port ( clk : in  STD_LOGIC;												-- vstupni hodiny audio generatoru ( 50 MHz )
           SAMPLE_ENABLE : in  STD_LOGIC;									-- povoleni nabirani vzorku (sample rate)
			  DATA : in  STD_LOGIC_VECTOR (7 downto 0);					-- datova sbernice z MZ800
			  WE : in STD_LOGIC;													-- povolen zapis dat do PSG (aktivni pri 1)
			  CTC0_PWM : in STD_LOGIC;											-- zdroj zvuku z casovace CTC0
           L_DATA : out  STD_LOGIC_VECTOR (23 downto 0);				-- vystupni audio data pro levy kanal
           R_DATA : out  STD_LOGIC_VECTOR (23 downto 0));			-- vystupni audio data pro pravy kanal
end audiogen;

architecture RTL of audiogen is


	component square_generator is
   Port (
		CK443K : in  STD_LOGIC;										-- src hodiny (443 kHz)
      ATTEN : in  STD_LOGIC_VECTOR(3 downto 0);				-- uroven tlumeni hlasitosti kanalu  
      DIVIDER : in  STD_LOGIC_VECTOR(9 downto 0);			-- nastaveni delicky generatoru
      WAVE : out  SIGNED(23 downto 0)							-- vystupni data v binarnim komplementu +, -, nebo 0
	);
	end component;

	component noise_generator is
   Port (
		CK443K : in  STD_LOGIC;										-- src hodiny (443 kHz)
      ATTEN : in  STD_LOGIC_VECTOR(3 downto 0);				-- uroven tlumeni hlasitosti kanalu  
      DIVIDER : in  STD_LOGIC_VECTOR(9 downto 0);			-- nastaveni delicky generatoru
		NOISE_TYPE : in STD_LOGIC;									-- typ sumu 0 - synchronni, 1 - bily sum
		DIVIDER_SRC : in STD_LOGIC_VECTOR(1 downto 0);		-- zdroj frq generatoru sumu 0 - /64, 1 - /32, 2 - /16, 3 - podle 3 kanalu
      WAVE : out  SIGNED(23 downto 0)							-- vystupni data v binarnim komplementu +, -, nebo 0
	);
	end component;
	
	-- zdrojove hodiny ke zvukovym generatorum
	signal iCK443K : std_logic;									-- zdrojove hodiny pro zvukovy generator (443 kHz)


	-- 1. zvukovy kanal
	signal divider0 : std_logic_vector(9 downto 0); 					-- delicky urcujici frequenci zvuku, divider = (3 546 895 / 32) / frq
	signal atten0 : std_logic_vector(3 downto 0) :="1111";			-- uroven tlumeni hlasitosti kanalu ( 0000 - max hlasitost, 1111 - ticho )
	signal wave0 : signed(23 downto 0);										-- sample data
	-- 2. zvukovy kanal
	signal divider1 : std_logic_vector(9 downto 0); 					-- delicky urcujici frequenci zvuku, divider = (3 546 895 / 32) / frq
	signal atten1 : std_logic_vector(3 downto 0) :="1111";			-- uroven tlumeni hlasitosti kanalu ( 0000 - max hlasitost, 1111 - ticho )
	signal wave1 : signed(23 downto 0);										-- sample data
	-- 3. zvukovy kanal
	signal divider2 : std_logic_vector(9 downto 0); 					-- delicky urcujici frequenci zvuku, divider = (3 546 895 / 32) / frq
	signal atten2 : std_logic_vector(3 downto 0) :="1111";			-- uroven tlumeni hlasitosti kanalu ( 0000 - max hlasitost, 1111 - ticho )
	signal wave2 : signed(23 downto 0);										-- sample data
	-- 4. zvukovy kanal (noise generator)
	signal noise_type : std_logic;											-- typ sumu 0 - synchronni, 1 - bily sum
	signal noise_src : std_logic_vector(1 downto 0);					-- zdroj frq generatoru sumu 0 - /64, 1 - /32, 2 - /16, 3 - podle 3 kanalu
	signal atten3 : std_logic_vector(3 downto 0) :="1111";			-- uroven tlumeni hlasitosti kanalu ( 0000 - max hlasitost, 1111 - ticho )
	signal wave3 : signed(23 downto 0);										-- sample data

	-- zvukovy kanal z CTC0
	signal ctc0_enabled : std_logic := '1';								-- povoleni pristupu CTC0_PWM do mixeru
	signal wave_ctc0 : signed(23 downto 0);								-- sample data

	
begin


	-- dekoder vstupnich dat
	process ( clk )
		variable channel : std_logic_vector(1 downto 0) := "00";
	begin
		if (clk'event and clk = '1') then
			if WE = '1' then

				-- dekodovani vstupnich dat (urceni aktivniho kanalu)
				if DATA(7) = '1' then
					channel := DATA(6 downto 5);
				end if;

				-- hornich 6 bitu delicky
				if DATA(7) = '0' then
					case channel is
						when "00" => divider0(9 downto 4) <= DATA(5 downto 0);
						when "01" => divider1(9 downto 4) <= DATA(5 downto 0);
						when "10" => divider2(9 downto 4) <= DATA(5 downto 0);
						when others => null;
					end case;
			
				-- tlumeni hlasitosti
				elsif DATA(4) = '1' then
					case channel is
						when "00" => atten0 <= DATA(3 downto 0);
						when "01" => atten1 <= DATA(3 downto 0);
						when "10" => atten2 <= DATA(3 downto 0);
						when "11" => atten3 <= DATA(3 downto 0);
						when others => null;
					end case;

				else
					-- nastavenu sumoveho generatoru
					if channel = "11" then
						noise_type <= DATA(2);
						noise_src <= DATA(1 downto 0);

					-- dolni 4 bity delicky				
					else
						case channel is
							when "00" => divider0(3 downto 0) <= DATA(3 downto 0);
							when "01" => divider1(3 downto 0) <= DATA(3 downto 0);
							when "10" => divider2(3 downto 0) <= DATA(3 downto 0);
							when others => null;
						end case;
					end if;

				end if;
				
			end if;
			
		end if;
	end process;
	


	-- hodiny zvukoveho generatoru v MZ800 (iCK443K). Pri 50 MHz delime 113.
	process (clk)
		variable clk_counter : std_logic_vector(8 downto 0) := (others=>'0');
	begin
		if (clk'event and clk = '1') then
			if clk_counter = "000000000" then
				clk_counter := CONV_STD_LOGIC_VECTOR(112,9);
				iCK443K <= not iCK443K;
			else 
				clk_counter := clk_counter - 1;
			end if;
		end if;
	end process;



	-- napojeni 1. generatoru
	squaregen0: square_generator
   Port map ( 
		CK443K		=> iCK443K,							-- src hodiny (443 kHz)
      ATTEN			=> ATTEN0,							-- uroven tlumeni hlasitosti kanalu  
      DIVIDER		=> divider0,						-- nastaveni delicky generatoru
      WAVE			=> wave0								-- vystupni data v binarnim komplementu +, -, nebo 0
	);

	-- napojeni 2. generatoru
	squaregen1: square_generator
   Port map ( 
		CK443K		=> iCK443K,							-- src hodiny (443 kHz)
      ATTEN			=> ATTEN1,							-- uroven tlumeni hlasitosti kanalu  
      DIVIDER		=> divider1,						-- nastaveni delicky generatoru
      WAVE			=> wave1								-- vystupni data v binarnim komplementu +, -, nebo 0
	);

	-- napojeni 3. generatoru
	squaregen2: square_generator
   Port map ( 
		CK443K		=> iCK443K,							-- src hodiny (443 kHz)
      ATTEN			=> ATTEN2,							-- uroven tlumeni hlasitosti kanalu  
      DIVIDER		=> divider2,						-- nastaveni delicky generatoru
      WAVE			=> wave2								-- vystupni data v binarnim komplementu +, -, nebo 0
	);
	
	-- napojeni sumoveho generatoru
	noisegen: noise_generator
   Port map ( 
		CK443K		=> iCK443K,							-- src hodiny (443 kHz)
      ATTEN			=> ATTEN3,							-- uroven tlumeni hlasitosti kanalu
		NOISE_TYPE	=> noise_type,						-- typ sumu 0 - synchronni, 1 - bily sum
      DIVIDER		=> divider2,						-- nastaveni delicky generatoru (podle 3. kanalu)
		DIVIDER_SRC	=> noise_src,						-- zdroj frq generatoru sumu 0 - /64, 1 - /32, 2 - /16, 3 - podle 3 kanalu
      WAVE			=> wave3								-- vystupni data v binarnim komplementu +, -, nebo 0
	);


	-- pokud se stav CTC0_PWM uz nejakou dobu nezmenil, tak zakazeme jeho pristup do mixeru
	process ( iCK443K )
		variable ctc0_last : std_logic := '0';
		variable ctc0_changed : std_logic_vector(15 downto 0) := x"0000";
	begin
		if iCK443K'event and iCK443K = '1' then
			if CTC0_PWM = ctc0_last then
				if ctc0_enabled = '1' then
					ctc0_changed := ctc0_changed + 1;
					if ctc0_changed = CONV_STD_LOGIC_VECTOR( 11062, 16 ) then
						ctc0_enabled <= '0';
					end if;
				end if;			
			else
				ctc0_enabled <= '1';
				ctc0_changed := x"0000";
				ctc0_last := CTC0_PWM;
			end if;
		end if;
	end process;
	
	-- vytvoreni audio samplu pro zvuk z CTC0
	wave_ctc0 	<= (others=>'0') when ctc0_enabled = '0' else 
						"0" & "00" & "11111111" & "1111111111111" when CTC0_PWM = '1' else 	-- maximalni hlasitost jednoho kanalu +
						"1" & "11" & "00000000" & "0000000000000";									-- maximalni hlasitost jednoho kanalu -
	
	-- Vystupni mixer
	process ( clk )
	begin
		if clk'event and clk = '1' then
			if SAMPLE_ENABLE = '1' then	
				
				L_DATA <= CONV_STD_LOGIC_VECTOR( wave0 + wave1 + wave2 + wave3 + wave_ctc0 , 24 );
				R_DATA <= CONV_STD_LOGIC_VECTOR( wave0 + wave1 + wave2 + wave3 + wave_ctc0 , 24 );
								
			end if;
		end if;
	end process;

end RTL;

