module vga_curproc (clk, rst_i, Thgate, Tvgate, idat, idat_wreq, 
	cursor_xy, cursor_en, cursor_res, 
	cursor_wadr, cursor_wdat, cursor_we,
	cc_adr_o, cc_dat_i,
	rgb_fifo_wreq, rgb);

	//
	// inputs & outputs
	//

	// wishbone signals
	input         clk;           // master clock input
	input         rst_i;         // synchronous active high reset

	// image size
	input [15:0] Thgate, Tvgate; // horizontal/vertical gate
	// image data
	input [23:0] idat;           // image data input
	input        idat_wreq;      // image data write request

	// cursor data
	input [31:0] cursor_xy;      // cursor (x,y)
	input        cursor_en;      // cursor enable (on/off)
	input        cursor_res;     // cursor resolution (32x32 or 64x64 pixels)
	input [ 8:0] cursor_wadr;    // cursor buffer write address
	input [31:0] cursor_wdat;    // cursor buffer write data
	input        cursor_we;      // cursor buffer write enable

	// color registers interface
	output [ 3:0] cc_adr_o;      // cursor color registers address
	reg  [ 3:0] cc_adr_o;    
	input  [15:0] cc_dat_i;      // cursor color registers data

	// rgb-fifo connections
	output        rgb_fifo_wreq; // rgb-out write request
	reg        rgb_fifo_wreq;
	output [23:0] rgb;           // rgb data output
	reg [23:0] rgb;

	//
	// variable declarations
	//
	reg         dcursor_en, ddcursor_en, dddcursor_en;
	reg  [15:0] xcnt, ycnt;
	wire        xdone, ydone;
	wire [15:0] cursor_x, cursor_y;
	wire        cursor_isalpha;
	reg  [15:0] cdat, dcdat;
	wire [ 7:0] cursor_r, cursor_g, cursor_b, cursor_alpha;
	reg         inbox_x, inbox_y;
	wire        inbox;
	reg         dinbox, ddinbox, dddinbox;

	reg  [23:0] didat, ddidat, dddidat;
	reg         didat_wreq, ddidat_wreq;
	wire [31:0] cbuf_q;
	reg  [11:0] cbuf_ra;
	reg  [ 2:0] dcbuf_ra;
	wire [ 8:0] cbuf_a;

	reg         store1, store2;

	//
	// module body
	//

	//
	// generate x-y counters
	always@(posedge clk)
		if(rst_i || xdone)
			xcnt <= #1 16'h0;
		else if (idat_wreq)
			xcnt <= #1 xcnt + 16'h1;

	assign xdone = (xcnt == Thgate) && idat_wreq;

	always@(posedge clk)
		if(rst_i || ydone)
			ycnt <= #1 16'h0;
		else if (xdone)
			ycnt <= #1 ycnt + 16'h1;

	assign ydone = (ycnt == Tvgate) && xdone;


	// decode cursor (x,y)
	assign cursor_x = cursor_xy[15: 0];
	assign cursor_y = cursor_xy[31:16];

	//
	// generate inbox signals
	always@(posedge clk)
		begin
			inbox_x <= #1 (xcnt >= cursor_x) && (xcnt < (cursor_x + (cursor_res ? 16'h7f : 16'h1f) ));
			inbox_y <= #1 (ycnt >= cursor_y) && (ycnt < (cursor_y + (cursor_res ? 16'h7f : 16'h1f) ));
		end

	assign inbox = inbox_x && inbox_y;

	always@(posedge clk)
		dinbox <= #1 inbox;

	always@(posedge clk)
		if (didat_wreq)
			ddinbox <= #1 dinbox;

	always@(posedge clk)
		dddinbox <= #1 ddinbox;

	//
	// generate cursor buffer address counter
	always@(posedge clk)
		if (!cursor_en || ydone)
			cbuf_ra <= #1 12'h0;
		else if (inbox && idat_wreq)
			cbuf_ra <= #1 cbuf_ra +12'h1;

	always@(posedge clk)
		dcbuf_ra <= #1 cbuf_ra[2:0];

	assign cbuf_a = cursor_we ? cursor_wadr : cursor_res ? cbuf_ra[11:3] : cbuf_ra[9:1];

	// hookup local cursor memory (generic synchronous single port memory)
	// cursor memory should never be written to/read from at the same time
	generic_spram #(9, 32) cbuf(
		.clk(clk),
		.rst(1'b0),       // no reset
		.ce(1'b1),        // always enable memory
		.we(cursor_we),
		.oe(1'b1),        // always output data
		.addr(cbuf_a),
		.di(cursor_wdat),
		.do(cbuf_q)
	);

	//
	// decode cursor data for 32x32x16bpp mode
	always@(posedge clk)
		if (didat_wreq)
			cdat <= #1 dcbuf_ra[0] ? cbuf_q[31:16] : cbuf_q[15:0];

	always@(posedge clk)
		dcdat <= #1 cdat;

	//
	// decode cursor data for 64x64x4bpp mode

	// generate cursor-color address
	always@(posedge clk)
		if (didat_wreq)
			case (dcbuf_ra)
				3'b000: cc_adr_o <= cbuf_q[ 3: 0];
				3'b001: cc_adr_o <= cbuf_q[ 7: 4];
				3'b010: cc_adr_o <= cbuf_q[11: 8];
				3'b011: cc_adr_o <= cbuf_q[15:12];
				3'b100: cc_adr_o <= cbuf_q[19:16];
				3'b101: cc_adr_o <= cbuf_q[23:20];
				3'b110: cc_adr_o <= cbuf_q[27:24];
				3'b111: cc_adr_o <= cbuf_q[31:28];
			endcase

	//
	// generate cursor colors
	assign cursor_isalpha =  cursor_res ? cc_dat_i[15]    : dcdat[15];
	assign cursor_alpha   =  cursor_res ? cc_dat_i[7:0]   : dcdat[7:0];
	assign cursor_r       = {cursor_res ? cc_dat_i[14:10] : dcdat[14:10], 3'h0};
	assign cursor_g       = {cursor_res ? cc_dat_i[ 9: 5] : dcdat[ 9: 5], 3'h0};
	assign cursor_b       = {cursor_res ? cc_dat_i[ 4: 0] : dcdat[ 4: 0], 3'h0};

	//
	// delay image data
	always@(posedge clk)
		didat <= #1 idat;

	always@(posedge clk)
		if (didat_wreq)
			ddidat <= #1 didat;

	always@(posedge clk)
		dddidat <= #1 ddidat;

	always@(posedge clk)
		begin
			didat_wreq  <= #1 idat_wreq;
			ddidat_wreq <= #1 didat_wreq;
		end

	//
	// generate selection unit
	always@(posedge clk)
		dcursor_en <= #1 cursor_en;

	always@(posedge clk)
		if (didat_wreq)
			ddcursor_en <= #1 dcursor_en;

	always@(posedge clk)
		dddcursor_en <= #1 ddcursor_en;

	// Alpha blending:
	// rgb = (rgb1 * alhpa1) + (rgb2 * alpha2)
	// We generate an alpha mixer (alpha1 + alpha2 = 1)
	// rgb = (alpha1)(rgb1) + (1-alpha1)(rgb2)
	// We always mix to black (rgb2 = 0)
	// rgb = (alpha1)(rgb1)
	always@(posedge clk)
		if (ddidat_wreq)
			if (!dddcursor_en || !dddinbox)
				rgb <= #1 dddidat;
			else if (cursor_isalpha)
				`ifdef VGA_HWC_3D
					rgb <= #1 dddidat * cursor_alpha;
				`else
					rgb <= #1 dddidat;
				`endif
			else
				rgb <= #1 {cursor_r, cursor_g, cursor_b};

	//
	// generate write request signal
	always@(posedge clk)
		if (rst_i)
		begin
			store1 <= #1 1'b0;
			store2 <= #1 1'b0;
		end
		else
		begin
			store1 <= #1  didat_wreq           | store1;
			store2 <= #1 (didat_wreq & store1) | store2;
		end

	// skip 2 idat_wreq signal, to keep in pace with rgb_fifo_full signal
	always@(posedge clk)
		rgb_fifo_wreq <= #1 ddidat_wreq & store2;

endmodule