// ZX-Evo Base Configuration (c) NedoPC 2008,2009,2010,2011,2012,2013,2014
//
// NMI generation
/*
This file is part of ZX-Evo Base Configuration firmware.
ZX-Evo Base Configuration firmware 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.
ZX-Evo Base Configuration firmware 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 ZX-Evo Base Configuration firmware.
If not, see <http://www.gnu.org/licenses/>.
*/
`include "../include/tune.v"
module znmi
(
input wire rst_n,
input wire fclk,
input wire zpos,
input wire zneg,
input wire int_start, // when INT starts
input wire [ 1:0] set_nmi, // NMI requests from slavespi and #BF port
input wire imm_nmi, // immediate NMI from breakpoint
input wire clr_nmi, // clear nmi: from zports, pulsed at out to #xxBE
input wire rfsh_n,
input wire m1_n,
input wire mreq_n,
input wire csrom,
input wire [15:0] a,
output reg in_nmi, // when 1, there must be last (#FF) ram page in 0000-3FFF
output wire gen_nmi // NMI generator: when 1, NMI_N=0, otherwise NMI_N=Z
);
reg [1:0] set_nmi_r;
wire set_nmi_now;
reg imm_nmi_r;
wire imm_nmi_now;
reg pending_nmi;
reg in_nmi_2; // active (=1) when NMIed to ROM, after 0066 M1 becomes 0,
// but in_nmi becomes 1 -- ROM switches to #FF RAM
reg [2:0] nmi_count;
reg [1:0] clr_count;
reg pending_clr;
reg last_m1_rom;
reg last_m1_0066;
wire nmi_start;
//remember whether last M1 opcode read was from ROM or RAM
reg m1_n_reg, mreq_n_reg;
reg [1:0] rfsh_n_reg;
always @(posedge fclk) if( zpos )
rfsh_n_reg[0] <= rfsh_n;
always @(posedge fclk)
rfsh_n_reg[1] <= rfsh_n_reg[0];
always @(posedge fclk) if( zpos )
m1_n_reg <= m1_n;
always @(posedge fclk) if( zneg )
mreq_n_reg <= mreq_n;
wire was_m1 = ~(m1_n_reg | mreq_n_reg);
reg was_m1_reg;
always @(posedge fclk)
was_m1_reg <= was_m1;
always @(posedge fclk)
if( was_m1 && (!was_m1_reg) )
last_m1_rom <= csrom && (a[15:14]==2'b00);
always @(posedge fclk)
if( was_m1 && (!was_m1_reg) )
last_m1_0066 <= ( a[15:0]==16'h0066 );
always @(posedge fclk)
set_nmi_r <= set_nmi;
//
assign set_nmi_now = | (set_nmi_r & (~set_nmi) );
always @(posedge fclk)
imm_nmi_r <= imm_nmi;
//
assign imm_nmi_now = | ( (~imm_nmi_r) & imm_nmi );
always @(posedge fclk, negedge rst_n)
if( !rst_n )
pending_nmi <= 1'b0;
else // posedge clk
begin
if( int_start )
pending_nmi <= 1'b0;
else if( set_nmi_now )
pending_nmi <= 1'b1;
end
// actual nmi start
assign nmi_start = (pending_nmi && int_start) || imm_nmi_now;
always @(posedge fclk)
if( clr_nmi )
clr_count <= 2'd3;
else if( rfsh_n_reg[1] && (!rfsh_n_reg[0]) && clr_count[1] )
clr_count <= clr_count - 2'd1;
always @(posedge fclk)
if( clr_nmi )
pending_clr <= 1'b1;
else if( !clr_count[1] )
pending_clr <= 1'b0;
always @(posedge fclk, negedge rst_n)
if( !rst_n )
in_nmi_2 <= 1'b0;
else // posedge fclk
begin
if( nmi_start && (!in_nmi) /*&& last_m1_rom*/ ) // fix for NMI page after execution of 0066 opcode everywhere
in_nmi_2 <= 1'b1;
else if( rfsh_n_reg[1] && (!rfsh_n_reg[0]) && last_m1_0066 )
in_nmi_2 <= 1'b0;
end
always @(posedge fclk, negedge rst_n)
if( !rst_n )
in_nmi <= 1'b0;
else // posedge clk
begin
if( pending_clr && (!clr_count[1]) )
in_nmi <= 1'b0;
else if( /*(nmi_start && (!in_nmi) && (!last_m1_rom)) ||*/
(rfsh_n_reg[1] && (!rfsh_n_reg[0]) && last_m1_0066 && in_nmi_2) )
in_nmi <= 1'b1;
end
always @(posedge fclk, negedge rst_n)
if( !rst_n )
nmi_count <= 3'b000;
else if( nmi_start && (!in_nmi) )
nmi_count <= 3'b111;
else if( nmi_count[2] && zpos )
nmi_count <= nmi_count - 3'd1;
assign gen_nmi = nmi_count[2];
endmodule