--
-- scc_wave.vhd
--   Sound generator with wave table
--   Revision 1.00
--
-- Copyright (c) 2006 Kazuhiro Tsujikawa (ESE Artists' factory)
-- All rights reserved.
--
-- Redistribution and use of this source code or any derivative works, are
-- permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
--    this list of conditions and the following disclaimer.
-- 2. Redistributions in binary form must reproduce the above copyright
--    notice, this list of conditions and the following disclaimer in the
--    documentation and/or other materials provided with the distribution.
-- 3. Redistributions may not be sold, nor may they be used in a commercial
--    product or activity without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-- OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-- WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-- OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--

--  2007/01/31  modified by t.hara

library ieee;
    use ieee.std_logic_1164.all;
    use ieee.std_logic_signed.all;

entity scc_wave_mul is
    port(
        a           : in    std_logic_vector(  7 downto 0 );    -- 8bit �Q�̕␔
        b           : in    std_logic_vector(  3 downto 0 );    -- 4bit �o�C�i��
        c           : out   std_logic_vector( 11 downto 0 )     -- 12bit �Q�̕␔
    );
end scc_wave_mul;

architecture rtl of scc_wave_mul is
    signal w_mul    : std_logic_vector( 12 downto 0 );
begin
    w_mul   <= a * ('0' & b);
    c       <= w_mul( 11 downto 0 );
end rtl;

------------------------------------------------------------------------
library ieee;
    use ieee.std_logic_1164.all;
    use ieee.std_logic_signed.all;

entity scc_mix_mul is
    port(
        a           : in    std_logic_vector( 15 downto 0 );    -- 16bit �Q�̕␔
        b           : in    std_logic_vector(  2 downto 0 );    -- 3bit �o�C�i��
        c           : out   std_logic_vector( 18 downto 0 )     -- 19bit �Q�̕␔
    );
end scc_mix_mul;

architecture rtl of scc_mix_mul is
    signal w_mul    : std_logic_vector( 19 downto 0 );
begin
    w_mul   <= a * ('0' & b);
    c       <= w_mul( 18 downto 0 );
end rtl;

------------------------------------------------------------------------
library ieee;
    use ieee.std_logic_1164.all;
    use ieee.std_logic_unsigned.all;

entity scc_wave is
    port(
        clk21m      : in    std_logic;
        reset       : in    std_logic;
        clkena      : in    std_logic;
        req         : in    std_logic;
        ack         : out   std_logic;
        wrt         : in    std_logic;
        adr         : in    std_logic_vector( 7 downto 0 );
        dbi         : out   std_logic_vector( 7 downto 0 );
        dbo         : in    std_logic_vector( 7 downto 0 );
        wave        : out   std_logic_vector( 14 downto 0 )
    );
end scc_wave;

architecture rtl of scc_wave is

    -- wire signal
    signal w_wave_ce        : std_logic;
    signal w_wave_we        : std_logic;
    signal w_wave_adr       : std_logic_vector(  7 downto 0 );
    signal w_ch_dec         : std_logic_vector(  4 downto 0 );
    signal w_ch_bit         : std_logic;
    signal w_ch_mask        : std_logic_vector(  7 downto 0 );
    signal w_ch_vol         : std_logic_vector(  3 downto 0 );
    signal w_wave           : std_logic_vector(  7 downto 0 );
    signal w_mul            : std_logic_vector( 11 downto 0 );
    signal ram_dbi          : std_logic_vector(  7 downto 0 );
    signal lpf1_wave        : std_logic_vector( 14 downto 0 );
    signal lpf2_wave        : std_logic_vector( 14 downto 0 );
    signal lpf3_wave        : std_logic_vector( 14 downto 0 );

    -- scc resisters
    signal reg_freq_ch_a    : std_logic_vector( 11 downto 0 );
    signal reg_freq_ch_b    : std_logic_vector( 11 downto 0 );
    signal reg_freq_ch_c    : std_logic_vector( 11 downto 0 );
    signal reg_freq_ch_d    : std_logic_vector( 11 downto 0 );
    signal reg_freq_ch_e    : std_logic_vector( 11 downto 0 );
    signal reg_vol_ch_a     : std_logic_vector(  3 downto 0 );
    signal reg_vol_ch_b     : std_logic_vector(  3 downto 0 );
    signal reg_vol_ch_c     : std_logic_vector(  3 downto 0 );
    signal reg_vol_ch_d     : std_logic_vector(  3 downto 0 );
    signal reg_vol_ch_e     : std_logic_vector(  3 downto 0 );
    signal reg_ch_sel       : std_logic_vector(  4 downto 0 );
    signal reg_mode_sel     : std_logic_vector(  7 downto 0 );

    -- internal registers
    signal ff_rst_ch_a      : std_logic;
    signal ff_rst_ch_b      : std_logic;
    signal ff_rst_ch_c      : std_logic;
    signal ff_rst_ch_d      : std_logic;
    signal ff_rst_ch_e      : std_logic;
    signal ff_ptr_ch_a      : std_logic_vector(  4 downto 0 );
    signal ff_ptr_ch_b      : std_logic_vector(  4 downto 0 );
    signal ff_ptr_ch_c      : std_logic_vector(  4 downto 0 );
    signal ff_ptr_ch_d      : std_logic_vector(  4 downto 0 );
    signal ff_ptr_ch_e      : std_logic_vector(  4 downto 0 );
    signal ff_ch_num        : std_logic_vector(  2 downto 0 );
    signal ff_ch_num_dl     : std_logic_vector(  2 downto 0 );
    signal ff_mix           : std_logic_vector( 14 downto 0 );
    signal ff_wave_ce       : std_logic;
    signal ff_wave_ce_dl    : std_logic;
    signal ff_req_dl        : std_logic;
    signal ff_wave_dat      : std_logic_vector(  7 downto 0 );
    signal ff_wave          : std_logic_vector( 14 downto 0 );
begin

    ----------------------------------------------------------------
    -- scc register access
    ----------------------------------------------------------------
    process(clk21m, reset)
    begin
        if (rising_edge(clk21m)) then

            if( reset = '1' )then
                ff_req_dl       <= '0';

                reg_freq_ch_a   <= (others => '0');
                reg_freq_ch_b   <= (others => '0');
                reg_freq_ch_c   <= (others => '0');
                reg_freq_ch_d   <= (others => '0');
                reg_freq_ch_e   <= (others => '0');

                reg_vol_ch_a    <= (others => '0');
                reg_vol_ch_b    <= (others => '0');
                reg_vol_ch_c    <= (others => '0');
                reg_vol_ch_d    <= (others => '0');
                reg_vol_ch_e    <= (others => '0');

                reg_ch_sel      <= (others => '0');
                reg_mode_sel    <= (others => '0');

                ff_rst_ch_a     <= '0';
                ff_rst_ch_b     <= '0';
                ff_rst_ch_c     <= '0';
                ff_rst_ch_d     <= '0';
                ff_rst_ch_e     <= '0';

            else

                -- mapped i/o port access on b8a0-b8afh (9880-988fh) ... register write
                if( req = '1' and ff_req_dl = '0' and adr(7 downto 5) = "101" and wrt = '1' )then
                    case adr(3 downto 0) is
                        when "0000" => reg_freq_ch_a(  7 downto 0 ) <= dbo( 7 downto 0 ); ff_rst_ch_a <= reg_mode_sel(5);
                        when "0001" => reg_freq_ch_a( 11 downto 8 ) <= dbo( 3 downto 0 ); ff_rst_ch_a <= reg_mode_sel(5);
                        when "0010" => reg_freq_ch_b(  7 downto 0 ) <= dbo( 7 downto 0 ); ff_rst_ch_b <= reg_mode_sel(5);
                        when "0011" => reg_freq_ch_b( 11 downto 8 ) <= dbo( 3 downto 0 ); ff_rst_ch_b <= reg_mode_sel(5);
                        when "0100" => reg_freq_ch_c(  7 downto 0 ) <= dbo( 7 downto 0 ); ff_rst_ch_c <= reg_mode_sel(5);
                        when "0101" => reg_freq_ch_c( 11 downto 8 ) <= dbo( 3 downto 0 ); ff_rst_ch_c <= reg_mode_sel(5);
                        when "0110" => reg_freq_ch_d(  7 downto 0 ) <= dbo( 7 downto 0 ); ff_rst_ch_d <= reg_mode_sel(5);
                        when "0111" => reg_freq_ch_d( 11 downto 8 ) <= dbo( 3 downto 0 ); ff_rst_ch_d <= reg_mode_sel(5);
                        when "1000" => reg_freq_ch_e(  7 downto 0 ) <= dbo( 7 downto 0 ); ff_rst_ch_e <= reg_mode_sel(5);
                        when "1001" => reg_freq_ch_e( 11 downto 8 ) <= dbo( 3 downto 0 ); ff_rst_ch_e <= reg_mode_sel(5);
                        when "1010" => reg_vol_ch_a( 3 downto 0 )   <= dbo( 3 downto 0 );
                        when "1011" => reg_vol_ch_b( 3 downto 0 )   <= dbo( 3 downto 0 );
                        when "1100" => reg_vol_ch_c( 3 downto 0 )   <= dbo( 3 downto 0 );
                        when "1101" => reg_vol_ch_d( 3 downto 0 )   <= dbo( 3 downto 0 );
                        when "1110" => reg_vol_ch_e( 3 downto 0 )   <= dbo( 3 downto 0 );
                        when others => reg_ch_sel(   4 downto 0 )   <= dbo( 4 downto 0 );
                    end case;
                elsif (clkena = '1') then
                    ff_rst_ch_a <= '0';
                    ff_rst_ch_b <= '0';
                    ff_rst_ch_c <= '0';
                    ff_rst_ch_d <= '0';
                    ff_rst_ch_e <= '0';
                end if;

                -- mapped i/o port access on b8c0-b8dfh (98e0-98ffh) ... register write
                if( req = '1' and wrt = '1' and adr(7 downto 5) = "110" )then
                    reg_mode_sel <= dbo;
                end if;

                ff_req_dl <= req;
            end if;
        end if;
    end process;

    -- mapped i/o port access on b800-bfffh (9800-9fffh) ... wave memory access
    w_wave_ce   <= '1'  when( req = '1' and ff_req_dl = '0' )else '0';
    w_wave_we   <= wrt  when( req = '1' and ff_req_dl = '0' )else '0';
    ack     <= ff_req_dl;

    ----------------------------------------------------------------
    -- tone generator
    ----------------------------------------------------------------
    process(clk21m, reset)
        variable ff_cnt_ch_a : std_logic_vector( 11 downto 0 );
        variable ff_cnt_ch_b : std_logic_vector( 11 downto 0 );
        variable ff_cnt_ch_c : std_logic_vector( 11 downto 0 );
        variable ff_cnt_ch_d : std_logic_vector( 11 downto 0 );
        variable ff_cnt_ch_e : std_logic_vector( 11 downto 0 );
    begin
        if (rising_edge(clk21m)) then
            if (reset = '1') then
                ff_cnt_ch_a := (others => '0');
                ff_cnt_ch_b := (others => '0');
                ff_cnt_ch_c := (others => '0');
                ff_cnt_ch_d := (others => '0');
                ff_cnt_ch_e := (others => '0');

                ff_ptr_ch_a <= (others => '0');
                ff_ptr_ch_b <= (others => '0');
                ff_ptr_ch_c <= (others => '0');
                ff_ptr_ch_d <= (others => '0');
                ff_ptr_ch_e <= (others => '0');
            else
                if (clkena = '1') then

                    if (reg_freq_ch_a(11 downto 3) = "000000000" or ff_rst_ch_a = '1') then
                        ff_ptr_ch_a <= "00000";
                        ff_cnt_ch_a := reg_freq_ch_a;
                    elsif (ff_cnt_ch_a = x"000") then
                        ff_ptr_ch_a <= ff_ptr_ch_a + 1;
                        ff_cnt_ch_a := reg_freq_ch_a;
                    else
                        ff_cnt_ch_a := ff_cnt_ch_a - 1;
                    end if;

                    if (reg_freq_ch_b(11 downto 3) = "000000000" or ff_rst_ch_b = '1') then
                        ff_ptr_ch_b <= "00000";
                        ff_cnt_ch_b := reg_freq_ch_b;
                    elsif (ff_cnt_ch_b = x"000") then
                        ff_ptr_ch_b <= ff_ptr_ch_b + 1;
                        ff_cnt_ch_b := reg_freq_ch_b;
                    else
                        ff_cnt_ch_b := ff_cnt_ch_b - 1;
                    end if;

                    if (reg_freq_ch_c(11 downto 3) = "000000000" or ff_rst_ch_c = '1') then
                        ff_ptr_ch_c <= "00000";
                        ff_cnt_ch_c := reg_freq_ch_c;
                    elsif (ff_cnt_ch_c = x"000") then
                        ff_ptr_ch_c <= ff_ptr_ch_c + 1;
                        ff_cnt_ch_c := reg_freq_ch_c;
                    else
                        ff_cnt_ch_c := ff_cnt_ch_c - 1;
                    end if;

                    if (reg_freq_ch_d(11 downto 3) = "000000000" or ff_rst_ch_d = '1') then
                        ff_ptr_ch_d <= "00000";
                        ff_cnt_ch_d := reg_freq_ch_d;
                    elsif (ff_cnt_ch_d = x"000") then
                        ff_ptr_ch_d <= ff_ptr_ch_d + 1;
                        ff_cnt_ch_d := reg_freq_ch_d;
                    else
                        ff_cnt_ch_d := ff_cnt_ch_d - 1;
                    end if;

                    if (reg_freq_ch_e(11 downto 3) = "000000000" or ff_rst_ch_e = '1') then
                        ff_ptr_ch_e <= "00000";
                        ff_cnt_ch_e := reg_freq_ch_e;
                    elsif (ff_cnt_ch_e = x"000") then
                        ff_ptr_ch_e <= ff_ptr_ch_e + 1;
                        ff_cnt_ch_e := reg_freq_ch_e;
                    else
                        ff_cnt_ch_e := ff_cnt_ch_e - 1;
                    end if;

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

    ----------------------------------------------------------------
    -- wave memory control
    ----------------------------------------------------------------
    w_wave_adr   <= adr                 when( w_wave_ce = '1'   )else
                ("000" & ff_ptr_ch_a)   when( ff_ch_num = "000" )else
                ("001" & ff_ptr_ch_b)   when( ff_ch_num = "001" )else
                ("010" & ff_ptr_ch_c)   when( ff_ch_num = "010" )else
                ("011" & ff_ptr_ch_d)   when( ff_ch_num = "011" )else
                ("100" & ff_ptr_ch_e);

    wavemem : work.ram
    port map(
        adr => w_wave_adr   ,
        clk => clk21m       ,
        we  => w_wave_we    ,
        dbo => dbo          ,
        dbi => ram_dbi
    );

    --  wave memory read
    process( reset, clk21m )
    begin
        if( rising_edge(clk21m) )then
            if( reset = '1' )then
                dbi <= (others => '1');
            else
                -- mapped i/o port access on b800-bfffh (9800-9fffh) ... wave memory read data
                if( ff_wave_ce = '1' )then
                    dbi <= ram_dbi;
                end if;
            end if;
        end if;
    end process;

    ----------------------------------------------------------------
    -- delay signal
    ----------------------------------------------------------------
    process( reset, clk21m )
    begin
        if( rising_edge(clk21m) ) then
            if( reset = '1' )then
                ff_wave_ce      <= '0';
                ff_wave_ce_dl   <= '0';
                ff_wave_dat     <= (others => '0');
                ff_ch_num_dl    <= (others => '0');
            else
                ff_wave_ce      <= w_wave_ce;
                ff_wave_ce_dl   <= ff_wave_ce;
                ff_wave_dat     <= ram_dbi;
                ff_ch_num_dl    <= ff_ch_num;
            end if;
        end if;
    end process;

    ----------------------------------------------------------------
    -- mixer control
    ----------------------------------------------------------------
    with ff_ch_num_dl select w_ch_dec <=
        "00001" when "001",
        "00010" when "010",
        "00100" when "011",
        "01000" when "100",
        "10000" when "101",
        "00000" when others;

    w_ch_bit    <=  (w_ch_dec(0) and reg_ch_sel(0)) or
                    (w_ch_dec(1) and reg_ch_sel(1)) or
                    (w_ch_dec(2) and reg_ch_sel(2)) or
                    (w_ch_dec(3) and reg_ch_sel(3)) or
                    (w_ch_dec(4) and reg_ch_sel(4));

    w_ch_mask   <=  (others => w_ch_bit);

    with ff_ch_num_dl select w_ch_vol <=
        reg_vol_ch_a        when "001",
        reg_vol_ch_b        when "010",
        reg_vol_ch_c        when "011",
        reg_vol_ch_d        when "100",
        reg_vol_ch_e        when "101",
        (others => '0') when others;

    w_wave  <=  (w_ch_mask and ff_wave_dat);        -- 8bit ��̕␔

    u_mul: work.scc_wave_mul
    port map (
        a   => w_wave   ,   -- 8bit ��̕␔
        b   => w_ch_vol ,   -- 4bit �o�C�i���i���������j
        c   => w_mul        -- 12bit ��̕␔
    );

    -- -------------------------------------------------------------
    --  ff_ch_num   X 0   X 1   X 2   X 3   X 4   X 5   X 0
    --  ff_ch_num_dl      X 0   X 1   X 2   X 3   X 4   X 5   X 0
    --  w_wave_adr  X chA X chB X chC X chD X chE
    --  ram_dbi           X chA X chB X chC X chD X chE
    --  ff_wave_dat             X chA X chB X chC X chD X chE
    --  ff_mix                        X a   X ab  X a-c X a-d X a-e X 0
    --  wave                                                        X a-e
    -- -------------------------------------------------------------
    --  w_wave_ce   X 0   X 1   X 0
    --  ff_wave_ce  X 0         X 1   X 0
    --  ff_wave_ce_dl     X 0         X 1   X 0
    --
    --  ff_ch_num   X 0   X 1         X 2   X 3   X 4   X 5   X 0
    --  ff_ch_num_dl      X 0   X 1         X 2   X 3   X 4   X 5   X 0
    --  w_wave_adr  X chA X chB       X chC X chD X chE
    --  ram_dbi           X chA X chB       X chC X chD X chE
    --  ff_wave_dat             X chA X chB       X chC X chD X chE
    --  ff_mix                        X a         X ab  X a-c X a-d X a-e X 0
    --  wave                                                              X a-e
    -- -------------------------------------------------------------
    --  w_wave_ce   X 0                           X 1   X 0
    --  ff_wave_ce  X 0                                 X 1   X 0
    --  ff_wave_ce_dl     X 0                                 X 1   X 0   X
    --                                                               ~~~~~
    --  ff_ch_num   X 0   X 1   X 2   X 3   X 4   X 5   X 0         X 1
    --  ff_ch_num_dl      X 0   X 1   X 2   X 3   X 4   X 5   X 0         X 1   X 2   X
    --                                                         ~~~~~~~~~~~
    --  w_wave_adr  X chA X chB X chC X chD X chE       X chA       X chB X chC X
    --  ram_dbi           X chA X chB X chC X chD X chE       X chA       X chB X chC X
    --  ff_wave_dat             X chA X chB X chC X chD X chE       X chA       X chB X chC X
    --  ff_mix                        X a   X ab  X a-c X a-d X a-e       X 0   X a   X ab  X
    --  wave                                                              X a-e
    --                                                                     ~~~~~
    --  wave �́Aff_wave_ce_dl = 0 and ff_ch_num_dl = 0 �̂Ƃ� ff_mix ����荞��
    ----------------------------------------------------------------
    process( reset, clk21m )
    begin
        if( rising_edge(clk21m) )then
            if( reset = '1' )then
                ff_ch_num <= "000";
            else
                if( ff_wave_ce = '0' )then
                    if( ff_ch_num = "101" )then
                        ff_ch_num <= "000";
                    else
                        ff_ch_num <= ff_ch_num + 1;
                    end if;
                end if;
            end if;
        end if;
    end process;

    --  mixer
    process( reset, clk21m )
    begin
        if( rising_edge(clk21m) )then
            if( reset = '1' )then
                ff_mix  <= (others => '0');
            else
                if( ff_wave_ce_dl = '0' )then
                    if( ff_ch_num_dl = "000" )then
                        ff_mix  <=  (others => '0');
                    else
                        ff_mix  <=  (w_mul(11) & w_mul(11) & w_mul(11) & w_mul) + ff_mix;   -- 15bit ��̕␔
                    end if;
                end if;
            end if;
        end if;
    end process;

    --  wave out
    process( reset, clk21m )
    begin
        if( rising_edge(clk21m) )then
            if( reset = '1' )then
                ff_wave <= (others => '0');
            else
                if( ff_wave_ce_dl = '0' )then
                    if( ff_ch_num_dl = "000" )then
                        ff_wave <= ff_mix;  -- 15bit ��̕␔
                    else
                        --  hold
                    end if;
                end if;
            end if;
        end if;
    end process;

    wave <= ff_wave;
end rtl;
