`timescale 1ns / 1ps `default_nettype none // This file is part of the ZXUNO Spectrum core. // Creation date is 15:52:26 2015-06-07 by Miguel Angel Rodriguez Jodar // (c)2014-2020 ZXUNO association. // ZXUNO official repository: http://svn.zxuno.com/svn/zxuno // Username: guest Password: zxuno // Github repository for this core: https://github.com/mcleod-ideafix/zxuno_spectrum_core // // ZXUNO Spectrum core 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 3 of the License, or // (at your option) any later version. // // ZXUNO Spectrum core 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 the ZXUNO Spectrum core. If not, see . // // Any distributed copy of this file must keep this notice intact. module joystick_protocols ( input wire clk, //-- cpu interface input wire [15:0] a, input wire iorq_n, input wire rd_n, input wire [7:0] din, output reg [7:0] dout, output reg oe, //-- interface with ZXUNO reg bank input wire [7:0] zxuno_addr, input wire zxuno_regrd, input wire zxuno_regwr, //-- actual joystick and keyboard signals input wire [5:0] kbdjoy_in, input wire [5:0] db9joy1_in, input wire [5:0] db9joy2_in, output wire joy1fire3, input wire [4:0] kbdcol_in, output reg [4:0] kbdcol_out, input wire vertical_retrace_int_n, // this is used as base clock for autofire input wire joy_splitter ); `include "config.vh" localparam SINCLAIRP1ADDR = 12, SINCLAIRP2ADDR = 11, SINCLAIRADDRE = 8, //sinclair extendido, semifila ZXCV DISABLED = 3'h0, KEMPSTON = 3'h1, SINCLAIRP1 = 3'h2, SINCLAIRP2 = 3'h3, CURSOR = 3'h4, FULLER = 3'h5, // Protocolo OPQASPACEM KMAPOPQA = 3'h6, KMAPOP = 13, KMAPQ = 10, KMAPA = 9, KMAPSPACEM = 15; `ifdef JOYSPLITTER_SUPPORT // Joystick multiplex (hardware joystick splitter) reg joySelector = 1'b0; assign joy1fire3 = (joy_splitter == 1'b1) ? joySelector : 1'b1; reg [5:0] db9joy1_muxed = 6'b111111; reg [5:0] db9joy2_muxed = 6'b111111; reg [18:0] joyMuxerCounter = 19'd0; always @(posedge clk) begin if ( joyMuxerCounter == 19'd0 ) begin // 28 MHz / 140000 = 200 Hz, 100 Hz for each joystick. joyMuxerCounter <= 19'd140000; if ((joy_splitter == 1'b0) || (joySelector == 1'b1)) begin db9joy1_muxed <= db9joy1_in; end else begin db9joy2_muxed <= db9joy1_in; end joySelector <= ~joySelector; end else begin joyMuxerCounter <= joyMuxerCounter - 19'd1; end end `else assign joy1fire3 = 1'b1; `endif // Input format: FUDLR . 0=pressed, 1=released reg db9joyup; reg db9joydown; reg db9joyleft; reg db9joyright; reg db9joyfire1; reg db9joyfire2; reg kbdjoyup; reg kbdjoydown; reg kbdjoyleft; reg kbdjoyright; reg kbdjoyfire1; reg kbdjoyfire2; `ifdef JOYSPLITTER_SUPPORT always @* begin {db9joyfire2,db9joyfire1,db9joyup,db9joydown,db9joyleft,db9joyright} <= ~db9joy1_muxed; {kbdjoyfire2,kbdjoyfire1,kbdjoyup,kbdjoydown,kbdjoyleft,kbdjoyright} <= kbdjoy_in | ~db9joy2_muxed | ~db9joy2_in; end `else always @* begin {db9joyfire2,db9joyfire1,db9joyup,db9joydown,db9joyleft,db9joyright} <= ~db9joy1_in; {kbdjoyfire2,kbdjoyfire1,kbdjoyup,kbdjoydown,kbdjoyleft,kbdjoyright} <= kbdjoy_in | ~db9joy2_in; end `endif // Update JOYCONF from CPU reg [7:0] joyconf = {1'b0,SINCLAIRP1, 1'b0,KEMPSTON}; always @(posedge clk) begin if (zxuno_addr==JOYCONFADDR && zxuno_regwr==1'b1) joyconf <= din; end // Autofire stuff reg [2:0] cont_autofire = 3'b000; reg edge_detect = 1'b0; wire autofire = cont_autofire[2]; always @(posedge clk) begin edge_detect <= vertical_retrace_int_n; if ({edge_detect,vertical_retrace_int_n} == 2'b01) cont_autofire <= cont_autofire + 3'd1; // count only on raising edge of vertical retrace int end wire kbdjoyfire_processed = (joyconf[3]==1'b0)? kbdjoyfire1 : kbdjoyfire1 & autofire; /* // Config bit joyconf[7] is db9 joystick autofire enable. Except if splitter supported, where it is 'splitter enabled' bit. // In that case joyconf[3] is autofire enable for both joysticks. `ifdef JOYSPLITTER_SUPPORT wire db9joyfire_processed = (joyconf[3]==1'b0)? db9joyfire1 : db9joyfire1 & autofire; `else wire db9joyfire_processed = (joyconf[7]==1'b0)? db9joyfire1 : db9joyfire1 & autofire; `endif */ wire db9joyfire_processed = (joyconf[7]==1'b0)? db9joyfire1 : db9joyfire1 & autofire; always @* begin oe = 1'b0; dout = 8'hFF; kbdcol_out = kbdcol_in; if (zxuno_addr==JOYCONFADDR && zxuno_regrd==1'b1) begin // lectura específica de I/O de ZXUNO oe = 1'b1; dout = joyconf; end else if (iorq_n == 1'b0 && rd_n == 1'b0) begin // lectura genérica de I/O if ((a[7:0] == KEMPSTONADDR1) || (a[7:0] == KEMPSTONADDR2)) begin dout = 8'h00; oe = 1'b1; if (joyconf[2:0] == KEMPSTON) begin dout = dout | {2'b00, kbdjoyfire2, kbdjoyfire_processed, kbdjoyup, kbdjoydown, kbdjoyleft, kbdjoyright}; end if (joyconf[6:4] == KEMPSTON) begin dout = dout | {2'b00, db9joyfire2, db9joyfire_processed, db9joyup, db9joydown, db9joyleft, db9joyright}; end if ((joyconf[2:0] != KEMPSTON) && (joyconf[6:4] != KEMPSTON)) begin dout = 8'hFF; end end else if (a[7:0] == FULLERADDR) begin dout = 8'hFF; oe = 1'b1; if (joyconf[2:0] == FULLER) begin dout = dout & {~kbdjoyfire_processed, ~kbdjoyfire2, 2'b11, ~kbdjoyright, ~kbdjoyleft, ~kbdjoydown, ~kbdjoyup}; end if (joyconf[6:4] == FULLER) begin dout = dout & {~db9joyfire_processed, ~db9joyfire2, 2'b11, ~db9joyright, ~db9joyleft, ~db9joydown, ~db9joyup}; end end else if (a[0] == 1'b0) begin // lectura de I/O de teclado if (a[SINCLAIRP1ADDR]==1'b0) begin if (joyconf[2:0]==SINCLAIRP1) kbdcol_out = kbdcol_out & {~kbdjoyleft,~kbdjoyright,~kbdjoydown,~kbdjoyup,~kbdjoyfire_processed}; if (joyconf[6:4]==SINCLAIRP1) kbdcol_out = kbdcol_out & {~db9joyleft,~db9joyright,~db9joydown,~db9joyup,~db9joyfire_processed}; if (joyconf[2:0]==CURSOR) kbdcol_out = kbdcol_out & {~kbdjoydown,~kbdjoyup,~kbdjoyright,~kbdjoyfire2,~kbdjoyfire_processed}; if (joyconf[6:4]==CURSOR) kbdcol_out = kbdcol_out & {~db9joydown,~db9joyup,~db9joyright,~db9joyfire2,~db9joyfire_processed}; end if (a[SINCLAIRP2ADDR]==1'b0) begin if (joyconf[2:0]==SINCLAIRP2) kbdcol_out = kbdcol_out & {~kbdjoyfire_processed,~kbdjoyup,~kbdjoydown,~kbdjoyright,~kbdjoyleft}; if (joyconf[6:4]==SINCLAIRP2) kbdcol_out = kbdcol_out & {~db9joyfire_processed,~db9joyup,~db9joydown,~db9joyright,~db9joyleft}; if (joyconf[2:0]==CURSOR) kbdcol_out = kbdcol_out & {~kbdjoyleft,4'b1111}; if (joyconf[6:4]==CURSOR) kbdcol_out = kbdcol_out & {~db9joyleft,4'b1111}; end //Sinclair extendido,Z-X if (a[SINCLAIRADDRE]==1'b0) begin if (joyconf[6:4]==SINCLAIRP1) kbdcol_out = kbdcol_out & {2'b11, ~db9joyfire2, 2'b11}; if (joyconf[6:4]==SINCLAIRP2) kbdcol_out = kbdcol_out & {3'b111, ~db9joyfire2, 1'b1}; if (joyconf[2:0]==SINCLAIRP1) kbdcol_out = kbdcol_out & {2'b11, ~kbdjoyfire2, 2'b11}; if (joyconf[2:0]==SINCLAIRP2) kbdcol_out = kbdcol_out & {3'b111, ~kbdjoyfire2, 1'b1}; end // //Protocolo OPQASPACEM if (a[KMAPOP]==1'b0) begin if (joyconf[6:4]==KMAPOPQA) kbdcol_out = kbdcol_out & {3'b111, ~db9joyleft, ~db9joyright}; if (joyconf[2:0]==KMAPOPQA) kbdcol_out = kbdcol_out & {3'b111, ~kbdjoyleft, ~kbdjoyright}; end if (a[KMAPQ]==1'b0) begin if (joyconf[6:4]==KMAPOPQA) kbdcol_out = kbdcol_out & {4'b1111, ~db9joyup}; if (joyconf[2:0]==KMAPOPQA) kbdcol_out = kbdcol_out & {4'b1111, ~kbdjoyup}; end if (a[KMAPA]==1'b0) begin if (joyconf[6:4]==KMAPOPQA) kbdcol_out = kbdcol_out & {4'b1111, ~db9joydown}; if (joyconf[2:0]==KMAPOPQA) kbdcol_out = kbdcol_out & {4'b1111, ~kbdjoydown}; end if (a[KMAPSPACEM]==1'b0) begin if (joyconf[6:4]==KMAPOPQA) kbdcol_out = kbdcol_out & {2'b11, ~db9joyfire2, 1'b1, ~db9joyfire_processed}; if (joyconf[2:0]==KMAPOPQA) kbdcol_out = kbdcol_out & {2'b11, ~kbdjoyfire2, 1'b1, ~kbdjoyfire_processed}; end // end // fin de lectura de I/O de teclado end // fin lectura genérica de I/O end endmodule