Секреты LPRINT, часть 1


Я уже писал об использовании трюка с LPRINT в статье об апрельском демо "Making of Back to Basics". Но поскольку снова звучат просьбы о пояснениях, расскажу об этом трюке поподробнее.

Начнем с самого начала. Впервые трюк с LPRINT я встретил в пиратском кассетном загрузчике игры TIGER ROAD (тот самый релиз):



К тому времени я уже достаточно наигрался с бейсиком, делал бегущие строчки (подсмотренные в другом загрузчике), разобрался с большинством команд и пытался делать игры, задавая переопределяя символы через UDG и рисуя лабиринты типа игры KLAD. То есть я уже ощущал возможности и ограничения бейсика. Так вот, когда я увидел LPRINT в действии в том загрузчике, — я был в шоке. Как так? Каким образом? Это был нереальный хак, позволявший выводить символы, растянутые по вертикали в 8 раз в любую треть экрана. Ведь это было не документировано!

Вдоволь напрактиковавшись с этим трюком тогда в 90-х, я не нашел ему никакого применения. Т.к. не понимал ничего — ни принципа его работы, ни устройства экранной памяти спектрума. Циферки в POKE 23681,64 были для меня чистой МАГИЕЙ.

В следующий раз я столкнулся с LPRINT в 2011 году в демо BBB by JtN/4D. Надписи в этом демо так же печатались по середине экрана. Но был и спецэффект — они хаотично прорезались пустыми линиями.



Это натолкнуло меня на мысль, что кроме стандартного пзу-шного шрифта можно выводить еще блочные символы Graphics Mode и, пожалуй, не только их!

Первое настоящее переосмысление случилось в апреле 2015 года. В чем для меня была суть LPRINT, как я понимал его до переосмысления? Очень просто:

В системную переменную 23681 записывались значения в цикле 64-71 для первой трети экрана, 72-79 для второй, 80-87 для третей. После каждой записи выводилась строка через LPRINT:

10 FOR Q=72 TO 79
20 POKE 23681,Q
30 LPRINT "          HELLO WORLD!"
40 NEXT Q

Что было дальше? Первое, что пришло в голову — а что будет, если на каждой итерации цикла выводить разные строки? И что такое 72-79?

Если вспомнить структуру и адресацию экранной области, то 72 — это старший байт адреса начала средней трети экрана. 73 — это старший байт следующего 256-байтного блока в средней трети, который идет на 1 строчку пикселей ниже предыдущего блока. То есть вся треть экрана состоит из 8 блоков по 256 байт и, соответственно, в ячейке 23681 адресуются эти самые блоки. LPRINT же занимается тем, что строку символов он печатает в буфер (по умолчанию расположенный с адреса 23296 — тот самый буфер принтера). Если мы перенаправляем вектор печати в экранную область, то тем самым мы печатаем 1 строку символов в один из 256-байтных блоков той или иной трети экрана. Сообразно структуре экрана данные ложатся в 8 разных пиксельных строк выбранной трети экрана с заданным смещением по вертикали в пикселях, в зависимости от старшего байта экранного адреса, который мы записали в системную переменную 23681.

Если же каждую итерацию цикла выводить разные строки, то в каждую треть мы можем вывести произвольный спрайт размером 256x64 пикселей. Для этого нам понадобится использовать системную переменную CHARS (23606,23607), строковую переменную с символами с кодами от 32 до 63, например и специальным образом перекодированный спрайт.

Системная переменная CHARS, в отличие от UDG, позволяет нам не задавать набор дополнительных 21 символов, а переопределять весь основной шрифт из 96 символов. Хотя для вывода в одну треть экрана нам будет достаточно и первых 32 символов (при выводе полного экрана конечно можно экономить на одном POKE и задействовать все 96 символов, выводить сразу 3 блока — по одному в каждую треть, что потребует немного иной перекодировки данных).

Код вывода спрайта 256x64 в первую треть экрана будет выглядеть следующим образом:

10 CLEAR 49151: LOAD "SCREEN" CODE 49152
20 FOR a=64 TO 71
30 POKE 23607,191+a-64: POKE 23681,a: LPRINT " !"+CHR$ 34+"#$%&'()*+,-./0123456789:;<=>?"
40 NEXT a


Итак, далее нам необходима перекодировка экранного спрайта в формат для вывода через LPRINT. Она требуется потому что данные для вывода на экран берутся из символов. И если экран кодируется линейными блоками по 256 байт, то шрифт в памяти — это спрайт размером 1хN байт. То есть нам надо взять каждую треть экрана и перекодировать байты в другом порядке: первый символ нашего нового шрифта мы берем из адресов 16384 до 16384+255 с шагом 32, далее таким же образом вся первая строка до 16384+31, затем смещение на 256 байт от начала экрана (на 1 строчку пикселей вниз) и снова таким же образом 32 символа. Таким образом на бейсике перекодировщик выглядит следующим образом:

10 CLEAR 49151: LET C=49152
20 FOR q=16384 TO 16384+6143 STEP 256
30 FOR w=q TO q+31
40 FOR e=w TO w+255 STEP 32
50 POKE c,PEEK e: POKE e,0: LET c=c+1
60 NEXT e: NEXT w: NEXT q


После чего готовый блок 6144 байт мы можем выгрузить с адреса 49152. Такой метод я использовал для вывода полноэкранных картинок уже в Back 2 Basics.

Оставалось придумать, каким образом вывести атрибуты, чтобы был полноценный вывод полноэкранной картинки.

Первой мыслью было закодировать атрибуты управляющими кодами в обычный PRINT и напечатать поверх. Но у меня что-то не срослось с печатью в нижние две строки экрана (с этим я разобрался только в YSKB) и пришлось думать дальше.

В результате нашлось очень простое решение. Ведь если мы переставляем вектор LPRINT в экранную область, то кто нам запрещает выставить вектор в область атрибутов?

Более того, вывод атрибутов получается довольно быстрый — всего за 3 связки POKE+POKE+LPRINT. А если задать 3 строковых переменных для 96 символов, то снова экономим одно POKE.

Перекодировка атрибутов немного проще перекодировки пикселей:

10 CLEAR 49151: LET c=49152+6144
20 FOR q=22528 TO 22528+767 STEP 256
30 FOR w=q TO q+31
40 FOR e=w TO w+255 STEP 32
50 POKE c,PEEK e: POKE e,0: LET c=c+1
60 NEXT e: NEXT w: NEXT q


Таким образом сделан вывод полноэкранных картинок в B2B и YSKB:

10 CLEAR 49151: LOAD "" CODE 49152
20 LET a$=" !"+CHR$ 34+"#$%&'()*+,-./0123456789:;<=>?"
30 FOR a=1 TO 27
40 POKE 23607,190+a: POKE 23681,63+a: LPRINT a$
50 NEXT a


Забегая дальше, можно сказать, что таким образом можно перебрасывать произвольные данные в памяти. Но надо учитывать, что данные перебрасываются не линейно, а неким образом «транспонируются», т.е. меняется порядок байтов.

Закономерный вопрос — что становится с блоком данных при серии подобных перебросок? Ответ: после 8ой переброски блок принимает исходный порядок байтов, т.е. всего мы имеем 8 различных «транспонированных» состояний. Имея буфер в 256 байт и достаточное количество времени мы можем перебрасывать произвольные блоки данных, не меняя порядка байтов внутри.

Учитывая зацикленность «транспонирования» через LPRINT можно написать конвертор картинок в описанный выше формат через сам же LPRINT. Работать он будет быстрее, чем приведенные выше процедуры.

Продолжение следует.

PS картинка в шапке была больше для привлечения внимания :) Но она и вправду выводилась в демо с помощью LPRINT и переключения теневого экрана.

22 комментария

avatar
«с помощью LPRINT и переключения теневого экрана. » — ага! мучил этот вопрос — как так быстро
  • VBI
  • +3
avatar
Быстро — только за счет 2го экрана конечно. Но к сожалению не очень просто даже имея второй экран быстро показать 4 фуллскрин кадра ничего не перебрасывая между этими экранами.
avatar
Ага, тоже голову ломал. Драйвер как всегда крут!
avatar
В чем? Во включении 2го экрана на 128к? Щас все кодеры усмехнулись :)
avatar
А кто ещё 2й экран на бейсике до тебя использовал?
avatar
JtN/4D в BBB, 2011 год.
avatar
avatar
Чо? Где?! Какие сиськи?!!!
avatar
Ты видишь сиськи? И я нет. А они есть.
avatar
А, ну так бы сразу и сказали.

«Я человек простой. Не вижу сиськи — всё равно ставлю лайк!»
avatar
сиськи, жопа там, вот все это…
avatar
жопе я, пожалуй, тоже лайк поставлю
avatar
по вашим каментам создается ощущение, что демы на бейсике это жопа.
avatar
Раньше мы только догадывались, но теперь — знаем! :)
avatar
Офигенно содержательный тред-то! О животрепещущих проблемах кодинга на бейсике!
avatar
И даже если так, то согласись как смело — добавить жопу в жопу!
avatar
One cannot have too much ass!!!
avatar
Я с самого начала ждал, как жопа из оригинала ляжет на спековские аттрибуты. Не разочаровался!
avatar
А, я понял. Вы ждали «Making of YSKB», а я тут про какой-то LPRINT.)))
avatar
весьма годно про lprint. то, что он текст так выводит, я и до этого знал, а вот о возможности относительно быстро перебрасывать произвольные куски памяти в любое место как-то и не догадался… это очень круто! два эффекта уже готовы, отрисовываю графику под них:)
avatar
Почему же. LPRINT тоже был нужен — я, по крайней мере, стал чуть больше бейсика знать, а то кроме рандомизе уср и набрать ничего не смогу.
avatar
Есть ещё вторая часть статьи про LPRINT, на днях опубликую. Там про более продвинутые эффекты.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.