________________________________________
Текст :VIATOR
Музыка :COOPER
________________________________________
(C) ВУ VIATOR/AVALON/RUSH/ASM
СОВЕРШЕННЫЕ МЕТОДЫ КОДИНГА И СОВРЕМЕННЫЕ
СПОСОБЫ РАБОТЫ C ГРАФИКОЙ
Большинство программистов на SPECCY
были вынуждены самостоятельно изучать
ассемблер и придумывать быстрые способы
работы с динамической графикой. К
сожалению, ни одной достойной книги по
данной теме так и не появилось.
Литература издательств "Питер" и
"Инфорком" устаревала еще до своего
появления. Книга,которую обещал написать
StalKer,могла бы стать отличным пособием
для кодера, но неизвестно появится ли
она когда-либо вообще...
В последнее время в электронных
изданиях стали публиковаться статьи,
посвященые профессиональным методам
работы с графикой. Но, как это не
печально, зачастую в них встречается
множество неточностей и ошибок.
Конечно, было бы прекрасно, если бы
подобную статью написал RST-7, IMP или
ALEX RAIDER, у которых богатый опыт в
данной области, но если уж они не
удосужились ничего написать за столько
лет, маловероятно, что они напишут
что-либо в будущем.
Как обычно, начнем с конца... и
ответим на вопрос, который долгое время
терзал умы кодеров всех стран: "Можно ли
вывести полноэкранную картинку (без
атрибутов) на экран за одно прерывание
?" - ДА !!! Это вполне возможно и
достаточно просто ! К сожалению, многие
программеры считали не так и даже
посвящали этой теме статьи в журналах, в
которых приводили кучу доводов и фактов,
отвергающих такую возможность. Стоит
вспомнить хотя бы статью в первом номере
журнала "ZX-POWER". Статья вполне
профессиональная, но кое-какие ошибки
имеются. Ошибаться может каждый, а наш
кодерский долг обязывает нас эти ошибки
исправлять, чем мы сейчас и займемся...
1. Рассмотрим наиболее часто
используемые методы вывода изображений
на экран.
а).Самый простой способ вывести спрайт-
перебрасывать его последовательно
байт за байтом, строку за строкой.
прорамма,выполняющая такие действия,
выглядит примерно так:
LOOP1 PUSH ВС
PUSH HL
LOOP2 LD A,(DE)
INC DE
LD (HL),A
INC L
DJNZ LOOP2
POP HL
CALL DOWN_HL
POP ВС
DEC C
JR NZ,LOOP1
RET
DE-адрес спрайта в памяти,
HL-адрес в экране,
В -ширина спрайта в знакоместах,
C -высота спрайта в пикселях.
DOWN_HL - переход на строку ниже.
Данный способ, несмотря на свою
простоту и примитивность,вполне подходит
для вывода статичной (неподвижной)
графики.
Изменив входные параметры в регистрах
HL,DE,ВС, можно вывести картинку любого
размера в любое место экрана. К сожале-
нию, такая гибкость не свойственна всем
остальным способам вывода графики.
б). Выводить графику с фиксированной
шириной.
LOOP1 PUSH DE
LDI ;колличество зависит
... ;от ширины спрайта.
POP DE
EX AF,AF'
CALL DOWN_DE
EX AF,AF'
DEC A
JR NZ,LOOP1
RET
HL-адрес спрайта в памяти,
DE-адрес в экране,
A -высота спрайта в пикселях.
Этот способ несколько быстрее предыду-
щего, но из-за привязки к определенной
ширине спрайта, менее распостранен.
в.) Вывод графики при помощи стека.
Этот и следующий приемы вывода гра-
фики более эффективны и быстры по
сравнению с предыдущими,но вместе
с тем и несколько сложнее.
Итак, пересылка стек-экран:
Программа создает при первом
запуске рабочий блок , который
собственно и будет заниматься
переброской графики.
Адрес графики заносится в регистр
SP, затем значения со стека
последовательно извлекаются и
заносятся в экран. Примерно это
будет выглдеть так:
...
POP HL
LD (#ЧOOO),HL
POP HL
LD (#ЧOO2),HL
POP HL
LD (#ЧOOЧ),HL
...
Естественно, что адреса, в
которые будет бросаться графика,
заранее фиксированы, фиксирваны
также размеры спрайта. Длина
полученой процедуры в памяти
зависит от размеров спрайта, и
будет в два раза большей чем он. То
есть, если вы перебрасываете спрайт
на весь экран (6144б.),
размер программы будет - 12288б.
Такое расточительство вполне
оправдано, так как скорость рабо-
ты этой программы гораздо выше чем
предыдущих. Один байт
перебрасывается за 13 тактов. Тем
более, что до инсталляции программа
будет занимать всего каких-нибудь
1OO байтов.
г). Следующий способ похож на прдыдущий
но более подходит для скроллингов
вверх и вниз,чем для вывода графики
Так как к определенным адресам
здесь привязывается и источник, и
приемник.
...
LD HL,(#41OO)
LD (#ЧOOO),HL
LD HL,(#41O2)
LD (#ЧOO2),HL
LD HL,(#41O4)
LD (#ЧOOЧ),HL
...
Абсолютно не используется стек и не
изменяет регистры,за исключением HL
Использует на каждый
перебрасываемый байт графики по три
байта памяти.Один байт
перебрасывается за 16 тактов.
д). Наиболее быстрым способом
СКРОЛЛИНГА экрана по вертикали и
довольно совершенным методом вывода
графики из памяти является
следующий:
...
LD SP,адрес-источник
POP HL
POP DE
POP ВС
POP AF
POP IX
POP IY
EXX
POP HL
POP DE
POP ВС
LD SP,адрес-цель
PUSH ВС
PUSH DE
PUSH HL
EXX
PUSH IY
PUSH IX
PUSH AF
PUSH ВС
PUSH DE
PUSH HL
...
Этот метод очень эффективен,но все
равно не позволяет вывести весь
экран за одно прерывание.
е). Теперь рассмотрим САМЫЙ совершенный
и малоиспользуемый способ вывода
графики:
Данные перемешиваются с программой,
и получается конструкция типа:
...
LD SP,адрес в экране
LD HL,два байта графики
PUSH HL
LD HL,очередные два байта
PUSH HL
LD HL,очередные два байта
PUSH HL
...
А теперь давайте немного посчитаем.
LD HL,число - 1O тактов,
PUSH HL - 11 тактов.
Для вывода полноэкранной картинки
необходимо повторить этот фрагмент
3O72 раза = 21*3O72 = 64512 тактов.
LD SP,число - 1O тактов.
Стек изменять придется( хотя и не
обязательно )перед выводом каждой
строки,то есть 192 раза = 1O*192=
=192O тактов.
И того,все в целом будет занимать
64512+192O=66432 такта !!!
Как видите,все вполне бозможно и ре
ально.Возможности Спектрума не ис-
черпали себя и до сих пор !
SPECCY ALIVE FOREVER !!!
Данный способ вывода графики далеко
не нов и придуман не мной. Во всяком
случае им пользовались многие мои друзья
из RUSH, и рассказал мне о нем IMP еще
несколько лет назад...
При использовании этого метода также
расходуется по два байта памяти на
каждый выводимый байт графики. Вывод
полноэкранной картинки абсолютно без
повторяющихся фрагментов элементарно
укладывается не только в пентагоновское
прерывание, а и в прерывание на любой
нормальной машине, например, SCORPION,
PROFI и т.д. Правда на фирменной машине
появляются проблемы связанные с тем, что
в ней раздельные поля памяти, то есть
экранная память работает гораздо
медленее, а нам приходится помещать стек
в экран...
Но если бы у фирменного SPECCY было
реально 7OOOO тактов в прерывании, все
отлично успевало бы работать, даже
осталось бы место для музыки.
Чтоб заставить изображение
двигаться, придется менять адреса,
заносимые в SP, но и это вполне реально.
Остается лишь сказать, что в нашей новой
демке "HALLUCINATIONS OPERA" есть такой
FX, правда на весь экран он работает
только на Пентагоне, но надо же иметь
совесть, как говорит наш Знахарь...
И еще несколько полезных советов для
кодеров, которые были почерпнуты из
многолетнего опыта работы на ассемблере
как моего личного, так и многих других
кодеров:
При выводе изображения по маске,
спрайт удобнее всего хранить
перемешанным с маской. Байт графики,
затем байт маски и т.д. Выводиться такой
спрайт будет так:
...
POP DE ; два байта графики
LD A,D
AND (HL)
OR E
LD (HL),A
INC L
...
Стек при этом должен указывать на
графику, а HL на экран.
Наиболее быстрый способ очистки эк-
рана:
LD SP,#58OO
LD ВС,O
PUSH ВС - повторить 3O72 раза.
Также можно очищать любые области в
памяти и т.п. Конечно вовсе не
обязательно делать такие процедуры
абсолютно без циклов. Можно, например,
сделать всего 256 PUSH'ей, и зациклить
этот блок нужное колличество раз. Особых
потерь в скорости не будет.
Теперь очень важная вещь, которую
следует знать всем, кто решил писать
качественные программы на SPECCY. При
выводе большого количества графики,
вероятность того, что телевизионный луч
пересечется с выводимым в данный момент
блоком графики очень велика. Это
приведет к тому, что в месте пересечения
с лучем, динамическое изображение будет
дискрировать. Например, при скроллинге
большого фрагмента экрана верхняя часть
его будет сдвинута, затем луч "догонит"
место вывода, и дальнейшая половина
картинки будет выводиться с отставанием
на один кадр. Еще более ужасная картина
получается, если изображение на экране
времменно стирается, развертка может
попасть именно на такой момент, и тогда
могут появиться исчезающие спрайты,
промигивающие курсоры и прочее
ламерство.
Как с этим бороться:
Можно постараться, чтобы развертка не
пересекалась с выводом на протяжении
всего прерывания. Так, можно сначала
вывести графику в среднюю и нижнюю трети
экрана, а в конце, когда лучь уже
пройдет верхнюю половину экрана, можно
вывести верхнюю треть. Этот метод
используется довольно часто, но из-за
огромных различий в конструкции
компьютеров развертка на разных типах
SPECCY может сильно отличаться, что
станет причиной дискрирования
изображения. Стоит посмотреть как
работает "SHOCK" на Пентагоне, и вам все
станет ясно.
Самым эффективным и практичным
методом в данном случае является
использование дополнительного экрана в
машинах с O3Y 128к и больше. Наличие
двух экранов, которые можно переключить
мгновенно при помощи одного
единственного OUT'а, очень сильно
облегчает жизнь программисту.
Работа с двумя экранами
производится следующим образом: на
прерывания (не обязательно) вешается
небольшая программа, которая поочередно
подключает то 7 страницу и обычный
экран, то 5 страницу и теневой экран.
Выглядеть это может примерно так:
SCREEN_CHANGER:
SCR LD A,#55 ; %O1O1O1O1
RRCA
LD (SCR+1),A
LD A,#17 ; 16+7
JR C,PAGER
LD A,#1D ; 24+5
PAGER:
LD ВС,#7FFD ; 32765
OUT (C),A
RET
Все процедуры, работающие с экраном,
должны быть написаны с учетом того, что
экран теперь находится не с адреса #ЧOOO
(16384 dec), а с адреса #COOO (49152
dec). Процедуру DOWN_HL менять не нужно,
она и так будет работать корректно.
Суть данного метода состоит в том,
что изменять (стирать, двигать и т.д.)
Вы будете не тот экран, который
отображается в данный момент на
мониторе, а тот, который находится в
памяти и не виден зрителю. Никакого,
дискрирования не будет абсолютно, даже
если Вы будете изменять весь экран
целиком. Так например сделано в
словацкой демке "ECHOLOGY", и она
великолепно работает на всех машинах,
вкючая Пентагон, за исключением
MULTICOLOR'ов, разумеется. Кстати,
MULTICOLOR - это и есть дискрирование...
Конечно писать программы используя
один экран - круче, но на практике это
не целесообразно. Я советую Вам не
мучить пользователей и писать soft под
два экрана.
А теперь совет, не имеющий прямого
отношения к работе с динамической
графикой, но он может пригодиться Вам в
том случае, если у Вас не хватает
процессорного времени на проигрывание
музыки, а свободной памяти остается
много. В таком случае можно поступить
следующим образом:
Музыка раскладывается на
составляющие компоненты и затем
проигрывается минуя player. Простой
вывод данных в порты будет занимать
каких-нибудь ЗОО тактов, если сравнить с
тем, что player от "ASM" требует более
8OOO тактов - разница ощутима.
Желательно не подгружать уже
разложенную на данные музыку с диска, а
создавать ее непосредственно перед
запуском программы , так-как размеры
полученного блока данных могут достигать
десятков киллобайт. Можно также
попробовать компрессировать
повторяющиеся фрагменты.
Данный метод использовался во
многих демонстрациях - "ECSTASY",
"SHOCK", "SATISFACTION", "INSULT" и др.
А вот наиболее красивый и компакт-
ный способ отключения звука на АУ,
который я видел (писал его не я):
SHUT_UP:
LD HL,#OOOD
SHUT1 LD ВС,#FFFD
OUT (C),L
LD В,#BF
OUT (C),H
DEC L
JR NZ,SHUT1
RET
Если кто-то не понял, что именно я
имел в виду под процедурой DOWN_HL или
DOWN_DE, я привожу ниже их текст:
DOWN_HL:
INC H
LD A,H
AND 7
RET NZ
LD A,L
ADD A,#2O
LD L,A
RET C
LD A,H
SUB 8
LD H,A
RET
DOWN_DE:
INC D
LD A,D
AND 7
RET NZ
LD A,E
ADD A,#2O
LD E,A
RET C
LD A,D
SUB 8
LD D,A
RET
Иногда бывает нужно пересчитать адрес
в экране в адрес соответствующий данному
знакоместу в области атрибутов. Сделать
это можно так:
SCR_ATR:
LD A,H
RRCA
RRCA
RRCA
AND 3
OR #58
LD H,A
RET
На сегодня хватит... Конечно
способов повышения эффективности
программ может быть бесконечное
множество. Для каждого конкретного
случая есть свои приемы и тонкости. Их
нужно знать не только для того, чтобы
писать сногсшибательные эффекты...
Не умея пользоваться достижениями
современных кодеров, вы не сможете
писать качественные динамические
игрушки, да и хорошей системной
программы написать невозможно, пользуясь
методами десятилетней давности.
Поэтому дерзайте ! Возможно, скоро
Вам удастся написать полноэкранный
multicolor или еще какое-нибудь чудо
кодинга !
Good lucK !!!
P.S. Неплохо бы написать
профессиональную книгу по кодингу, в
которой были бы описаны все методы
Не умея пользоваться достижениями
современных кодеров, вы не сможете
писать качественные динамические
игрушки, да и хорошей системной
программы написать невозможно, пользуясь
методами десятилетней давности.
Поэтому дерзайте ! Возможно, скоро
Вам удастся написать полноэкранный
multicolor или еще какое-нибудь чудо
кодинга !
Good lucK !!!
P.S. Неплохо бы написать
профессиональную книгу по кодингу, в
которой были бы описаны все методы
программирования и множество полезных
процедур. Возможно, если ктонибудь меня
попросит, я мог бы попробовать написать,
что-то подобное...
Other articles: