Optron #41
19 ноября 2000

Ликбез - Ассемблеp - взгляд издалека: работа с графикой.

<b>Ликбез</b> - Ассемблеp - взгляд издалека: работа с графикой.

        Ассемблеp - взгляд издалека.

               Продолжение.
  Начало в || 20, 21, 24, 25, 28-30,
              32, 33, 35, 36

  {} Инфарх, 2000

  Вот мы и снова встретились!  После  рас-
сказа о распределении памяти Speccy вы на-
верняка  полны  желания использовать полу-
ченную информацию по  назначению,  написав
какую-нибудь программу;  в этом я вам пос-
тараюсь помочь.
  Не секрет, что каждый программист желает
видеть,  как работает его  программа.  Для
этого он должен организовать в своей прог-
рамме интерфейс общения  с  пользователем.
Начаать надо со средств вывода, а именно -
с экрана. Итак...

         Общая организация экрана

  Не исключено, что по Бейсику вы уже зна-
комы с  делением экрана на строки,  знако-
места и т.д.  Но если вы всё-таки этого не
знаете,  то  прочитайте этот раздел внима-
тельно.
  Минимальной единицей экрана является од-
на его точка - пиксел  (от англ.  "picture
element", pixel - элемент изображения).
  Собранные в матрицу 8х8,  точки образуют
знакоместо.  В нём размещается стандартный
символ.  Для знакоместа также определяется
атрибут  -  цвет  пиксела установленного и
цвет пиксела, сброшенного в знакоместе.
  Экран содержит 24 строки по 32 знакомес-
та в каждой. Если необходимо указать коор-
динаты определённого знакоместа, то отсчёт
ведётся от верхнего  левого  угла  экрана.
Расположеное  там знакоместо имеет коорди-
наты 0, 0. Знакоместо справа от него имеет
координаты 1, 0, а снизу - 0, 1. Знакомес-
то  в нижнем правом  углу  имеет кординаты
31, 23.
  Перейдём  к графике.  На  более   низком
уровне  экран  делится  на столбцы (верти-
кальные) и линии  (горизонтальные).  Всего
экран содержит 192x256 пиксел. Пиксел сле-
ва вверху имеет координаты 0, 0,  а справа
внизу - 255, 191. Младшие три бита коорди-
наты Х определяют, какой бит в байте адре-
са соответствует данному пикселу.
  А теперь посмотрим, как всё это выводит-
ся на экран.
  В Спектруме его экрану соответствует об-
ласть памяти, начинающаяся с адреса #4000.
В её начале определяется графика,  а затем
- атрибуты.  Каждый байт в области графики
определяет состояние восьми пикселов экра-
на. Пиксел будет изображён на экране, если
в байте будет  установлен  соответствующий
бит. Например, возьмём байт %10011101. Пе-
решлём его по адресу #4000 и посмотрим  на
экран. Видите, в самой верхней линии у ле-
вого края экрана появились точки? Они име-
ют вот такой вид:

  █ ░ ░ █ █ █ ░ █

  ░ - нет точки
  █ - есть точка

  Легко понять соответствие битов и точек,
сравнив  пересланый в экран бит и получен-
ное изображение:  каждый байт  в  экранной
области памяти соответствует 8-ми точкам и
равен ширине знакоместа.
  Зная, что экран состоит из

         32 х 24 = 768 знакомест,

причём каждое знакоместо имеет по вертика-
ли 8 линий  (8  байт),  нетрудно  получить
размер области графики в байтах:

     32 х 24 х 8 = 6144 (#1800) байт

  Теперь - об атрибутах. Их область памяти
расположена сразу за графической областью,
начиная с адреса #5800. Т.к. атрибут зада-
ётся для целого знакоместа (а знакомест  у
нас -  768) и каждый атрибут занимает один
байт, то нетрудно вывестии и размер облас-
ти атрибутов - 768 байт.
  Вот как  этот байт  определяет состояние
экрана:
  - три младших бита  (D2, D1, D0)  задают
цвет чернил (INK, установленныe пикселы);
  - биты D5, D4, D3 - цвет бумаги  (PAPER,
пикселы сброшенные);
  - бит D6  определяет  яркость  (BRIGHT);
если он установлен, то информация на экра-
не для данного знакоместа  отображается  с
повышеной яркостью.
  Остался бит D7 - бит  мерцания  (FLASH).
Если он включен, то через каждые 16/50 до-
лей секунды ULA меняет местами цвет бумаги
и чернил для данного знакоместа.
  Рассмотим байт атрибута в общей форме:

           Paper
           ┌────┐
   ┌──┬──┬──┬──┬──┬──┬──┬──┐
   │D7│D6│D5│D4│D3│D2│D1│D0│
   └──┴──┴──┴──┴──┴──┴──┴──┘
     │  │           └────┘
     │  └Bright      Ink
     └Flash

  Цвета для экрана Спектрума имеют следую-
щее соответствие кодов:

   0       чёрный
   1  ███  синий
   2  ███  красный
   3  ███  малиновый
   4  ███  зелёный
   5  ███  циан
   6  ███  жёлтый
   7  ███  белый


       Организация экранной памяти

  Ознакомившись  с экранной памятью  Спек-
трума, не подумайте,  что она организована
линейно! Её структура более сложная, в чём
мы сейчас и убедимся. Наберите и запустите
такую программу:

        LD     HL,#4000 ; Очищаем экран
        LD     DE,#4001
        LD     BC,#1800
        LD     (HL),L
        LDIR
        LD     BC,#2FF  ; И устанавливаем
                        ; его атрибут
        LD     (HL),7   ; ink=7, paper=0
        LDIR

        LD     HL,#4000 ; Начало экранной
                        ; памяти
        LD     DE,#1800 ; Размер
                        ; графической
                        ; области
SCR
        LD     (HL),#FF ; Пересылаем
                        ; в экран
        INC    HL       ; Следующий байт
                        ; экрана

; Задержка 1/10 секунды для наглядности

        LD     B,5
PAUSE
        HALT
        DJNZ   PAUSE

; Повторяем для всей области графики

        DEC    DE
        LD     A,D
        OR     E
        JR     NZ,SCR

        RET

  Назначение  этой,  весьма  простой  про-
граммы - последовательное  заполнение  об-
ласти памяти графики экрана значением #FF,
что соответствует  одной  закрашеной линии
знакоместа.  Кстати,  если вы  испoльзуете
ассембер  XAS,  запускайте  эту программу,
используя  комбинацию клавиш  CS+R, - и вы
сможете после  завершения работы программы
лицезреть получившееся изображение  столь-
ко,  сколько  угодно.  Лишь  после нажатия
"any key" (не путать с RESET) вы вернётесь
в редактор.
  Теперь рассмотрим специфику работы  све-
женабранной программы более подробно.
  Итак,  первые 32 байта  экранной  памяти
соответствуют  первой  линии первой строки
знакомест.  Впрочем, вы это видели. Навер-
ное, вы подумали, что далее следует вторая
линия первой строки?  А вот и  нет!  Далее
следует первая линия второй строки!  И так
последовательно заполняются  все 8 строк -
от первой до восьмой.
  Можно предположить,  что следующий  байт
будет соответствовать первой линии девятой
строки...  Но это опять-таки неверно. Оче-
редь   заполнения  возвращается  к  первой
строке,  на этот раз - ко второй линии.  И
вот так,  по линии из каждой строки знако-
мест, заполняются все восемь строк.
  Как вы уже могли заметить,  экран разде-
ляется на трети  и  переход  на  следующую
треть  происходит  только после заполнения
предыдущей. Конечно, такая организация эк-
рана  создаёт  определённые  сложности для
программиста,  но при  некоторой  ловкости
рук это легко решается. О технических под-
робностях поговорим потом,  а сейчас  зай-
мёмся областью атрибутов.
  Честно говоря,  особо заниматься ею и не
придётся.  В отличие от графики,  атрибуты
организованы совершенно  линейно.  Сначала
последовательно (слева направо) заполняет-
ся нулевая строка, далее очередь переходит
к первой, и так до конца экрана. Дабы про-
иллюстрировать всё вышесказанное, обратим-
ся опять-таки к программе.
  В  предыдущем  примере удалите последнюю
команду (RET) и с этой позиции введите та-
кие строки:

        LD     DE,#300  ; Размер области
                        ; атрибутов
ATTR
        LD     (HL),2
        INC    HL
        LD     B,5
PAUSE1
        HALT
        DJNZ   PAUSE1
        DEC    DE
        LD     A,D
        OR     E
        JR     NZ,ATTR
        RET

  Вот вам наглядная демонстрация всего вы-
шесказанного! Как видите, работа с атрибу-
тами весьма проста.
  А теперь отвлечёмся на  некоторое  время
от освоения экрана и глянем повнимательнее
на ту программу,  которую  вы  только  что
набрали.  Естественно,  она  не  идеальна.
Ведь это только пример,  в котором нагляд-
ность - важнее всего. А вот давайте попро-
буем записать его так,  чтобы он  был  как
можно более компактным. Тем более, что все
команды ассемблера вам уже известны.
  Итак...

        LD     HL,#4000
        PUSH   HL
        LD     DE,#4001
        LD     BC,#1800
        PUSH   BC
        LD     (HL),L
        LDIR
        LD     BC,#2FF
        LD     (HL),7
        LDIR
        POP    BC
        POP    HL
        LD     E,#FF
        CALL   FILL
        LD     E,2
        LD     BC,#300
FILL
        LD     (HL),E
        INC    HL
        LD     D,5
WAIT
        HALT
        DEC    D
        JR     NZ,WAIT
        DEC    BC
        LD     A,B
        OR     C
        JR     NZ,FILL
        RET

  Проанализировав на досуге эту програмку,
вы наверняка поймёте её принцип работы.
  А мы вернёмся к экранным операциям.
  Узнав методику   распределения  экранной
памяти,  следует научиться и тому, как на-
ходить в экране адрес  необходимого  байта
(пока  только байта,  до пикселей мы добе-
рёмся позже).
  Вычислить адрес  первого  байта  первого
знакоместа в строке, номер которой переда-
ётся  через регистр А,  вам позволит такая
программа:

        LD     H,A      ; В "А" находится
                        ; номер строки
        RRCA
        RRCA
        RRCA
        AND    #E0
        LD     L,A
        LD     A,H
        AND    #18
        OR     #40
        LD     H,A

; На выходе в HL адрес первого байта
; заданной строки

  Вот и вся сложность. Не забудьте только,
что если вы хотите использовать  процедуру
как  подпрограмму,  то  в конце необходимо
добавить команду RET.  Если вы  проследите
за выполнением процедуры пошагово, причём,
обращая внимание на содержимое аккумулято-
ра в двоичной форме,  то без труда поймёте
принцип работы.
  А теперь  попробуем  несколько  изменить
пример, чтобы получить возможность расчёта
адреса с учётом ещё и столбца. Если в пре-
дыдущем примере мы  в  качестве  аргумента
использовали номер строки в регистре А, то
здесь надо будет использовать ещё и  номер
столбца в регистре L. Смотрите:

        LD     H,A
        RRCA
        RRCA
        RRCA
        AND    #E0
        ADD    A,L    ; Добавленная строка
        LD     L,A
        LD     A,H
        AND    #18
        OR     #40
        LD     H,A

  Как видите, путём добавления всего одной
команды  пример  был  доработан  до  более
сложного. Попробуйте  теперь  сделать так,
чтобы расчитывался адрес любой линии экра-
на (0..191). Наверняка это окажется полез-
ным упражнением.
  Ну, поехали дальше...
  Очень редко бывает  необходимо  что-либо
делать  в  пределах  одной линии пикселей.
Обычно изображение занимает  по  вертикали
гораздо  больше места.  И для того,  чтобы
перейти на следующую линию,  вовсе не обя-
зательно заново высчитывать её адрес.  На-
пример,  если вам нужна следующая линия  в
пределах знакоместа  (при печати символа),
достаточно  провести  инкремент   старшего
байта адреса.
  На тот случай,  если переход должен быть
осуществлён  без привязки к знакоместу,  а
просто на следующую строку в пределах  эк-
рана,  предлагаю вашему вниманию небольшую
подпрограмму:

DOWN_HL
        INC    H
        LD     A,H
        AND    7
        RET    NZ
        LD     A,L
        ADD    A,#20
        LD     L,A
        RET    C
        LD     A,H
        SUB    8
        LD     H,A
        RET

  На  входе  сия  процедура получает адрес
байта  в  экране, а на выходе выдаёт адрес
байта,  расположенного  под ним. Заметьте,
это вовсе  не  должен быть первый байт ли-
нии. Если вы предпочитаете использовать не
регистр HL,  а нечто иное, то просто заме-
ните соответствующие регистры в процедуре.
А заодно, раз уж зашёл разговор на эту те-
му, посмотрите и на процедуру, выполняющую
действие прямо противоположное - рассчиты-
вает адрес строки предыдущей:

UP_HL
        DEC    H
        LD     A,H
        AND    7
        CP     7
        RET    NZ
        LD     A,L
        SUB    #20
        LD     L,A
        RET    C
        LD     A,H
        ADD    A,8
        LD     H,A
        RET

  Иногда бывает  нужно пересчитать адрес в
экране в  адрес,  соответствующий  данному
знакоместу  в  области атрибутов.  Сделать
это можно так:

SCR_ATR
        LD     A,H
        RRCA
        RRCA
        RRCA
        AND    3
        OR     #58
        LD     H,A
        RET

  Ну, на этот раз пока хватит.
  До следующего  урока  попробуйте позани-
маться написанием программ  для  рисования
графических  примитивов - для практики это
весьма полезно.
  Удачи!

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

              ──══════════──




Другие статьи номера:

4 килобайта - номер посвящен Сhaos Constructions'2000.

Рубрика X - рассуждения о Chaos Constructions'000 и обзор дем.

ZX-обоз - Партийная жизнь (окончание).

Ликбез - Ассемблеp - взгляд издалека: работа с графикой.

Стихи - "Выбор".

Реклама - обьявления для Львова.

https://govoritel.ru очень полезная вещь громкоговорители на пояс.

Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
Реклама - Реклама и объявления ...
Форум - Особенности ассемблера ZX ASM 3.0.
News - Вся прошедшая неделя проходила под мнимым лозунгом "Даешь хороший Интернет!".
О релизах - Провел я тут акцию-провокацю по вопросам необходимости существования релизов. Конечно я немного схалтурил...
Articles - История итальянской спектрумовской софт-сцены в Италии.

В этот день...   26 апреля