uint8_t *data[] = {9,8,7,6,5,4,3,2};
uint8_t pin_e   = 10;
uint8_t pin_rw  = 11;
uint8_t pin_rs  = 12;

char *message = "Hello World!";

#define RS 0x01
#define RW 0X02
#define E 0X04


void set_control(uint8_t ctrl) {
  digitalWrite(pin_rs, (ctrl & RS)?HIGH:LOW);
  digitalWrite(pin_rw, (ctrl & RW)?HIGH:LOW);
  digitalWrite(pin_e, (ctrl & E)?HIGH:LOW);
}

void set_data(uint8_t data_bits) {
  for (int i = 0; i < 8; i++) {
    digitalWrite(data[i], data_bits & 0x01);
    data_bits = data_bits >> 1;
  }
}

void set_data_output() {
  for (int i = 0; i < 8; i++) {
    pinMode(data[i], OUTPUT);
  }
}

void set_data_input() {
  for (int i = 0; i < 8; i++) {
    pinMode(data[i], INPUT);
  }
}

void lcd_wait() {
lcd_wait:
  set_data_input();

  do {
  set_control(RW);
  set_control(RW|E);
  } while (digitalRead(data[7]) == HIGH);
  
  set_control(RW);
  set_data_output();
}

void lcd_instruction(uint8_t inst) {
  lcd_wait();
  set_data(inst);
  set_control(9);  // Clear RS/RW/E bits
  set_control(E);  // Set E bit to send instruction
  set_control(9);  // Clear RS/RW/E bits
}

void print_char(uint8_t chr) {
print_char:
  lcd_wait();
  set_data(chr);
  set_control(RS);   // Set RS; Clear RW/E bits
  set_control(RS|E); // Set E bit to send 
  set_control(RS);   // Clear E bits
}

void reset() {

  lcd_instruction(0b00111000); // Set 8-bit mode; 2-line display; 5x8 font
  lcd_instruction(0b00001110); // Display on; cursor on; blink off
  lcd_instruction(0b00000110); // Increment and shift cursor; don't shift display
  lcd_instruction(0b00000001); // Clear display

  char *msg_ptr = message;
  while (*msg_ptr != 0) {
    print_char(*msg_ptr);
    msg_ptr++;
  }
}

void setup() {
  // put your setup code here, to run once:
  set_data_output();
  pinMode(pin_e, OUTPUT);
  pinMode(pin_rw, OUTPUT);
  pinMode(pin_rs, OUTPUT);

  reset();
}

void loop() {
  // put your main code here, to run repeatedly:

}