THE WAR OF THE WORLDS: A DYNAMIC TAPE LOADER E-BOOK FOR FAST READERS
(C) EINAR SAUKAS - WOOT 2023
Это «технодемо» представляет собой специальную электронную книгу с динамическим загрузчиком с ленты для ZX Spectrum. Она выводит на экран полный текст книги «Война миров» (Герберта Уэллса / H. G. Wells) прямо во время загрузки с кассеты. На стандартной скорости ленты текст появляется слишком быстро для комфортного чтения (если только вы не мастер скорочтения), но иначе это не было бы столь впечатляющей технодемкой... Впрочем, при использовании эмулятора всегда можно замедлить эмуляцию или ставить паузу после каждой страницы.
С технической точки зрения ZX Spectrum сохраняет данные на ленте в виде последовательности импульсов. В стандартном загрузчике бит 0 кодируется парой импульсов длительностью 855T, а бит 1 — парой импульсов по 1710T. Длительность каждого импульса определяется по его «фронту» (edge), то есть когда сигнал с ленты переключается из ON в OFF или наоборот.
Оригинальная подпрограмма загрузки с ленты содержит небольшую паузу в 354T перед тем, как начать поиск следующего «фронта» импульса (для измерения его длительности). Наш кастомный загрузчик попросту использует это время для выполнения полезной работы.
В таймингах загрузчика заложен значительный допуск, компенсирующий неточности скорости записи/воспроизведения магнитофонов. Однако менять эти тайминги — дело крайне рискованное, поскольку требуется огромное количество практических тестов на множестве реальных магнитофонов, чтобы оценить надёжность любого нового загрузчика. Чтобы избежать подобных проблем, этот загрузчик разрабатывался с соблюдением драконовского ограничения: каждый возможный путь выполнения занимает В ТОЧНОСТИ то же время, что и оригинальный стандартный загрузчик, за исключением разве что (максимум) одного обращения к contended memory на импульс (что дает лишь от 0T до 7T дополнительной задержки в зависимости от точного положения луча ТВ-развёртки).
Это ограничение позволяет изменять (максимум) один байт экранной памяти за импульс. Поскольку считывание каждого символа с ленты занимает 16 импульсов (2 x 8 бит), за каждый загруженный с ленты байт можно изменить не более 16 адресов экранной памяти. Это значит, что пока считывается новый байт, времени вполне хватит на то, чтобы отобразить на экране предыдущий символ (для чего нужно скопировать 8 байт пикселей, стереть знакоместо под ним и сместить курсор). Однако времени не хватит, если предыдущим кодом был ENTER, поскольку в этом случае требуется скрыть курсор и/или очистить до 32 знакомест на экране. Поэтому, если с ленты считывается подряд несколько кодов ENTER, вывод начинает «отставать» на пару символов. Затем подпрограмме требуется некоторое время (в процессе загрузки и печати следующих символов), чтобы наверстать упущенное. Из-за этого в кастомном загрузчике используется небольшой кольцевой буфер ввода (на 256 байт), куда складываются вновь загруженные коды, пока предыдущие ещё только отрисовываются на экране.
Если вкратце, отрисовка каждого загруженного кода происходит в несколько шагов. Каждый шаг выполняется во время упомянутого выше 354-тактного интервала простоя. Эта последовательность шагов управляется конечным автоматом следующим образом:
+-------+
| |
V |
+----> st_enter --+
\ |
\ |
(ENTER)\ | (non-ASCII)
\ | +-----------+
+------+ \ | / |
| | \ | / |
| V (CHAR) \ V / V
+-- st_char <------------- st_main <-------- st_cursor <--+
| | |
| | |
| (SPACE)| |
| | |
V V |
st_show -------------> st_erase ----------------------+
Шаги конечного автомата:
-
«st_main»: проверка следующего байта из входного буфера и принятие решения о дальнейших действиях с выбором следующего шага.
-
«st_char»: отрисовка печатного символа в текущей позиции экрана по одной пиксельной строке за раз. Этот шаг всегда выполняется 8 раз, и, чтобы на экране не отображался недорисованный символ, текущая позиция всегда скрыта с помощью одинаковых атрибутов INK/PAPER.
-
«st_show»: изменение текущего экранного атрибута, чтобы сделать символ видимым. Таким образом, каждый только что напечатанный символ мгновенно «всплывает» на экране.
-
«st_erase»: чтобы между выводимым текстом и любым старым текстом, всё ещё видимым на экране, всегда оставалась пустая строка-разделитель, при каждом изменении экранной позиции позиция непосредственно под ней стирается (т.е. скрывается с использованием одинаковых атрибутов INK/PAPER).
-
«st_cursor»: отображение текстового курсора в новой позиции экрана.
-
«st_enter»: стирание следующей позиции на экране путём изменения цветовых атрибутов для её скрытия. Этот шаг повторяется до конца текущей строки, чтобы имитировать нажатие клавиши ENTER (код 13) на экране.
Для получения более подробной информации о реализации этого кастомного загрузчика обратитесь к исходному коду с комментариями.
Поделитесь вашим мнением о статье