---------------------------------------------------------------------------
-- NES-Controller Module
---------------------------------------------------------------------------
-- This file is a part of "Aeon Lite" project
-- Dmitriy Schapotschkin aka ILoveSpeccy '2014
-- ilovespeccy@speccyland.net
-- Project homepage: www.speccyland.net
---------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

------------------
-- Bit - Button --
-- (1 = pressed)
------------------
--  7     A
--  6     B
--  5     Select
--  4     Start
--  3     Up
--  2     Down
--  1     Left
--  0     Right
------------------

entity nes_gamepad is
generic (
    CLK_FREQ        : integer   := 25000000;
    TICK_FREQ       : integer   := 20000 );
port ( 
    CLK             : in     std_logic;
    RESET           : in     std_logic;
    
    JOY_CLK         : out    std_logic;
    JOY_LOAD        : out    std_logic;
    JOY_DATA0       : in     std_logic;
    JOY_DATA1       : in     std_logic;
   
    JOY0_BUTTONS    : out    std_logic_vector(7 downto 0);
    JOY1_BUTTONS    : out    std_logic_vector(7 downto 0);
    
    JOY0_CONNECTED  : out    std_logic; -- 1 when gamepad connected
    JOY1_CONNECTED  : out    std_logic );
end nes_gamepad;

architecture RTL of nes_gamepad is

    signal TICK     : integer range 0 to (CLK_FREQ / TICK_FREQ);
    signal STATE    : integer range 0 to 17;
    signal DATA0    : std_logic_vector(7 downto 0); 
    signal DATA1    : std_logic_vector(7 downto 0); 
   
begin

process (CLK)
begin
    if rising_edge(CLK) then
        if RESET = '1' then
            STATE <= 0;
            JOY_CLK <= '0';
            JOY_LOAD <= '0';
            TICK <= 0;
            JOY0_BUTTONS <= "00000000";
            JOY0_BUTTONS <= "00000000";
            JOY0_CONNECTED <= '0';
            JOY1_CONNECTED <= '0';
        else
            TICK <= TICK + 1;
            if TICK = (CLK_FREQ / TICK_FREQ) then
                TICK <= 0;
                STATE <= STATE + 1;
            
                case STATE is
               
                    when 0 =>
                        JOY_LOAD <= '1';

                    when 1 => 
                        JOY_LOAD <= '0';
                        DATA0(7) <= JOY_DATA0;
                        DATA1(7) <= JOY_DATA1;

                    when 2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 => 
                        JOY_CLK <= '1';
                  
                    when 3 =>
                        JOY_CLK <= '0';
                        DATA0(6) <= JOY_DATA0;
                        DATA1(6) <= JOY_DATA1;
                              
                    when 5 =>
                        JOY_CLK <= '0';
                        DATA0(5) <= JOY_DATA0;
                        DATA1(5) <= JOY_DATA1;
            
                    when 7 =>
                        JOY_CLK <= '0';
                        DATA0(4) <= JOY_DATA0;
                        DATA1(4) <= JOY_DATA1;

                    when 9 =>
                        JOY_CLK <= '0';
                        DATA0(3) <= JOY_DATA0;
                        DATA1(3) <= JOY_DATA1;
                  
                    when 11 =>
                        JOY_CLK <= '0';
                        DATA0(2) <= JOY_DATA0;
                        DATA1(2) <= JOY_DATA1;
            
                    when 13 =>
                        JOY_CLK <= '0';
                        DATA0(1) <= JOY_DATA0;
                        DATA1(1) <= JOY_DATA1;
                  
                    when 15 =>
                        JOY_CLK <= '0';
                        DATA0(0) <= JOY_DATA0;
                        DATA1(0) <= JOY_DATA1;

                    when 17 =>
                        JOY_CLK <= '0';
                        JOY0_BUTTONS <= "00000000";
                        JOY1_BUTTONS <= "00000000";
                        JOY0_CONNECTED <= '0';
                        JOY1_CONNECTED <= '0';
                        STATE <= 0;

                        if DATA0 /= "00000000" then -- gamepad connected
                            JOY0_BUTTONS <= not DATA0;
                            JOY0_CONNECTED <= '1';
                        end if;

                        if DATA1 /= "00000000" then -- gamepad connected
                            JOY1_BUTTONS <= not DATA1;
                            JOY1_CONNECTED <= '1';
                        end if;

                    when OTHERS =>
                        NULL;
                  
                end case;
            end if;
        end if;
    end if;
end process; 
   
end RTL;
