//============================================================================
// ZX Spectrum keyboard input
//
// This module takes scan-codes from the attached PS/2 keyboard and sets
// corresponding ZX key bitfields which are read by the 'IN' instructions.
//
// PS/2      |  ZX Spectrum
// ----------+-----------------
// CTRL      |  CAPS SHIFT
// ALT       |  SYMBOL SHIFT
//
// For convenience, in addition to regular alpha-numeric keys, this code
// simulates several other standard symbols on the PS/2 keyboard.
//
// PS/2      |  ZX Spectrum
// ----------+-----------------
// BACKSPACE |  DELETE
// Arrows Left, Right, Up, Down
// ESC       |  BREAK (CAPS+SPACE)
// SHIFT => PS/2 shift for additional keys: -_ += ;: "' <, >. ?/
//
//  Copyright (C) 2014-2016  Goran Devic
//
//  This program is free software; you can redistribute it and/or modify it
//  under the terms of the GNU General Public License as published by the Free
//  Software Foundation; either version 2 of the License, or (at your option)
//  any later version.
//
//  This program is distributed in the hope that it will be useful, but WITHOUT
//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
//  more details.
//
//  You should have received a copy of the GNU General Public License along
//  with this program; if not, write to the Free Software Foundation, Inc.,
//  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//============================================================================
module zx_keyboard
(
    input wire clk,
    input wire nreset,          // Active low reset

    // Output ZX-specific keyboard codes when requested by the ULA access
    input wire [15:0] A,        // Address bus
    output wire [4:0] key_row,  // Output the state of a requested row of keys

    // Input key scan codes from the PS/2 keyboard
    input wire [7:0] scan_code, // PS/2 scan-code
    input wire scan_code_ready, // Active for 1 clock: a scan code is ready
    input wire scan_code_error, // Error receiving PS/2 keyboard data
    output wire pressed         // Signal that a key is pressed
);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
reg [4:0] keys [0:7];       // 8 rows of 5 bits each: contains 0 for a pressed key at a specific location, 1 otherwise

reg released;               // Tracks "released" scan code (F0): contains 0 when a key is pressed, 1 otherwise
reg extended;               // Tracks "extended" scan code (E0)
reg shifted;                // Tracks local "shifted" state

// Calculate a "key is pressed" signal
assign pressed = ~(&keys[7] & &keys[6] & &keys[5] & &keys[4] & &keys[3] & &keys[2] & &keys[1] & &keys[0]);

// Output requested row of keys continously
assign key_row =
    (~A[8]  ? keys[0] : 5'b11111) &
    (~A[9]  ? keys[1] : 5'b11111) &
    (~A[10] ? keys[2] : 5'b11111) &
    (~A[11] ? keys[3] : 5'b11111) &
    (~A[12] ? keys[4] : 5'b11111) &
    (~A[13] ? keys[5] : 5'b11111) &
    (~A[14] ? keys[6] : 5'b11111) &
    (~A[15] ? keys[7] : 5'b11111);

always @(posedge clk or negedge nreset)
begin
    if (!nreset) begin
        released <= 0;
        extended <= 0;
        shifted  <= 0;

        keys[0] <= '1;
        keys[1] <= '1;
        keys[2] <= '1;
        keys[3] <= '1;
        keys[4] <= '1;
        keys[5] <= '1;
        keys[6] <= '1;
        keys[7] <= '1;
    end else
    if (scan_code_ready) begin
        if (scan_code==8'hE0) // Extended code prefix byte
            extended <= 1;
        else if (scan_code==8'hF0) // Break code prefix byte
            released <= 1;
        else begin
            // Cancel release/extended flags for the next clock
            extended <= 0;
            released <= 0;

            if (extended) begin
                // Extended keys
                case (scan_code)
                    8'h6B:  begin                       // LEFT
                            keys[0][0] <= released;     // CAPS SHIFT
                            keys[3][4] <= released;     // 5
                            end
                    8'h72:  begin                       // DOWN
                            keys[0][0] <= released;     // CAPS SHIFT
                            keys[4][4] <= released;     // 6
                            end
                    8'h75:  begin                       // UP
                            keys[0][0] <= released;     // CAPS SHIFT
                            keys[4][3] <= released;     // 7
                            end
                    8'h74:  begin                       // RIGHT
                            keys[0][0] <= released;     // CAPS SHIFT
                            keys[4][2] <= released;     // 8
                            end
                endcase
            end
            else begin
                // For each PS/2 scan-code, set the ZX keyboard matrix state
                case (scan_code)
                    8'h12:  shifted <= !released;       // Local SHIFT key (left)
                    8'h59:  shifted <= !released;       // Local SHIFT key (right)

                    8'h14:  keys[0][0] <= released;     // CAPS SHIFT = Left or right Ctrl

                    8'h1A:  keys[0][1] <= released;     // Z
                    8'h22:  keys[0][2] <= released;     // X
                    8'h21:  keys[0][3] <= released;     // C
                    8'h2A:  keys[0][4] <= released;     // V

                    8'h1C:  keys[1][0] <= released;     // A
                    8'h1B:  keys[1][1] <= released;     // S
                    8'h23:  keys[1][2] <= released;     // D
                    8'h2B:  keys[1][3] <= released;     // F
                    8'h34:  keys[1][4] <= released;     // G

                    8'h15:  keys[2][0] <= released;     // Q
                    8'h1D:  keys[2][1] <= released;     // W
                    8'h24:  keys[2][2] <= released;     // E
                    8'h2D:  keys[2][3] <= released;     // R
                    8'h2C:  keys[2][4] <= released;     // T

                    8'h16:  keys[3][0] <= released;     // 1
                    8'h1E:  keys[3][1] <= released;     // 2
                    8'h26:  keys[3][2] <= released;     // 3
                    8'h25:  keys[3][3] <= released;     // 4
                    8'h2E:  keys[3][4] <= released;     // 5

                    8'h45:  keys[4][0] <= released;     // 0
                    8'h46:  keys[4][1] <= released;     // 9
                    8'h3E:  keys[4][2] <= released;     // 8
                    8'h3D:  keys[4][3] <= released;     // 7
                    8'h36:  keys[4][4] <= released;     // 6

                    8'h4D:  keys[5][0] <= released;     // P
                    8'h44:  keys[5][1] <= released;     // O
                    8'h43:  keys[5][2] <= released;     // I
                    8'h3C:  keys[5][3] <= released;     // U
                    8'h35:  keys[5][4] <= released;     // Y

                    8'h5A:  keys[6][0] <= released;     // ENTER
                    8'h4B:  keys[6][1] <= released;     // L
                    8'h42:  keys[6][2] <= released;     // K
                    8'h3B:  keys[6][3] <= released;     // J
                    8'h33:  keys[6][4] <= released;     // H

                    8'h29:  keys[7][0] <= released;     // SPACE
                    8'h11:  keys[7][1] <= released;     // SYMBOL SHIFT (Red) = Left and right Alt
                    8'h3A:  keys[7][2] <= released;     // M
                    8'h31:  keys[7][3] <= released;     // N
                    8'h32:  keys[7][4] <= released;     // B

                    8'h66:  begin                       // BACKSPACE
                            keys[0][0] <= released;
                            keys[4][0] <= released;
                            end
                    8'h76:  begin                       // ESC -> BREAK
                            keys[0][0] <= released;     // CAPS SHIFT
                            keys[7][0] <= released;     // SPACE
                            end
                    // With shifted keys, we need to make inactive (set to 1) other corresponding key
                    // Otherwise, it will stay active if the shift was released first
                    8'h4E:  begin                       // - or (shifted) _
                            keys[7][1] <= released;     // SYMBOL SHIFT (Red)
                            keys[4][0] <= shifted ? released : 1'b1;     // 0
                            keys[6][3] <= shifted ? 1'b1 : released;     // J
                            end
                    8'h55:  begin                       // = or (shifted) +
                            keys[7][1] <= released;     // SYMBOL SHIFT (Red)
                            keys[6][2] <= shifted ? released : 1'b1;     // K
                            keys[6][1] <= shifted ? 1'b1 : released;     // L
                            end
                    8'h52:  begin                       // ' or (shifted) "
                            keys[7][1] <= released;     // SYMBOL SHIFT (Red)
                            keys[5][0] <= shifted ? released : 1'b1;     // P
                            keys[4][3] <= shifted ? 1'b1 : released;     // 7
                            end
                    8'h4C:  begin                       // ; or (shifted) :
                            keys[7][1] <= released;     // SYMBOL SHIFT (Red)
                            keys[0][1] <= shifted ? released : 1'b1;     // Z
                            keys[5][1] <= shifted ? 1'b1 : released;     // O
                            end
                    8'h41:  begin                       // , or (shifted) <
                            keys[7][1] <= released;     // SYMBOL SHIFT (Red)
                            keys[2][3] <= shifted ? released : 1'b1;     // R
                            keys[7][3] <= shifted ? 1'b1 : released;     // N
                            end
                    8'h49:  begin                       // . or (shifted) >
                            keys[7][1] <= released;     // SYMBOL SHIFT (Red)
                            keys[2][4] <= shifted ? released : 1'b1;     // T
                            keys[7][2] <= shifted ? 1'b1 : released;     // M
                            end
                    8'h4A:  begin                       // / or (shifted) ?
                            keys[7][1] <= released;     // SYMBOL SHIFT (Red)
                            keys[0][3] <= shifted ? released : 1'b1;     // C
                            keys[0][4] <= shifted ? 1'b1 : released;     // V
                            end
                endcase
            end
        end
    end
end

endmodule
