О распространённом баге эмуляции команды PUSH процессора Z80

Разбираясь с артефактом скроллинга мультиколорных картинок в деме EyeAche2 (правый столбец на экране слегка подглючивает — полоски подмигивают, скроллинг в нем слегка рваный), перешерстив в связи с этим все тайминги Пентагона и узнав о нём много нового, а также попутно исследовав тайминги переключения экранов на фирменных машинах, наткнулся на любопытный баг кода эмуляции Z80, конкретно команды PUSH. Ну, баг и баг, но дело в том, что этот же баг присутствует ещё в куче эмуляторов: в Unreal, Zemu, SpecEmu, Zero, Fuse, fMSX. Возможно, ещё где-то. Так что думаю, что нижеизложенная информация может быть интересна и полезна для всего сообщества:

А баг — в неправильной логике эмуляции команды PUSH. Все эти эмули (включая мой, до вчерашнего дня) действуют в «оптимизаторской» логике её выполнения:
dec sp
ld (sp),h
dec sp
ld (sp),l

А надо — как в документации:
dec sp
dec sp
ld (sp),l
ld (sp+1),h

Из-за того, какая часть засылаемой в стек регистровой пары кладется в память первой, и происходит накладка в EyeAche 2. Там идет такой код:
LD SP, #5820; //начало следующей строки атрибутов
LD HL,#3F00
PUSH HL; //запись в конец предыдущей строки атрибутов

и далее несколько раз похожий. Но проблема в том, что код этот происходит как раз во время, когда заканчивается вывод текущей строки экрана (последнее знакоместо). И если логика выполнения PUSH правильная, то сначала в память пишется младший байт HL — в знакоместо, байт которого уже выведен на экран, а потом — старший, и на этот момент видеоконтроллер уже выводит последний байт строки. Т.е. новая инфа в атрибутах не повлияет на вывод текущей строки (она и не должна).

А если логика PUSH «оптимизаторская», то сначала в память пишется старший байт, и иногда (но не всегда), в зависимости от того, на каком такте цикла команды HALT закончился предыдущий кадр, эмулятор видеоконтроллера успевает считать этот только что записанный в память байт атрибута, хотя по логике авторов кода, не должен. Соответственно, в крайнем знакоместе текущей строки выводится полоска с атрибутом, который должен выводиться только в конце следующей строки. Из-за этого в эмуляторах на экране во время мультиколор-скроллинга некоторые полоски в крайнем справа ряду мигают, либо скроллинг в нем слегка неровный. (Этот глюк попал даже в видео демы на ютюбе.)

Вот такая вот история. Пользуясь случаем, выражаю благодарность форумчанам JV-Soft, Portos13, PheeL с форума zx-pk.ru, а также Ast-A-Moore и The Mighty Dopethrone с форума сайта WorldOfSpectrum, которые снимали по моей просьбе видео работы тестов и демок со своих реалов, для исследования таймингов и артефактов Пентагона и фирменных машин. (Для фирменных машин выяснилось, что ни один эмуль на сегодня не эмулирует полностью правильно логику работу видеоконтроллера и тайминги переключения экранов, плюс есть как минимум две схемы работы видеоконтроллера у черных амстрадовских моделей, но это уже другая история).