module lcd (
	input            clk,
	output reg       lcd_rs,
	output reg       lcd_rw,
	output reg       lcd_e,
	output reg [7:4] lcd_d,
	output     [4:0] mem_addr,
	input      [7:0] mem_bus
	);
	
	parameter        n = 24;
	parameter        j = 17;           // Initialization is slow, runs at clk/2^(j+2) ~95Hz
	parameter        k = 11;           // Writing/seeking is fast, clk/2^(k_2) ~6KHz
	parameter        noop = 6'b010000; // Allows LCD to drive lcd_d, can be safely written any time
	
	reg        [n:0] count = 0;
	reg        [5:0] lcd_state = noop;
	reg              init = 1;         // Start in initialization on power on
	reg              row = 0;          // Writing to top or or bottom row
	
	assign mem_addr = {row, count[k+6:k+3]};
	
	initial count[j+7:j+2] = 11;

	always @ (posedge clk) begin
		count <= count + 1;
		if (init) begin                    // initalization
			case (count[j+7:j+2])
				1: lcd_state <= 6'b000010;    // function set
				2: lcd_state <= 6'b000010;
				3: lcd_state <= 6'b001000;
				4: lcd_state <= 6'b000000;    // display on/off control
				5: lcd_state <= 6'b001100;
				6: lcd_state <= 6'b000000;    // display clear
				7: lcd_state <= 6'b000001;
				8: lcd_state <= 6'b000000;    // entry mode set
				9: lcd_state <= 6'b000110;
				10: begin init <= ~init; count <= 0; end
			endcase
			// Write lcd_state to the LCD and turn lcd_e high for the middle half of each lcd_state
			{lcd_e,lcd_rs,lcd_rw,lcd_d[7:4]} <= {^count[j+1:j+0] & ~lcd_rw,lcd_state}; 
		end else begin                                                              // Continuously update screen from memory
			case (count[k+7:k+2]) 
				32: lcd_state <= {3'b001,~row,2'b00};                                 // Move cursor to begining of next line
				33: lcd_state <= 6'b000000;
				34: begin count <= 0; row <= ~row; end                                // Restart and switch which row is being written
				default: lcd_state <= {2'b10, ~count[k+2] ? mem_bus[7:4] : mem_bus[3:0]}; // Pull characters from bus
			endcase
			// Write lcd_state to the LCD and turn lcd_e high for the middle half of each lcd_state
			{lcd_e,lcd_rs,lcd_rw,lcd_d[7:4]} <= {^count[k+1:k+0] & ~lcd_rw,lcd_state};
		end
	end
endmodule