`include	"defines.v"

// Emulated keyboard timing defaults.
// Override these values in defines.v if needed.
`ifndef EMUKBD_START_DELAY
`define EMUKBD_START_DELAY 32'd40000
`endif

`ifndef EMUKBD_CHAR_DELAY
`define EMUKBD_CHAR_DELAY 32'd5000
`endif

// ----------------------------------------------------------------------------------------
// CONSOLE INTERFACE
// ----------------------------------------------------------------------------------------
// new version May 2026 fully compliant and tested with the exception of the reader relay
// tested with maindec-08-d2pe-d (only test 0 and 1 - all the others are related to the 
// teletype unit)

module teletype (clk, clk_n, reset, 
						iop_in,
						db_in, db_out, 
						IOSKP_out, ACCLR_out, ORAC_out, 
					   Txd, Rxd, RdrRun, TTY_irq, IR_in, sys_clear);
  
   // "bus" signals
	input clk, clk_n;
	input reset;
	input [11:0] 	IR_in;		// current IR
	input [2:0] 	iop_in;		// iop pulses
	output 			IOSKP_out;	// answers to processor
	output 			ORAC_out;
	output 			ACCLR_out;
	input [11:0] 	db_in; 		// board input from bus
	output [11:0] 	db_out;		// board output to bus
	output         TTY_irq;		// interrupt request from this board (change name to irq)
	input 			sys_clear;	// global request to clear all flags

   // UART i/o
	input 			Rxd;			// data in to UART (keyboard)
	output         Txd;			// data out from UART (teleprinter)
	output         RdrRun;		// reader enable / reader run relay output


	// main flags
	reg printer_flag;		   // teleprinter flag status
`ifdef emulated_keyboard
	reg keyboard_flag;		   // keyboard flag status, generated by the simulated keyboard
	reg [7:0] kbd_data;	   // current simulated keyboard character
`else
	reg keyboard_flag;		   // keyboard flag status, generated by the UART receiver
	reg [7:0] rx_latched;	   // byte latched for KRS/KRB until the PDP-8 consumes it
	reg rx_req_r;		   // acknowledge/consume request to UART receiver
	reg rx_empty_d;		   // previous RX empty state for edge detection
`endif
	reg CON_inten_flag;		   // local interrupt enable
	reg printer_irq_armed;    // enables printer-ready flag to generate IRQ after software has used printer
	reg reader_run;			   // paper tape reader enable relay / reader run FF
	assign RdrRun = reader_run;

	// UART interface signals
	wire tx_load_req, tx_ack;
	wire rx_req, rx_ack;
	wire rx_start;			// one-clk pulse when UART begins assembling a character
	wire rx_empty;
	wire [7:0] rx_data;
	wire tx_baud_clk; 
   wire rx_baud_clk; 
	wire tx_empty;

	// decode the three IOP cycles
	wire IOP_1;					// IOSKP  cycle
	wire IOP_2;					// ACCLRC cycle
	wire IOP_4;					// ORAC 	 cycle

	assign IOP_1 = iop_in[0];   // IOSKP cycle
	assign IOP_2 = iop_in[1];   // ACCLRC cycle
	assign IOP_4 = iop_in[2];   // ORAC cycle

	/* remind: answers to the processor
	output IOSKP_out;			// request the processor to skip next instruction			(in IOP_1)
	output ORAC_out;			// request the processor to OR db_out to the accumulator (in IOP_4)
	output ACCLR_out;			// request the processor to clear the accumulator			(in IOP_2) */

	// IOT processor instructions
	wire do_TFL;	// 6040	// named also SPF
	wire do_TSF;	// 6041
	wire do_TCF;	// 6042
	wire do_TCP;	// 6044
	wire do_TSK;	// 6045
	wire do_TLS;	// 6046

	wire do_KCF;	// 6030
	wire do_KSF;	// 6031
	wire do_KCC;	// 6032
	wire do_KRS;	// 6034
	wire do_KIE;	// 6035
	wire do_KRB;	// 6036

	// decode the required instructions
	//
	// Console device codes:
	//   603x = keyboard/reader
	//   604x = teleprinter/punch
	//
	// The processor evaluates IOTs in three internal phases:
	//   IOP_1 -> IOSKP
	//   IOP_2 -> ACCLR
	//   IOP_4 -> ORAC
	//
	// Therefore instructions which clear AC, such as KCC/KRB, must answer
	// during IOP_2, while instructions which put data on the bus for ORAC,
	// such as KRS/KRB, must answer during IOP_4.
	//
	// On the keyboard side, KRB (6036) is KCC+KRS.
	// On the teleprinter side, TLS (6046) is TCF+TPC.
	wire is_TTY_dev;
	wire is_KBD_dev;

	assign is_TTY_dev = (IR_in[8:3] == 6'd4);
	assign is_KBD_dev = (IR_in[8:3] == 6'd3);

	assign do_TFL = is_TTY_dev & (IR_in[2:0] == 3'b000) & IOP_1;	// 6040: set tpflag, also called SPF
	assign do_TSF = is_TTY_dev & (IR_in[2:0] == 3'b001) & IOP_1;	// 6041: skip if tpflag
	assign do_TCF = is_TTY_dev & ((IR_in[2:0] == 3'b010) |
	                              (IR_in[2:0] == 3'b110)) & IOP_2;	// 6042, and TCF part of TLS
	assign do_TCP = is_TTY_dev & ((IR_in[2:0] == 3'b100) |
	                              (IR_in[2:0] == 3'b110)) & IOP_4;	// 6044, and TPC part of TLS
	assign do_TSK = is_TTY_dev & (IR_in[2:0] == 3'b101) & IOP_1;	// 6045: skip if tpflag or kbflag and interrupt enabled
	assign do_TLS = is_TTY_dev & (IR_in[2:0] == 3'b110) & IOP_4;	// 6046: debug/trace name, transmit phase only

	assign do_KCF = is_KBD_dev & (IR_in[2:0] == 3'b000) & IOP_1;	// 6030: clear kbflag
	assign do_KSF = is_KBD_dev & (IR_in[2:0] == 3'b001) & IOP_1;	// 6031: skip if kbflag
	assign do_KCC = is_KBD_dev & ((IR_in[2:0] == 3'b010) |
	                              (IR_in[2:0] == 3'b110)) & IOP_2;	// 6032, and KCC part of KRB
	assign do_KRS = is_KBD_dev & ((IR_in[2:0] == 3'b100) | 
										   (IR_in[2:0] == 3'b110)) & IOP_4;// 6034, and KRS part of KRB

	assign do_KIE = is_KBD_dev & (IR_in[2:0] == 3'b101) & IOP_1;	// 6035: set interrupt enable according to AC[0]
	assign do_KRB = is_KBD_dev & (IR_in[2:0] == 3'b110) & (IOP_2 | IOP_4);	// 6036: debug/trace name only

	// interrupt request
	// CON_inten_flag starts enabled, as on the real console interface.
	// However, a merely idle/ready printer after reset/CAF must not create an
	// autonomous interrupt request.  The printer IRQ source is armed only after
	// software has explicitly touched the printer with TCF/TLS/TFL.
	assign TTY_irq = ((printer_flag & printer_irq_armed) | keyboard_flag) & CON_inten_flag;

	// data from this board to external bus
	// note that must be zero when not do_KRS because the out is ored with other boards
`ifdef emulated_keyboard
	assign db_out = do_KRS ? {4'b0000,kbd_data[7:0]} : 12'd0;
`else
	assign db_out = do_KRS ? {4'b0000,rx_latched[7:0]} : 12'd0;
`endif

	// data from external bus to this board
	//
	// TPC/TLS transfers AC to the teleprinter during IOP4 (do_TCP).
	// The UART request latch is set on clk_n and the UART samples tx_data
	// on the following clk edge.  Therefore the transmit register must also
	// be updated on clk_n, so that btx_data is already stable when the UART
	// accepts tx_load_req.  Loading TXREG on clk caused a one-character delay:
	// the UART accepted the request while still seeing the previous TXREG value.
	reg [11:0] btx_data;
	always @(posedge clk_n or posedge reset) begin
		if (reset)
			btx_data <= 12'd0;
		else if (do_TCP)
			btx_data <= db_in;
	end

	// la teleprinter ha due condizioni di skip possibili per TSF e TSK
	wire CON_irq_condition;
	assign CON_irq_condition = ((printer_flag & printer_irq_armed) | keyboard_flag) & CON_inten_flag;
	assign IOSKP_out = (do_TSF & printer_flag) |
   	                (do_TSK & CON_irq_condition) |
      	             (do_KSF & keyboard_flag);
	assign  ORAC_out = do_KRS;					// solo la tastiera può richiedere l'OR
	assign  ACCLR_out = do_KCC;			   // KCC/KRB clear AC; TPC/TLS must not clear AC

	wire set_pflag;	                     // auxiliary signal to set printer_flag at the end of transmission
	wire set_kflag; 	                     // auxiliary signal to set keyboard_flag at the end of reception

	// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	// *                             LOCAL INTERRUPT ENABLE FLIP-FLOPS                               *
	// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	// On the real PDP-8 console interface this enable is set after reset/clear.
	// FOCAL relies on printer interrupts being available after the initial TLS,
	// otherwise its output buffer is never drained.
	wire set_CON_inten;
	wire clr_CON_inten;

	assign set_CON_inten = (do_KIE & db_in[0]) | sys_clear; // CAF/sys_clear enables console interrupts
	assign clr_CON_inten =  do_KIE & ~db_in[0];             // KIE with AC0=0 disables console interrupts

	// Console local interrupt enable.
	// On the real machine this is enabled after reset/clear, so OTTO starts enabled too.
	always @(posedge clk_n or posedge reset) begin
		if (reset) begin
			CON_inten_flag <= 1'b1;
		end else if (set_CON_inten) begin
			CON_inten_flag <= 1'b1;
		end else if (clr_CON_inten) begin
			CON_inten_flag <= 1'b0;
		end
	end

/*	always @(posedge clk) begin
	if (do_TLS) begin
		$display("TLS t=%0t char=%03o printer_flag=%b tx_empty=%b Txd=%b",
					$time, db_in,
					printer_flag, tx_empty, Txd);
	end			
	end*/
	// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	// *                               TELEPRINTER FLIP-FLOPS                                        *
	// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	// printer_flag - flag ufficiale della teleprinter
	//
	// Diagnostic/fix variant: keep the flag in the same clock domain used
	// to observe tx_empty.  The previous ff_SR sampled set_pflag on clk_n,
	// while set_pflag is generated from tx_empty in the clk domain.
	// FOCAL showed that set_pflag was occurring, but printer_flag remained low.
	//
	// Reset/sys_clear leave the printer ready.  TCF/TLS clears the flag;
	// completion of a UART transmission sets it again.
	reg tx_empty_d_for_pflag;
	always @(posedge clk or posedge reset) begin
		if (reset) begin
			printer_flag <= 1'b1;
			printer_irq_armed <= 1'b0;
			tx_empty_d_for_pflag <= 1'b1;
		end else begin
			tx_empty_d_for_pflag <= tx_empty;

			if (sys_clear) begin
				// Ready for TSF polling, but not a pending interrupt source.
				printer_flag <= 1'b0;
				printer_irq_armed <= 1'b0;
			end else begin
				if (do_TCF) begin
					printer_flag <= 1'b0;
				end else if ((tx_empty & ~tx_empty_d_for_pflag) | do_TFL) begin
					printer_flag <= 1'b1;
				end

				// From this point on, a completed printer operation may request IRQ.
				// TLS contains TCF at IOP2 and TCP at IOP4; TFL explicitly sets the flag.
				// DHMCA also expects a ready printer flag observed by TSF to be a valid
				// interrupt source after the following ION. This avoids a spontaneous
				// IRQ after reset/CAF, but allows the diagnostic's TSF/ION sequence.
				if (do_TCF | do_TCP | do_TFL | (do_TSF & printer_flag)) begin
					printer_irq_armed <= 1'b1;
				end
			end
		end
	end
	
	// questo memorizza la richiesta di load del tx perch� il TCP dura troppo poco
	ff_SR TTY1 (.clk(clk_n), .set(do_TCP), .res1(reset), .res2(tx_ack), .Q(tx_load_req), .Q_n());

	// questo rileva il fronte di salita di tx_empty per settare il flag alla fine della trasmissione
	edge_det_dly EDp (.clk(clk), .reset(reset), .signal_in(tx_empty), .edge_detected(set_pflag));


	// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	// *                                  KEYBOARD FLIP-FLOPS                                        *
	// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

`ifdef emulated_keyboard
	// ----------------------------------------------------------------------------------------
	// SIMULATED KEYBOARD
	// ----------------------------------------------------------------------------------------
	// This block injects a short FOCAL command as if it were typed on the console keyboard.
	// It deliberately waits between characters.  Clearing keyboard_flag only proves that
	// the interrupt service routine has fetched the character from the console interface;
	// it does not prove that FOCAL has already consumed its internal input buffer.
	//
	// Command currently sent:
	//      TYPE 2+2<CR>
	//
	// If FOCAL still reports an input-buffer related error, increase EMUKBD_CHAR_DELAY
	// in defines.v, for example:
	//      `define EMUKBD_CHAR_DELAY 32'd1000000
	// FOCAL interactive input test passed.

	// TYPE 2+2 -> 4.0000
	// TYPE 1/3 -> 0.3333
	//
	// Important: the emulated keyboard requires an inter-character delay.
	// keyboard_flag being cleared only means that the interrupt routine has
	// accepted the character; it does not mean that FOCAL has already consumed
	// the input buffer.

	reg [7:0]  kline [0:63];
	reg [5:0]  kidx;
	reg [31:0] emukbd_start_count;
	reg [31:0] emukbd_char_count;

	localparam EMUKBD_WAIT_START = 2'd0;
	localparam EMUKBD_SEND       = 2'd1;
	localparam EMUKBD_WAIT_READ  = 2'd2;
	localparam EMUKBD_GAP        = 2'd3;

	reg [1:0] emukbd_state;

	initial begin
		keyboard_flag      = 1'b0;
		kbd_data           = 8'd0;
		kidx               = 6'd0;
		emukbd_start_count = 32'd0;
		emukbd_char_count  = 32'd0;
		emukbd_state       = EMUKBD_WAIT_START;

		// TYPE 2+2<CR>
		kline[0]  = 8'o124;   // T
		kline[1]  = 8'o131;   // Y
		kline[2]  = 8'o120;   // P
		kline[3]  = 8'o105;   // E
		kline[4]  = 8'o040;   // space
		kline[5]  = 8'o061;   // 1
		kline[6]  = 8'o057;   // /
		kline[7]  = 8'o063;   // 3
		kline[8]  = 8'o015;   // CR
		kline[9]  = 8'd0;     // end marker, not sent
	end

	// No UART receiver is used in emulated keyboard mode.
	assign rx_req = 1'b0;

	always @(posedge clk or posedge reset) begin
		if (reset) begin
			keyboard_flag      <= 1'b0;
			reader_run         <= 1'b0;
			kbd_data           <= 8'd0;
			kidx               <= 6'd0;
			emukbd_start_count <= 32'd0;
			emukbd_char_count  <= 32'd0;
			emukbd_state       <= EMUKBD_WAIT_START;
		end else begin

			// KCF/KCC/KRB clear the keyboard flag.  sys_clear clears all console flags.
			// KCC/KRB also request the next paper-tape reader character.
			if (sys_clear) begin
				keyboard_flag <= 1'b0;
				reader_run    <= 1'b0;
			end else if (do_KCF | do_KCC) begin
				keyboard_flag <= 1'b0;
				if (do_KCC)
					reader_run <= 1'b1;
			end else begin
				case (emukbd_state)

					EMUKBD_WAIT_START: begin
						if (emukbd_start_count < `EMUKBD_START_DELAY) begin
							emukbd_start_count <= emukbd_start_count + 32'd1;
						end else begin
							emukbd_state <= EMUKBD_SEND;
						end
					end

					EMUKBD_SEND: begin
						if (!keyboard_flag && (kline[kidx] != 8'd0)) begin
							kbd_data      <= kline[kidx];
							keyboard_flag <= 1'b1;
							reader_run    <= 1'b0;

							$display("EMUKBD SEND: idx=%0d char=%03o '%c' time=%0t",
							         kidx, kline[kidx], kline[kidx], $time);

							emukbd_state <= EMUKBD_WAIT_READ;
						end
					end

					EMUKBD_WAIT_READ: begin
						// Wait until the PDP-8 has read the character and cleared keyboard_flag.
						if (!keyboard_flag) begin
							kidx              <= kidx + 6'd1;
							emukbd_char_count <= 32'd0;
							emukbd_state      <= EMUKBD_GAP;
						end
					end

					EMUKBD_GAP: begin
						// Artificial inter-character delay, to avoid overflowing FOCAL's input buffer.
						if (emukbd_char_count < `EMUKBD_CHAR_DELAY) begin
							emukbd_char_count <= emukbd_char_count + 32'd1;
						end else begin
							emukbd_state <= EMUKBD_SEND;
						end
					end

					default: begin
						emukbd_state <= EMUKBD_WAIT_START;
					end

				endcase
			end
		end
	end

`else
	// ----------------------------------------------------------------------------------------
	// PHYSICAL UART KEYBOARD / READER
	// ----------------------------------------------------------------------------------------
	// UART convention used here:
	//   rx_empty = 1  no character pending
	//   rx_empty = 0  one character pending in rx_data
	//   rx_req   = 1  Teletype has consumed the pending character; UART may clear it
	//   rx_ack   = 1  UART accepted the consume request
	//
	// keyboard_flag is a PDP-8-visible sticky flag.  It is set when a new UART
	// character arrives and is cleared by KCF/KCC/KRB/sys_clear.  The actual
	// character is latched locally so that KRB may clear the flag in IOP2 and
	// still read the character in IOP4.
	assign rx_req = rx_req_r;

	always @(posedge clk_n or posedge reset) begin
		if (reset) begin
			keyboard_flag <= 1'b0;
			reader_run    <= 1'b0;
			rx_latched    <= 8'd0;
			rx_req_r      <= 1'b0;
			rx_empty_d    <= 1'b1;
		end else begin
			rx_empty_d <= rx_empty;

			// Hold the consume request until uart.v acknowledges it.
			if (rx_ack) begin
				rx_req_r <= 1'b0;
			end else if (do_KRS) begin
				rx_req_r <= 1'b1;
			end

			// The real reader-run relay drops when an input character begins
			// to be assembled, not only when the full character is available.
			// This gives an external tape emulator a clean one-character
			// handshake: KCC/KRB enables the reader; the start bit disables it.
			if (rx_start) begin
				reader_run <= 1'b0;
			end

			// New received byte: rx_empty falls from 1 to 0.
			if (rx_empty_d && !rx_empty) begin
				rx_latched    <= rx_data;
				keyboard_flag <= 1'b1;
			end

			// KCF/KCC/KRB clear the keyboard flag. sys_clear clears all console flags.
			// KRB is KCC+KRS: the flag is cleared in IOP2, while rx_latched remains
			// available for the KRS/IOP4 transfer.
			// KCC/KRB also assert reader_run, so an external tape emulator may send
			// exactly one more character. The next received start bit clears reader_run.
			if (sys_clear) begin
				keyboard_flag <= 1'b0;
				reader_run    <= 1'b0;
			end else if (do_KCF | do_KCC) begin
				keyboard_flag <= 1'b0;
				if (do_KCC)
					reader_run <= 1'b1;
			end
		end
	end
`endif

	// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	// *                                  U A R T	                                                 *
	// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	brg BRG (
    .clk(clk), 
    .reset(reset), 
    .tx_baud_clk(tx_baud_clk), 
    .rx_baud_clk(rx_baud_clk)
    );
	
	uart UART (
		 .clk(clk),    			//
		 .reset(reset), 			//
		 .tx_clk(tx_baud_clk), 	//
		 .tx_req(tx_load_req),	// load and print 
		 .tx_ack(tx_ack), 		//
		 .tx_data({1'b0,btx_data[6:0]}),//
		 .tx_empty(tx_empty), 	//
		 .rx_clk(rx_baud_clk), 	//
		 .rx_req(rx_req), 		//
		 .rx_ack(rx_ack), 		//
		 .rx_start(rx_start), // reader-run reset: character assembly has begun
		 .rx_data(rx_data), //
		 .rx_empty(rx_empty), 	//
		 .rx_in(Rxd), 				//
		 .tx_out(Txd)				//
		 );


endmodule
