/*
* Communicates with SNES (and probably also NES) controller
* Based on https://opencores.org/projects/nescontroller by Yvo Zoer
*  with modifications to make it work for a single SNES controller
*  and to make the interface work nicer with the MU
*/

module NESpadReader (
    input clk,
    input reset,
    input frame, //start
    output reg nesl = 1'b0,
    output reg nesc = 1'b0,
    input nesd,
    output reg [15:0] nesState = 16'd0,
    output done
    );

    
 
    // states for state-machine
    parameter STATE_IDLE        = 3'd0;
    parameter STATE_LATCH_HI    = 3'd1; 
    parameter STATE_LATCH_LO    = 3'd2;
    parameter STATE_CLOCK_LO    = 3'd3;
    parameter STATE_CLOCK_HI    = 3'd4;
    parameter STATE_DONE        = 3'd5;

    assign done = (state == STATE_DONE);
 
    // start bit to catch single clock frame event
    reg start = 1'b0;
    always @(posedge clk)
        if (reset)
            start <= 1'b0;
        else if ( frame )
            start <= 1'b1;
        else if ( state != STATE_IDLE )
            start <= 1'b0;
 
    // clock divider / enable -- clock needs to be around 1.5mhz or lower
    reg [5:0] clkdiv = 6'd0;
    always @(posedge clk)
        if (reset)
            clkdiv <= 6'd0;
        else
            clkdiv <= clkdiv + 1'd1;
 
    wire enable = &clkdiv;
 
    // main state machine
    reg [2:0] state = 3'd0;
    reg [2:0] next_state = 3'd0;
    always @(*)
        case (state)
            STATE_IDLE : begin
                if ( start )
                    next_state = STATE_LATCH_HI;
                else
                    next_state = STATE_IDLE;
                end
            STATE_LATCH_HI : next_state = STATE_LATCH_LO;
            STATE_LATCH_LO : next_state = STATE_CLOCK_LO;
            STATE_CLOCK_LO : next_state = STATE_CLOCK_HI;
            STATE_CLOCK_HI : begin
                if ( &count )
                begin
                    next_state = STATE_DONE;
                end
                else
                    next_state = STATE_CLOCK_LO;
                end
            STATE_DONE : begin
                next_state = STATE_IDLE;
                end
            default : begin
                next_state = STATE_IDLE;
                end
        endcase
 

    always @(posedge clk)
        if (reset)
            nesl <= 1'b0;
        else
            nesl <= (state == STATE_LATCH_HI);
 
    always @(posedge clk)
        if (reset)
            nesc <= 1'b1;
        else
            nesc = (state == STATE_CLOCK_LO ) ? 1'b0 : 1'b1;
 
    reg [3:0] count = 4'd0;
    reg [3:0] next_count = 4'd0;
    always @(*)
        if ( state == STATE_CLOCK_HI )
            next_count = count + 4'd1;
        else
            next_count = count;
 
    reg [15:0] next_nesState = 16'd0;
    always @(*)
        if ( state == STATE_CLOCK_LO )
            begin
                next_nesState = { nesState[14:0], nesd };
            end
        else
            begin
                next_nesState = nesState;
            end
 
    always @(posedge clk)
        if (reset)
            begin
                state <= STATE_IDLE;
                count <= 4'd0;
                nesState <= 16'd0;
            end
        else if ( enable )
            begin
                state <= next_state;
                count <= next_count;
                nesState <= next_nesState;
            end
 
endmodule