---------------------------------------------
---------------------------------------------
-------- synchronized clock selector --------
---------------------------------------------
---------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity clock_selector is 
    port (  
                pi_rst_n        : in std_logic;
                pi_base_clk     : in std_logic;
                po_cpu_en       : out std_logic_vector (7 downto 0);
                po_rnd_en       : out std_logic_vector (3 downto 0)
        );
end clock_selector;

architecture rtl of clock_selector is

signal reg_cpu_en       : std_logic_vector (7 downto 0);
signal reg_rnd_en       : std_logic_vector (3 downto 0);

begin
    --Actual NES base clock =  21.477272 MHz
    --CPU clock = base clock / 12
    --PPU clock = base clock / 4
    --Actual NES CPU clock = 1.78 MHz (559 ns / cycle)
    --VGA clock = 25 MHz.

	---DE1 base clock 50 MHz
    ---motones sim project uses following clock.
    --cpu clock = base clock / 16
    --ppu clock = base clock / 8
    --vga clock = base clock / 2
    --emu ppu clock = base clock / 4

    po_cpu_en <= reg_cpu_en;
    
    cpu_clk_p : process (pi_rst_n, pi_base_clk)
    variable ref_cnt : integer range 0 to 31;
    begin
        if (pi_rst_n = '0') then
            reg_cpu_en <= (others => '0');
            ref_cnt := 0;
        else
            if (rising_edge(pi_base_clk)) then
                if (ref_cnt = 0) then
                    reg_cpu_en <= "00000001";
                elsif (ref_cnt = 4) then
                    reg_cpu_en <= "00000010";
                elsif (ref_cnt = 8) then
                    reg_cpu_en <= "00000100";
                elsif (ref_cnt = 12) then
                    reg_cpu_en <= "00001000";
                elsif (ref_cnt = 16) then
                    reg_cpu_en <= "00010000";
                elsif (ref_cnt = 20) then
                    reg_cpu_en <= "00100000";
                elsif (ref_cnt = 24) then
                    reg_cpu_en <= "01000000";
                elsif (ref_cnt = 28) then
                    reg_cpu_en <= "10000000";
                else
                    reg_cpu_en <= "00000000";
                end if;

                if (ref_cnt = 31) then
                    ref_cnt := 0;
                else
                    ref_cnt := ref_cnt + 1;
                end if;
            end if;
        end if;
    end process;

    --render clock timing enabler.
    po_rnd_en <= reg_rnd_en;

    ppu_clk_p : process (pi_rst_n, pi_base_clk)
    variable ref_cnt : integer range 0 to 3;
    begin
        if (pi_rst_n = '0') then
            reg_rnd_en <= (others => '0');
            ref_cnt := 0;
        else
            if (rising_edge(pi_base_clk)) then
                if (ref_cnt = 0) then
                    reg_rnd_en <= "0001";
                elsif (ref_cnt = 1) then
                    reg_rnd_en <= "0010";
                elsif (ref_cnt = 2) then
                    reg_rnd_en <= "0100";
                elsif (ref_cnt = 3) then
                    reg_rnd_en <= "1000";
                else
                    reg_rnd_en <= "0000";
                end if;
                
                if (ref_cnt = 3) then
                    ref_cnt := 0;
                else
                    ref_cnt := ref_cnt + 1;
                end if;
            end if;
        end if;
    end process;

end rtl;


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

entity chip_selector is 
    port (
                pi_rst_n        : in std_logic;
                pi_base_clk    : in std_logic;
                pi_addr         : in std_logic_vector (15 downto 0);
                po_rom_ce_n     : out std_logic;
                po_ram_ce_n     : out std_logic;
                po_ppu_ce_n     : out std_logic;
                po_apu_ce_n     : out std_logic
        );
end chip_selector;

architecture rtl of chip_selector is

signal reg_rom_ce_n      : std_logic;
signal reg_ram_ce_n      : std_logic;
signal reg_ppu_ce_n      : std_logic;
signal reg_apu_ce_n      : std_logic;

begin
    po_rom_ce_n <= reg_rom_ce_n;
    po_ram_ce_n <= reg_ram_ce_n;
    po_ppu_ce_n <= reg_ppu_ce_n;
    po_apu_ce_n <= reg_apu_ce_n;

    chip_sel_p : process (pi_rst_n, pi_base_clk)
    begin
        if (pi_rst_n = '0') then
            reg_rom_ce_n <= '1';
            reg_ram_ce_n <= '1';
            reg_ppu_ce_n <= '1';
            reg_apu_ce_n <= '1';
        else
            if (rising_edge(pi_base_clk)) then
                if (pi_addr(15) = '1') then
                    reg_rom_ce_n <= '0';
                else
                    reg_rom_ce_n <= '1';
                end if;

                if (pi_addr(15) = '0' and pi_addr(14) = '0' and pi_addr(13) = '1') then
                    reg_ppu_ce_n <= '0';
                else
                    reg_ppu_ce_n <= '1';
                end if;

                if (pi_addr(15) = '0' and pi_addr(14) = '1' and pi_addr(13) = '0') then
                    reg_apu_ce_n <= '0';
                else
                    reg_apu_ce_n <= '1';
                end if;

                if ((pi_addr(15) or pi_addr(14) or pi_addr(13)) = '0') then
                    reg_ram_ce_n <= '0';
                else
                    reg_ram_ce_n <= '1';
                end if;
            end if;
        end if;
    end process;

end rtl;



---------------------------------------------
---------------------------------------------
------------ vram chip selector -------------
---------------------------------------------
---------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity v_chip_selector is 
    port (
                pi_rst_n        : in std_logic;
                pi_base_clk     : in std_logic;
                pi_v_ce_n       : in std_logic;
                pi_v_addr       : in std_logic_vector (13 downto 0);
                pi_nt_v_mirror  : in std_logic;
                po_pt_ce_n      : out std_logic;
                po_nt0_ce_n     : out std_logic;
                po_nt1_ce_n     : out std_logic
        );
end v_chip_selector;

architecture rtl of v_chip_selector is

signal reg_pt_ce_n       : std_logic;
signal reg_nt0_ce_n      : std_logic;
signal reg_nt1_ce_n      : std_logic;

begin
    po_pt_ce_n <= reg_pt_ce_n;
    po_nt0_ce_n <= reg_nt0_ce_n;
    po_nt1_ce_n <= reg_nt1_ce_n;

    v_chip_sel_p : process (pi_rst_n, pi_base_clk)
    begin
        if (pi_rst_n = '0') then
            reg_pt_ce_n <= '1';
            reg_nt0_ce_n <= '1';
            reg_nt1_ce_n <= '1';
        else
            if (rising_edge(pi_base_clk)) then
                if (pi_v_ce_n = '0') then
                    if ((pi_v_addr(13) = '0')) then
                        reg_pt_ce_n <= '0';
                    else
                        reg_pt_ce_n <= '1';
                    end if;

                    if (pi_v_addr(13) = '0') then
                        reg_nt0_ce_n <= '1';
                    elsif (pi_v_addr(13 downto 8) = "111111") then
                        reg_nt0_ce_n <= '1';
                    elsif (((pi_v_addr(11) or pi_v_addr(10)) = '0') 
                            or (pi_nt_v_mirror = '1' and pi_v_addr(11) = '1' and pi_v_addr(10) = '0')
                            or (pi_nt_v_mirror = '0' and pi_v_addr(11) = '0' and pi_v_addr(10) = '1')) then
                        reg_nt0_ce_n <= '0';
                    else
                        reg_nt0_ce_n <= '1';
                    end if;

                    if (pi_v_addr(13) = '0') then
                        reg_nt1_ce_n <= '1';
                    elsif (pi_v_addr(13 downto 8) = "111111") then
                        reg_nt1_ce_n <= '1';
                    elsif (((pi_v_addr(11) and pi_v_addr(10)) = '1') 
                        or (pi_nt_v_mirror = '1' and pi_v_addr(11) = '0' and pi_v_addr(10) = '1')
                        or (pi_nt_v_mirror = '0' and pi_v_addr(11) = '1' and pi_v_addr(10) = '0')) then
                        reg_nt1_ce_n <= '0';
                    else
                        reg_nt1_ce_n <= '1';
                    end if;
                else
                    reg_pt_ce_n <= '1';
                    reg_nt0_ce_n <= '1';
                    reg_nt1_ce_n <= '1';
                end if;
            end if;
        end if;
    end process;

end rtl;
