Вот это "выравнивание интервалов работы с экранным ОЗУ по кадрам",
и дает полнейшую плавность графики. В некоторых ЖК/OLED
микро-дисплейчиках есть регистр либо номера строки, либо начала
скана кадра. Его всегда надо считывать и таким образом
синхронизировать обновление экранного ОЗУ. У LDI тоже есть инкремент регистровых пар HL, DE и декремент BC, установка флага в регистре флагов BC == 0 ?) но она в отличие от LDIR не "зацикливается", в этом и всё отличие.
Кстати, если кто не знает как в Z80 сделаны повторяющиеся/цепочечные команды - он каждый раз декрементирует счетчик команд на 2 и с каждой итерацией выполняет команду LDIR снова и снова.
Код команды LDIR в ОЗУ можно стереть/исказить откуда-нибудь из прерывания и её выполнение таким образом прервется на середине.
LDIR выполняется сам, пока счетчик BC не достигнет нуля. Но микроархитектура Z80 увы, такова, что каждый шаг LDIR по копированию байтов из (HL) в (DE) занимает 21 такт. А LDI делая то же самое, но незацикленно делает это всего за 16 тактов (столько же, как и LDIR на последнем шаге, когда он заканчивает копировать). Это почти всегда играет роль потому что процессор относительно слаб и если строить видео-игровой кадр, у тебя уже не секунда и не 3,5 миллиона тактов, а при кадровой частоте 50Hz это 20мсек и всего лишь 70 000 тактов. Так что приходится выжимать каждый такт и это прям реально, не красивое словцо.
Отсюда идея: если записать в память кучу LDI друг за другом можно получить аналог LDIR копирующий каждый байт не за 21 такт, а за 16 тактов ценой некоторого расхода памяти (обычно в играх надо бывало все-таки экономить память и более 16-32 двухбайтных команд LDI друг за другом редко кто ставил. Группа LDI сама по себе без доп.команд полностью LDIR не заменит, потому что тогда копирование происходит с "гранулярностью" или "дискретностью" в N команд LDI. Но если устроить самомодифицирующийся код (патчить блок LDI) или делать программный JP в цепочку LDI:LDI:LDI ... со смещением, то это будет аналог LIDR но более быстрый.
Были и еще более исхищренные способы копировать с использованием стека: с набором всех регистров, перестановкой стека и сохранением регистров.
Но это примитивная "база".
В реальности для работы с графикой требовалось не просто копирование, а выполнение на лету операций (Dst) = (Dst) XOR (Src) или OR или AND с инкрементами Dst, Src.
Или даже часто более сложный вывод графики с "маской": Dst = Dst AND SrcMask OR SrcSprite: INC Dst; INC Src. И т.д. Для Z80 существовало несколько десятков различных трюков по ускорению кода.
И совершенно уникальна история с "недокументированными инструкциями" Z80,
которые в отличие от каких-то сомнительных, сбойных или хакерских способов на других платформах и процессорах
самые настоящие команды, устойчивые, предсказуемые и стабильно работающие всегда, но из-за спешки не внесенные в документацию.