.. Da ich das erst richtig verarbeiten muss, erbitte ich mir etwas Zeit, bis ich wieder Rückmeldung gebe ..
Ich grins mich eins - mir gehts da wie Dir und dem LCD: wir brauchen alle mal ein paar waits wenns kompliziert wird *gg*.

Spass beiseite. Ich habe zwar keine Assemblerlösung. Aber in Assembler werden ja ähnliche Routinen nötig wie in C - und da kann ich ein bisschen was zu den Waits beitragen. Im folgenden Code sind die "waits" - heissen dort
......_delay_us (parameter)
rot hervorgehoben. Die Delays habe ich vor Jahren angepasst für 20 MHz Controllertakt (ist ja aber in µs angegeben) - und diese Routinen laufen bei mir so auf irgendwelchen, möglichst günstigen 2x16-LCDisplays. Vielleicht hilft Dir das ein bisschen weiter?

Code:
//      Erweiterte Fassung mit Routine für selbst definierte Zeichen
//      Stand = Erweiterung von lcd-routines.h und lcd-routines.c
//   vom 11. Apr. 2012
// 28 Nov 2012 16:24  Leichte Korrekturen
// 27 Juni 2015, 19:27  Datenausgabe PC0..PC3, Steuerport PD
 
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial
// Die Pinbelegung ist über defines in lcd-routines.h einstellbar
 
#include <avr/io.h>
//#include "lcd-routines.h"
#include "lcd_162_xt_ALCo.h"
#include <util/delay.h>
#include <avr/eeprom.h>
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// sendet ein Datenbyte an das LCD
 
void lcd_data(unsigned char temp1)
{
   LCD_PORT |= (1<<LCD_RS);        // RS auf 1 setzen
   lcd_send(temp1);
}
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// sendet einen Befehl an das LCD
 
void lcd_command(unsigned char temp1)
{
   LCD_PORT &= ~(1<<LCD_RS);        // RS auf 0 setzen
   lcd_send(temp1);
}
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Eigentliche LCD-Zugriffs-Funktion; 4-Bit-Modus
void lcd_send(unsigned char data) {
   // oberes Nibble setzen
  LCD_PORT = (LCD_PORT & 0xF0) | ((data >> 4) & 0x0F);
  _delay_us(15);
  lcd_enable();
   // unteres Nibble setzen
  LCD_PORT = (LCD_PORT & 0xF0) | (data & 0x0F);
  _delay_us (15);
  lcd_enable();
  _delay_us (60); 
  LCD_PORT &= 0xF0;
}
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// erzeugt den Enable-Puls
void lcd_enable(void)
{
            LCD_PORT |= (1<<LCD_EN1);
    _delay_us (20);                   // kurze Pause
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers verlängern
   // http://www.mikrocontroller.net/topic/80900
   LCD_PORT &= ~(1<<LCD_EN1);
}
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Initialisierung:
// Muss ganz am Anfang des Programms aufgerufen werden.
 
void lcd_init(void)
{
            LCD_DDR = LCD_DDR | 0x0F | (1<<LCD_RS) | (1<<LCD_EN1);   // Port auf Ausgang schalten
            // muss 3mal hintereinander gesendet werden zur Initialisierung
            _delay_us (40);
            LCD_PORT = (LCD_PORT & 0xF0 & ~(1<<LCD_RS)) | 0x03;
            lcd_enable();
 
            _delay_us (15);
            lcd_enable();
 
            _delay_us (11);
            lcd_enable();
            _delay_us (11);
            LCD_PORT = (LCD_PORT & 0xF0 & ~(1<<LCD_RS)) | 0x02;
            _delay_us (11);
            lcd_enable();
            _delay_us (11);
 
            // 4Bit / 2 Zeilen / 5x7
            lcd_command(CMD_SetIFOptions | 0x08);
 
            // Display ein / Cursor aus / kein Blinken
            lcd_command(CMD_SetDisplayAndCursor | 0x04);
 
            // inkrement / kein Scrollen   
            lcd_command(CMD_SetEntryMode | 0x02);         
            lcd_clear();
}
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Sendet den Befehl zur Löschung des Displays
 
void lcd_clear(void)
{
   lcd_command(CLEAR_DISPLAY);
   _delay_us (15);
}
 
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Schreibt einen String auf das LCD
 
void lcd_string(char *data)
{
    while(*data) {
        lcd_data(*data);
        data++;
    }
}
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//String aus EEPROM laden und an LCD senden
  void lcd_eep_string(const unsigned char *data)
 {                                 //
  unsigned char c;
  while(1)                      //
  {                             //
    c = eeprom_read_byte(data); //
    if(c==0) return;            //
    lcd_data(c);                //
    data++;                     //
  }
 }              // Ende void lcd_eep_string(const
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Schreibt ein Zeichen in den Character Generator RAM
// Daten liegen direkt im RAM
  void lcd_generatechar(uint8_t code, const uint8_t *data)
 {                             //
  lcd_command(LCD_SET_CGADR|(code<<3)); // Startposition des Zeichens einstellen
  for (uint8_t i=0; i<8; i++)           // Bitmuster übertragen
  {                             //
    lcd_data(data[i]);          //
  }                             //
 }              // Ende void lcd_generatechar(uint8_t code,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -