module T80PIO ( CLK_n, RESET_n, Di, Do, CDsel, /* C/D# sel */ CE_n, M1_n, IORQ_n, RD_n, RETI_n, IEI, IEO, INT_n, Ai, Ao, ASTB_n, ARDY, BSTB_n, BRDY ); input CLK_n; input RESET_n; input [7:0] Di; output [7:0] Do; input CDsel; input CE_n; input M1_n; input IORQ_n; input RD_n; input RETI_n; input IEI; output IEO; output INT_n; input [7:0] Ai; output [7:0] Ao; input ASTB_n; output ARDY; input BSTB_n; output BRDY; reg [6:0] V; // Interrupt vector reg [1:0] M; // Operating mode (1's hot) reg [7:0] IO; // Mode 3 direction mask reg [3:0] ICW; // Interrupt control word reg [7:0] MB; // Interrupt mask reg [7:0] D; // Output direction data register reg io_next; // Next control word is I/O mask reg mb_next; // Next control word is interrupt mask reg i_data_flag; // Have input data reg o_data_flag; // Have input data reg ASTB_n_old; // Previous ASTB_n reg irq_cond_old; // Previous irq_cond reg IORQ_n_old; // Previous IORQ_n reg M1_n_old; // Previous M1_n reg [7:0] data_in; // Latched input data (Di) // Are we currently being addressed? wire addressed = M1_n & ~IORQ_n & ~CE_n; reg addressed_old; // Previous addressed for edge detect // Data as it would be read by the CPU wire [7:0] read_data = (M == 2'b00) ? D : (M == 2'b01) ? data_in : (M == 2'b10) ? data_in : /* (M == 2'b11) */ (D & ~IO) | (data_in & IO); // Data out assign Do = ( ~M1_n & ~IORQ_n & (my_irq|intak) ) ? { V, 1'b0 } : ( addressed & ~RD_n & ~CDsel ) ? read_data : 8'hFF; // Return FF if not selected // Output data word (note: pins which would be tristated on Z80PIO // we feed back the input.) assign Ao = (M == 2'b00) ? D : (M == 2'b01) ? data_in : (M == 2'b10) ? D : /* (M == 2'b11) */ (D & ~IO) | (data_in & IO); // Output RDY signals assign ARDY = (M[0] == 1'b0) ? o_data_flag : (M == 2'b01) ? ~i_data_flag : 0; assign BRDY = (M == 2'b10) && ~i_data_flag; // This signal is high if we should latch the input data wire latch_in = ~addressed & ((M == 2'b01 && ~ASTB_n) | (M == 2'b10 && ~BSTB_n) | (M == 2'b11)); // Output INT# reg need_irq; // Need to issue interrupt reg need_irq_sync; // Synchronized to M1# reg intak; // INTAK in process reg servicing_irq; // Waiting for RETI wire my_irq = need_irq_sync & IEI; assign INT_n = ~my_irq; assign IEO = IEI & ~need_irq_sync & ~intak & ~servicing_irq; // This computes the mode 3 interrupt condition wire [7:0] xor_mask = (ICW[2]^ICW[1]) ? 8'h00 : 8'hFF; wire irq_cond = (((read_data ^ xor_mask) & ~MB) != 8'h00) ^ ICW[2]; always @(posedge CLK_n or negedge RESET_n) begin if ( !RESET_n ) begin M <= 2'b01; IO <= 0; ICW <= 0; MB <= 0; D <= 0; io_next <= 0; mb_next <= 0; need_irq <= 0; intak <= 0; servicing_irq <= 0; ASTB_n_old <= 0; irq_cond_old <= 0; addressed_old <= 0; end else begin // Only do this once per access cycle if ( addressed & ~addressed_old ) begin case ( { RD_n, CDsel } ) 2'b00: // Data Read begin // Do handled above i_data_flag <= 0; end 2'b01: // Control read begin // No readable control registers end 2'b10: // Data Write begin D <= Di; o_data_flag <= 1; end 2'b11: // Control Write begin io_next <= 0; mb_next <= 0; if ( io_next ) IO <= Di; else if ( mb_next ) MB <= Di; else begin casex ( Di[3:0] ) 4'bxxx0: V <= Di[7:1]; 4'b1111: begin M[1:0] <= Di[7:6]; io_next <= (Di[7:6] == 2'b11); end 4'b0111: begin ICW[3:0] <= Di[7:4]; mb_next <= Di[4]; end 4'b0011: begin ICW[3] <= Di[7]; end endcase // casex( Di[3:0] ) end // else: !if( mb_next ) end // case: 2'b11 endcase // case( { RD_n, CDsel } ) end // if ( ~IORQ_n & ~CE_n ) // Latch input data every cycle unless we are // currently being addressed by the CPU if ( latch_in ) data_in <= Ai; // Actual interrupt-generating logic. We have two // possible states to be in; both can be active: // need_irq (INT# -> INTAK) and servicing_irq (INTAK -> RETI). if ( ~M1_n & ~IORQ_n & (my_irq | intak) ) begin servicing_irq <= 1; intak <= 1; end else if ( intak ) begin intak <= 0; need_irq <= 0; end // Mode 3 interrupt control if ( M == 2'b11 ) begin if ( ICW[3] & irq_cond & ~irq_cond_old ) need_irq <= 1; end // Input strobe detect (mode != 3) else if ( ASTB_n & ~ASTB_n_old ) // ASTB_n rising edge begin if ( ICW[3] ) need_irq <= 1; if ( M[0] == 1'b0 ) // Output or Bidir modes o_data_flag <= 0; // Data sent, now empty else // Input mode i_data_flag <= 1; // Data received, now full end // End of interrupt detection if ( ~RETI_n ) servicing_irq <= 0; // Synchronize interrupt to the rising edge of M1# if ( M1_n & ~M1_n_old ) need_irq_sync <= need_irq; // Delayed signals for edge detection ASTB_n_old <= ASTB_n; irq_cond_old <= irq_cond; addressed_old <= addressed; IORQ_n_old <= IORQ_n; M1_n_old <= M1_n; end // else: !if( !RESET_n ) end // always @ (posedge CLK_n or negedge RESET_n) endmodule // T80PIO