----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    15:52:22 12/30/2008 
-- Design Name: 
-- Module Name:    toplevel - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;

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

entity keyboard is
  
  port (
    -- PS/2 Interface
    PS2_CLK     : inout std_logic;
    PS2_DATA    : inout std_logic;
    -- Z80 interface
    CLK_16M    : in    std_logic;
    IORQ_n     : in    std_logic;
    RD_n, WR_n : in    std_logic;
    DATA_I     : in    std_logic_vector(7 downto 0);
    DATA_O     : out   std_logic_vector(7 downto 0);
    -- DEBUG
    DEBUG      : out   std_logic_vector(7 downto 0)
--    DEBUGLED   : out   std_logic_vector(7 downto 0)
    );

end keyboard;

architecture Behavioral of keyboard is
type states is (ST_RESET, ST_IDLE, ST_READ, ST_PARITY, ST_ERROR, ST_STOP);
signal state : states := ST_RESET;
signal parity_r, up_r, ext_r : std_logic := '0';
signal key_in_r : std_logic_vector(7 downto 0);
signal count : unsigned(2 downto 0);
type line_lut_t is array (0 to 255) of integer range 0 to 9;
type bit_lut_t is array (0 to 255) of integer range 0 to 7;
signal line_lut : line_lut_t;
signal bit_lut : bit_lut_t;

signal ps2clk_s, ps2data_s : std_logic_vector(2 downto 0) := "111";
signal debug_evt_counter : std_logic_vector(7 downto 0) := X"00";

type key_buffer_t is array (0 to 9) of std_logic_vector(7 downto 0);
signal key_buffer : key_buffer_t;
signal keyb_row_r : unsigned(3 downto 0) := to_unsigned(0, 4);
signal lastin_r : std_logic_vector(1 downto 0) := "00";
begin  -- Behavioral
  --           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
  line_lut <= (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0,  -- 0_
               0, 0, 0, 0, 0, 5, 6, 0, 0, 0, 2, 3, 4, 4, 6, 0,  -- 1_
               0, 7, 1, 2, 3, 7, 5, 0, 0, 7, 7, 1, 1, 7, 1, 0,  -- 2_
               0, 2, 1, 1, 7, 2, 2, 0, 0, 0, 3, 2, 3, 3, 4, 0,  -- 3_
               
               0, 4, 3, 4, 5, 6, 5, 0, 0, 5, 6, 4, 5, 6, 8, 0,  -- 4_
               0, 0, 6, 0, 6, 0, 0, 0, 0, 0, 8, 7, 0, 5, 0, 0,  -- 5_
               0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0,  -- 6_
               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  -- 7_
               
               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  -- e0 0_
               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  -- e0 1_
               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  -- e0 2_
               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  -- e0 3_
               
               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  -- e0 4_
               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  -- e0 5_
               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0,  -- e0 6_
               0, 0, 3, 0, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);  -- e0 7_

  --          0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
  bit_lut <= (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0,  -- 0_
              0, 0, 4, 0, 0, 4, 4, 0, 0, 0, 5, 4, 4, 3, 3, 0,  -- 1_
              0, 3, 4, 3, 3, 2, 3, 0, 0, 4, 1, 3, 5, 5, 2, 0,  -- 2_
              0, 1, 1, 0, 0, 4, 2, 0, 0, 0, 1, 0, 5, 2, 2, 0,  -- 3_
              
              0, 1, 0, 5, 5, 2, 2, 0, 0, 1, 1, 0, 0, 5, 2, 0,  -- 4_
              0, 0, 0, 0, 6, 0, 0, 0, 0, 4, 1, 6, 0, 6, 0, 0,  -- 5_
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  -- 6_
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  -- 7_
              
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  -- e0 0_
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  -- e0 1_
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  -- e0 2_
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  -- e0 3_
              
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  -- e0 4_
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  -- e0 5_
              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0,  -- e0 6_
              0, 0, 6, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);  -- e0 7_
  
  z80bus: process(CLK_16M)
  begin
    if rising_edge(CLK_16M) then
      if (IORQ_n = '0') and (WR_n = '0') then
        if (DATA_I(0) = '0') and (lastin_r(0) = '1') then
          if keyb_row_r < 9 then
            keyb_row_r <= keyb_row_r + 1;
          end if;
        elsif (DATA_I(1) = '0') and (lastin_r(1) = '1') then
          keyb_row_r <= to_unsigned(0, keyb_row_r'length);
        end if;
        lastin_r <= DATA_I(1 downto 0);
      end if;
    end if;
  end process;

  DATA_O <= key_buffer(to_integer(keyb_row_r));

  process  (CLK_16M)
  begin
    if rising_edge(CLK_16M) then
      ps2clk_s <= ps2clk_s(1 downto 0) & PS2_CLK;
      ps2data_s <= ps2data_s(1 downto 0) & PS2_DATA;
    end if;
  end process;
    
  fsm: process (CLK_16M)
  variable next_state : states;
  variable line_v : integer range 0 to 8;
  variable bit_v : integer range 0 to 7;
  begin
    if rising_edge(CLK_16M) then
      next_state := state;
      case state is
        when ST_RESET =>
          key_buffer <= (X"FF", X"FF", X"FF", X"FF",
                         X"FF", X"FF", X"FF", X"FF",
                         X"FF", X"FF");
          next_state := ST_IDLE;
        when ST_IDLE =>
          if (ps2clk_s(2) = '1') and (ps2clk_s(1) = '0') then  -- falling edge
            if ps2data_s(2) = '0' then
              count <= to_unsigned(0, count'length);
              parity_r <= '0';
              next_state := ST_READ;
            end if;
          end if;
        when ST_READ =>
          if (ps2clk_s(2) = '1') and (ps2clk_s(1) = '0') then  -- falling edge
            key_in_r(to_integer(count)) <= ps2data_s(2);
            parity_r <= parity_r xor ps2data_s(2);
            if count = 7 then
              next_state := ST_PARITY;
            else
              count <= count + 1;
            end if;
          end if;
        when ST_PARITY =>
          if (ps2clk_s(2) = '1') and (ps2clk_s(1) = '0') then  -- falling edge
            if parity_r = ps2data_s(2) then
--              ext_r <= '0';
--              up_r <= '0';
              next_state := ST_ERROR;
            else
              if key_in_r = X"E0" then
                ext_r <= '1';
              elsif key_in_r = X"F0" then
                up_r <= '1';
              else
                -- process key code
                line_v := line_lut(conv_integer(ext_r & key_in_r(6 downto 0)));
                bit_v := bit_lut(conv_integer(ext_r & key_in_r(6 downto 0)));
                if not ((line_v = 0) and (bit_v = 0)) then
                  key_buffer(line_v)(bit_v) <= up_r;
                end if;
                ext_r <= '0';
                up_r <= '0';
              end if;
              next_state := ST_STOP;
            end if;
          end if;
        when ST_ERROR =>
          debug_evt_counter <= debug_evt_counter + 1;
          next_state := ST_IDLE;
        when ST_STOP =>
          next_state := ST_IDLE;
        when others => null;
      end case;
      state <= next_state;
    end if;
  end process;

--  DEBUGLED <= debug_evt_counter;
  
  PS2_CLK <= 'Z';
  PS2_DATA <= 'Z';
end Behavioral;
