Расширенный экран «Profi», что это такое и как с ним работать. Практикум 002. Вертикальный скроллинг

от автора

Продолжаем цикла статей по работе с расширенным экраном компьютера «Profi». Второй практикум.

В предыдущих статьях была описана структура расширенного экрана компьютера «Profi», работа с файлами в OS CP/M, а так же загрузка и вывод картинки из файла на экран. Теперь давайте рассмотрим методы работы с расширенным экраном и попробуем проскроллировать его.

Статья была опубликована в 2023 году в 44 номере журнала по «Downgrade».

Статья написана в соавторстве с Вадимом Чертковым.


В журнале «3aRulem» выходил цикл наших статей посвященных компьютеру «Profi». В рамках этого материала освящались разные аспекты работы с этой машиной. В том числе есть материал по расширенному экрану компьютера «Profi», в котором предоставлялась подробная информация о его строении, а так же началась публикация практикума по работе с ним. К сожалению, редакция журнала «3aRulem» временно прекратила работу над подготовкой новых номеров, сосредоточилась на иных своих проектах. По согласованию со всеми сторонами, публикацию цикла статье было решено перенести в этот журнал. В тех местах, где это необходимо будут даваться ссылки на номера журнала «3aRulem». И так начнём наш практикум…

Скроллинг экрана — одна из самых распространенных операций с ним. Давайте рассмотрим вертикальный скроллинг расширенного экрана, а горизонтальный скроллинг разберем в одной из следующих статей.

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

  1. Побайтная переброска через регистр A. Самый медленный способ, но позволяющий работать со сложными структурами данных. Мы использовали его при выводе на расширенный экран картинки из файла формата GRF.

  2. Переброска данных с использованием команды LDI. Один из самых быстрых методов переброски. В идеальном случае на переброску одного байта тратится 16 тактов.

  3. Переброска через стек, с использованием конструкции POP/PUSH. Самый быстрый из известных методов переброски, в идеальном случае на переброску одного байта тратится 10,5 тактов, но при этом получается сложная организация процесса переброски, требуются значительные дополнительные затраты которые увеличивают необходимое число тактов.

Так как расширенный экран «Profi» имеет линейную структуру, а значит, нет необходимости работать с каждым байтов в отдельности, реализуем только методы 2 и 3. Начнем с использования команды ассемблера LDI, как более простого.

Мы уже знаем, что расширенный экран «Profi» имеет строение практически идентичное с обычным экраном Спектрума. А значит, он имеет линейную структуру только в пределах одной пиксельной линии, которые организуют знакоместа, а они сегменты экрана. И для перемещения по экрану нужно проводить определенные расчёты. Как и в прошлой статье, для уменьшения объемов расчётов, воспользуемся той же таблицей адресов начала знакомест. А внутри знакомест переходить к следующей пиксельной линии будем путем увеличения на единицу старшего регистра в регистровой паре.

В основной программе необходимо командами «ld de, 0207h or 08D8h; call 0f82dH» включить экран в нижние 64 кб, графика с #8000, цвет с #4000.

В переменной «scr_step» будем хранить шаг скроллинга в знакоместах. На этапе разбора задачи он будет равен 1 знакоместу, но может иметь любое значение.

Забегая вперёд, скажу, что разница между процедурами скроллирования вверх и вниз минимальная, и заключается в расчете начальных адресов, откуда и куда кидаем данные, а так же в использовании inc/dec при переходе между знакоместами. Поэтому подготовим универсальную процедуру для скроллирования в обоих направлениях, за выбор, которого будет отвечать переменная «scr_move»: 0 – вверх, 1- вниз.

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

   ld a, (scr_step)      ;; a=шаг в знакоместах    add a, a              ;  a=a*2 Так как адрес это 2 байта    ld e, a               ;; de=a ширина смещения в таблице    ld d, 0               ;;

Далее в зависимости от направления скроллинга немного модернизируем код основного цикла и рассчитываем место в таблице адресов знакомест, откуда начнем брать данные.

   ld a, (scr_move)    and a                  ; if a=0    jr z, Scroll.ldi.cr.up ; then движемся вверх       ld a, 2bh           ; else движемся вниз       ld (Scroll.ldi.cr.IX+1), a ; для скроллинга в вниз       ld (Scroll.ldi.cr.IX+3), a ;  dec ix; dec ix       ld (Scroll.ldi.cr.IY+1), a ;; dec iy; dec iy       ld (Scroll.ldi.cr.IY+3), a ;;       ld hl, AdrZNGr_end-2 ; Конец списка адресов знакомест куда       push hl       sbc hl, de         ; hl=AdrZN_end-de Смещаемся в таблице на откуда       jr Scroll.ldi.cr.go1 Scroll.ldi.cr.up:       ld a, 23h             ; настройка подпрограммы       ld (Scroll.ldi.cr.IX+1), a    ; для скроллинга в верх       ld (Scroll.ldi.cr.IX+3), a    ;  inc ix; inc ix       ld (Scroll.ldi.cr.IY+1), a    ;; inc iy; inc iy       ld (Scroll.ldi.cr.IY+3), a    ;;       ld hl, AdrZNGr        ; Начало списка адресов знакомест куда       push hl       add hl, de            ; hl=AdrZN+de Смещаемся в таблице на откуда Scroll.ldi.cr.go1:       .ld ix, hl            ; ix=адрес в таблице, откуда берем данные

Здесь AdrZNGr, AdrZNGr_end – начало и конец таблицы адресов знакомест.

В последней строчке впервые встречается макрос «.ld» (смотри приложение), он копирует содержание одной регистровой пары в другую методом push/pop. В данном случае мы копируем содержание регистровой пары HL в пару IX.

Теперь нам нужно рассчитать общее число перемещаемых знакомест.

   ld a, (scr_step)      ; a=шаг скроллинга в знакоместах    ld b, a               ;;    ld a, 30              ;;a=30-scr_step    sub b                 ;;    ld b, a               ; b=Общее число перемещаемых знакомест    pop iy                ; iy=адрес в таблице, куда кидаем данные

После всех действий имеем в IX=адрес в таблице, откуда берем данные, IY=адрес в таблице, куда кидаем данные, а B=общее число перемещаемых знакомест.

Основное тело подпрограммы состоит из двух вложенных циклов: по знакоместам и по пиксельным линиям.

Цикл по знакоместам выглядит так:

Scroll.ldi.cr.x:         ; Цикл по знакоместам       push bc       ld l, (ix+0)       ;; hl= Адрес откуда данные будут браться       ld h, (ix+1)       ;; Scroll.ldi.cr.IX:       inc ix             ; <!> dec ix       inc ix             ; <!> dec ix       ld e, (iy+0)       ;; de=Адрес строки куда вставляем данные       ld d, (iy+1)       ;; Scroll.ldi.cr.IY:       inc iy             ; <!> dec ix       inc iy             ; <!> dec ix       ld b, 8            ; число пиксельных линий в знакоместе Scroll.ldi.cr.y:                ; Цикл по пиксельным линиям <……> <цикл рассмотрим ниже> <……>    pop bc    dec b    jp nz, Scroll.ldi.cr.x   ; Цикл по знакоместам

Отмечу, что во время подготовительных операций, мы модернизировали именно его, изменяемые строки помечены <!>. В остальном цикл прост и не должен вызвать дополнительных вопросов.

Цикл по пиксельным линиям выглядит так:

   ld b, 8            ; число пиксельных линий в знакоместе  Scroll.ldi.cr.y:      ; Цикл по пиксельным линиям       .push <bc, hl, de>       rept 32          ldi       endm       .pop <de, hl>       .push <hl, de>       res 5, h        ; Перешли на вторую часть экрана       res 5, d        ; Перешли на вторую часть экрана       rept 32          ldi       endm       .pop <de, hl>       .push <hl, de>       ld a, 11000000B ; переходим на цвет       xor h           ;       ld h, a         ;       ld a, 11000000B ; переходим на цвет       xor d           ;       ld d, a         ;       .push <hl, de>       rept 32          ldi       endm       .pop <de, hl>       res 5, h        ; Перешли на вторую часть экрана       res 5, d        ; Перешли на вторую часть экрана       rept 32          ldi       endm       .pop <de, hl, bc>       inc h       inc d    dec b    jp nz, Scroll.ldi.cr.y ; Цикл по пиксельным линиям

Где:

rept – endm это макрокоманда ассемблера M80, которая повторяет строки ограниченные этими командами указанное число раз.

.pop и .push – это макросы, с которыми мы уже знакомы по прошлой статье.

Как видно цикл состоит из 4 частей, первыми двумя (для каждого полуэкрана), мы перебрасываем графическую информацию, следующими двумя — цвет.

Опять же считаю, что тут всё просто и понятно, дополнительных пояснений не требуется.

Теперь разберёмся с третьим методом переброски данных — через стек, с использованием конструкции POP/PUSH. Первоначальная настройка полностью аналогична уже рассмотренной и повторяться не будем. Только отмечу изменение имен меток, где часть «ldi» заменяется на «sp».

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

scroll.sp.cr.x:                 ; Цикл по знакоместам       push bc       ld e, (ix+0)       ;; de=адрес откуда данные будут браться       ld d, (ix+1)       ;; scroll.sp.cr.ix:       inc ix             ; <!> dec ix       inc ix             ; <!> dec ix       ld l, (iy+0)       ;; hl=адрес куда будут вставляться данные       ld h, (iy+1)       ;; scroll.sp.cr.iy:       inc iy             ; <!> dec iy       inc iy             ; <!> dec iy       ld b, 8            ; число пиксельных линий в знакоместе scroll.sp.cr.y:                 ; Цикл по пиксельным линиям <……> <цикл рассмотрим ниже> <……>    pop bc                   ; Счетчик    dec b    jp nz, scroll.sp.cr.x    ; Цикл по знакоместам

Принцип метода основан на том, что команды ассемблера POP/PUSH самые быстрые для процесса чтение-запись данных. За раз считываются/записываются сразу два байта и затрачивается 21 такт, то есть на один байта приходиться 10.5 такта. Но существует несколько проблем.

Первое — для временного хранения данных можно использовать только регистровые пары. У процессора z80 имеется 4 основных регистровых пары и 4 альтернативных. То есть за раз можно перебросить всего 16 байт.

Второе — для переброски используется стек процессора, а он у нас один. Алгоритм работы такой: устанавливаем стек процессора (его адрес содержит регистровая пара SP) на то место, с которого нужно прочитать данные, читаем 16 байт, переносим стек процессора на то место, куда нужно сохранить данные +16 байт (так как стек растет вниз), записываем 16 байт. Повторяем операцию несколько раз. Наилучший результат по времени получаем, при создании огромного «поля», в котором переносим значительные объемы данных. Элемент подобного «поля» выглядит вот так:

   ld sp, 0000            ;     <!> Адрес откуда забираем данные.    .pop <af, bc, de, hl>  ;    exxa                   ;    .pop <af, bc, de, hl>  ;    ld sp, 0000            ;     <!> Адрес куда вставляем данные.    .push <hl, de, bc, af> ;    exxa                   ;    .push <hl, de, bc, af> ;

Здесь использован макрос «exxa» (смотри приложение). Он переключает все регистры на альтернативные.

Выходит, что для переброски 1 байта нужно затратить 13 байт. А «поле» POP/PUSH для переброски всего расширенного экрана «Profi» на одно знакоместо должно занимать 199 680 байт (195 кб), создать такое «поле», по понятным причинам, практически нереально.

К тому же, нам нужно весьма гибко управлять процессом переброски, а значит, мы не сможем создать «поле» POP/PUSH больше чем для переброски одной пиксельной линии.

Тело цикла переброски пиксельных линии состоит из двух частей: настройка «поля» POP/PUSH и собственно самого «поля». В настройке рассчитываем адреса, откуда/куда переносим данные и модернизируем код «поля». Сам цикл выглядит так:

   ld b, 8            ; число пиксельных линий в знакоместе  scroll.sp.cr.y:                 ; Цикл по пиксельным линиям       .push <bc, de, hl>       ld bc, 16       ; число переносимых за раз байт       add hl, bc      ; +00 т.к. вставка в обратном порядке       ld (scroll.sp.cr.01+1), hl ; +00       res 5, h        ;Перешли на вторую   часть экрана       ld (scroll.sp.cr.sp+(3+10)*1+1), hl ; +00       add hl, bc      ; +16       ld (scroll.sp.cr.sp+(3+10)*3+1), hl ; +16       set 5, h        ;Вернулись на первую часть экрана       ld (scroll.sp.cr.sp+(3+10)*5+1), hl ; +16       ld a, 11000000B ; переходим на цвет       xor h           ;       ld h, a         ;       ld (scroll.sp.cr.sp+(3+10)*7+1), hl ; +16       res 5, h        ;Перешли на вторую   часть экрана       ld (scroll.sp.cr.sp+(3+10)*9+1), hl ; +16       sbc hl, bc      ; -16       ld (scroll.sp.cr.sp+(3+10)*11+1), hl ;+00       set 5, h        ;Вернулись на первую часть экрана       ld (scroll.sp.cr.sp+(3+10)*13+1), hl ;+00       ex de, hl       ld (TempSP), sp ; <!!!>       ld sp, hl       ; <!>                 +00       res 5, h        ;Перешли на вторую   часть экрана       ld (scroll.sp.cr.sp+1), hl          ; +00       add hl, bc      ; +16       ld (scroll.sp.cr.sp+(3+10)*2+1), hl ; +16       set 5, h        ;Вернулись на первую часть экрана       ld (scroll.sp.cr.sp+(3+10)*4+1), hl ; +16       ld a, 11000000B ; переходим на цвет       xor h           ;       ld h, a         ;       ld (scroll.sp.cr.sp+(3+10)*6+1), hl ; +16       res 5, h        ;Перешли на вторую   часть экрана       ld (scroll.sp.cr.sp+(3+10)*8+1), hl ; +16       sbc hl, bc      ; -16       ld (scroll.sp.cr.sp+(3+10)*10+1), hl ;+00       set 5, h        ;Вернулись на первую часть экрана       ld (scroll.sp.cr.sp+(3+10)*12+1), hl ;+00       .pop <af, bc, de, hl>  ;       exxa                   ;       .pop <af, bc, de, hl>  ; scroll.sp.cr.01: ld sp, 0000           ;    <!>       .push <hl, de, bc, af> ;       exxa                   ;       .push <hl, de, bc, af> ; scroll.sp.cr.sp:       rept 7       ld sp, 0000            ; <!>       .pop <af, bc, de, hl>  ;       exxa                   ;       .pop <af, bc, de, hl>  ;       ld sp, 0000            ; <!>       .push <hl, de, bc, af> ;       exxa                   ;       .push <hl, de, bc, af> ;       endm       ld sp, (TempSP)    ; <!!!>       .pop <hl, de, bc>       inc h       inc d    dec b    jp nz, scroll.sp.cr.y ; Цикл по пиксельным линиям

Как видно, тело цикла несколько больше, чем при использовании команды ассемблера LDI. Не смотря на это, его логика, думаю понятна.

В заключении давайте проведем сравнительный анализ обоих вариантов. Для чего подготовим таблицу 1. Не смотря на то, что в статье приведен материал для скроллинга цветного изображения, в таблице приведу так же данные для скроллирования только графики (черно белого изображения). Считаю, что упростить приведенные примеры не составит труда.

Таблица 1.

Таблица 1.

Как видно из таблицы, метод с использованием команды ассемблера LDI незначительно короче, но проигрывает по скорости методу переброски данных через стек процессора. Причём, при скроллировании с цветом, весьма существенно.

Отмечу, что при использовании переброски данных через стек процессора требует отключения прерываний, иначе после прихода сигнала прерывания в стек запишется адрес возврата, что испортит данные. Это несколько сужает сферу применения данного метода.

Для стандартной частоты процессора z80, в 3.5Мгц, цифры кажутся огромными. Но не стоит забывать две вещи. Первое, расширенный экран «Profi» занимает 32 кб, против 6,7 кб стандартного экрана Спектрума. И второе, все модели компьютера «Profi» оснащены турбо режимом, значительно повышающем тактовую частоту процессора.

В зависимости от ряда факторов и версии схем, существуют машины со следующими частотами работы процессора в турбо режимом: 7Мгц, 10Мгц, 12Мгц, 15Мгц, 21 Мгц. В настоящее время наиболее часто встречаются частоты 10Мгц и 12Мгц. Их и следует брать как базовые.

Таблица 2. Число тактов между прерываниями при разных скоростях работы процессора

Скорость процессора, Мгц

Число тактов между прерываниями

3,5

69888

7

99880

10

149760

12

179712

15

224640

Если немного покривить против истины и распространить полученные данные по скорости работы процедур для расширенного экрана «Profi» на стандартный экран Спектрума, то для сравнительного анализа можно составить следующие таблицы.

Таблица 3. Число необходимых прерывания для скроллинга экрана на 1 знакоместо при разных частотах работы процессора.

Стандартный экран Спектрума 256х192

Стандартный экран Спектрума 256х192
Расширенный экран 512х240

Расширенный экран 512х240

Как видно на скорости процессора в 12Мгц, соотношение объема переносимых данных к числу тактов в прерывании у расширенного экрана «Profi» практически идентично этому показателю для стандартного экрана Спектрума при скорости процессора 3.5 Мгц. Скроллинг расширенного экрана с цветом всегда будет медленнее, так как объем данных о цвете в нем в 8 раз больше чем у стандартного экрана.

Все это говорит о том, что на компьютере «Profi», под его расширенный экран можно создавать столь же динамичные программы и игры как и для стандартного экрана Спектрума. Конечно же с учетом того что, сам компьютер «Profi» отличается от стандартного Спектрума не только расширенным экраном.

На этом пока стоит остановиться. Мы всесторонне рассмотрели поставленную задачу.

Если же у кого остались вопросы он всегда может обратиться по одному из следующих адресов.

  • FidoNet: Tarasow Aleksey 2:5053/57

  • E-mail: tae1980(очень злая собака)yandex.ru

А на форуме, в группе ВК есть много интересного, а все новое будет выкладываться, в том числе и там.

По этой ссылке или этой дублирующей можно скачать архив lzh, в котором находятся:

  • m80.com, l80.com  — Основные файлы пакета m80.

  • SCROLL.ASM – исподники кода, описанного в этом статье.

  • BIOSK.INC – файл для подзагрузки используемых макросов.

  • SCRLDI.ASM, SCRSTACK.ASM – программы для тестирования рассмотренных в статье алгоритмов скроллинга. На основе, рассмотренной в предыдущей статье программы загрузки картинки на экран из файлов GRF.

  • SCRLDI.BAT, SCRSTACK.BAT – командные файлы для ассемблирования примеров.

  • SCRLDI.COM, SCRSTACK.COM —  уже с ассемблированный код, готовый для запуска. Запускать из командной строки с указанием в качестве первого аргумента имени файла GRF для вывода на экран.

  • Группа черно-белых и цветных GRF файлов для теста.

Так же можно скачать загрузочный образ диска в формате «*.pro» (дублирующая ссылка). Для запуска программ SCRLDI.COM, SCRSTACK.COM нужно: в командной строке (вход по клавише «7») набрать их название (без «.COM») и ввести полное имя картинки. Для удобства можно использовать сочетание клавиш «Shift+J» — вставка в командную строку имени файла под курсором. По умолчанию в HC при выборе файлов «GRF» будет запускаться программа SCRSTACK.


Приложение. Используемые макросы

.Pop   Macro   Items ; (c) PSWsoft            Irp x,<Items>              POP x           Endm        EndM ;-----------------------------------------------------------  .Push  Macro   Items ; (c) PSWsoft            Irp x,<Items>              PUSH x           Endm        EndM ;-----------------------------------------------------------  .Ld    Macro   Dest, Sour ; (c) PSWsoft            push Sour           pop Dest        EndM ;-----------------------------------------------------------  exxa   Macro ; (c) PSWsoft            exx           ex af, af        EndM ;-----------------------------------------------------------

Приложение. Исходный код из файла SCROLL.ASM.

Scroll.stack: ;Скроллинг всего экрана (независимо от наличия цвета) вверх или вниз на заданное число знакомест. ;Алгоритм pop/push. ;Портит: все регистры, включая альтернативные. ;Использует: FColor, scr_step, scr_move, AdrZNgr, AdrZNgr_end           ld a, (scr_step)      ;; a=шаг в знакоместах           add a, a              ;  a=a*2 Так как адрес это 2 байта           ld e, a               ;; de=a ширина смещения в таблице           ld d, 0               ;;           ;           ld a, (FColor)         ; a=наличие цвета (0 - чб, 1-стандартные цвета, 2-палитра)           and a                  ;; if a<>0           jp nz, Scroll.stack.cr ;; then выводим цветную картинку           ;           ld a, (scr_move)           and a                 ;; if a=0           jr z, scroll.sp.bw.up ;; then движемся вверх              ld a, 2bh          ;  else движемся вниз              ld (scroll.sp.bw.IX+1), a ; для скроллинга в вниз              ld (scroll.sp.bw.IX+3), a ;  dec ix; dec ix              ld (scroll.sp.bw.IY+1), a ;; dec iy; dec iy              ld (scroll.sp.bw.IY+3), a ;;              ld hl, AdrZNGr_end-2 ;              push hl            ; для передачи в iy              sbc hl, de         ; hl=AdrZNgr_end-de Смещаемся в таблице на от куда              jr scroll.sp.bw.go1 scroll.sp.bw.up:           ld a, 23h             ; настройка подпрограммы           ld (scroll.sp.bw.IX+1), a ; для скроллинга в верх           ld (scroll.sp.bw.IX+3), a ;  inc ix; inc ix           ld (scroll.sp.bw.IY+1), a ;; inc iy; inc iy           ld (scroll.sp.bw.IY+3), a ;;           ld hl, AdrZNGr        ; Начало списка адресов пикс.линий куда           push hl               ; для передачи в iy           add hl, de            ; hl=AdrZNgr+de Смещаемся в таблице на от куда scroll.sp.bw.go1:           .ld ix, hl            ; ix=адрес в таблице от куда берем данные           ld a, (scr_step)      ; a=шаг скроллинга в знакоместах           ld b, a               ;;           ld a, 30              ;;a=30-scr_step           sub b                 ;;           ld b, a               ; b=Общее число перемещаемых знакомест           pop iy                ; iy=адрес в таблице куда кидаем данные scroll.sp.bw.x:                 ; Цикл по знакоместам              push bc              ld e, (ix+0)       ;; откуда данные будут браться              ld d, (ix+1)       ;; scroll.sp.bw.ix:              inc ix             ; <!> dec ix              inc ix             ; <!> dec ix              ld l, (iy+0)       ;; куда будут вставляться данные              ld h, (iy+1)       ;; scroll.sp.bw.iy:              inc iy             ; <!> dec iy              inc iy             ; <!> dec iy              ld b, 8            ; число пиксельных линий в знакоместе scroll.sp.bw.y:                 ; Цикл по пиксельным линиям                 .push <bc, de, hl>                 ld bc, 16       ; число переносимых за раз байт                 add hl, bc      ; +00 т.к. вставка в обратном порядке                 ld (scroll.sp.bw.01+1), hl ; +00                 res 5, h        ;Перешли на вторую часть экрана                 ld (scroll.sp.bw.sp+(3+10)*1+1), hl ; +00                 add hl, bc      ; +16                 ld (scroll.sp.bw.sp+(3+10)*3+1), hl ; +16                 set 5, h        ;Вернулись на первую часть экрана                 ld (scroll.sp.bw.sp+(3+10)*5+1), hl ; +16                 ex de, hl                 ld (TempSP), sp ; <!!!>                 ld sp, hl              ;  +00 <!>                     +00                 res 5, h        ;Перешли на вторую часть экрана                 ld (scroll.sp.bw.sp+1), hl ; +00                 add hl, bc      ; +16                 ld (scroll.sp.bw.sp+(3+10)*2+1), hl ; +16                 set 5, h        ;Вернулись на первую часть экрана                 ld (scroll.sp.bw.sp+(3+10)*4+1), hl ; +16                 .pop <af, bc, de, hl>  ;         забрали  полуэкран 1 +00                 exxa                   ;                 .pop <af, bc, de, hl>  ; scroll.sp.bw.01: ld sp, 0000           ;     <!> вставили полуэкран 1 +00                 .push <hl, de, bc, af> ;                 exxa                   ;                 .push <hl, de, bc, af> ; scroll.sp.bw.sp:                 rept 3                    ld sp, 0000            ; <!>                    .pop <af, bc, de, hl>  ;                    exxa                   ;                    .pop <af, bc, de, hl>  ;                    ld sp, 0000            ; <!>                    .push <hl, de, bc, af> ;                    exxa                   ;                    .push <hl, de, bc, af> ;                 endm                 ld sp, (TempSP)                 .pop <hl, de, bc>                 inc h                 inc d              dec b              jp nz, scroll.sp.bw.y ; Цикл по пиксельным линиям           pop bc                   ; Счетчик           dec b           jp nz, scroll.sp.bw.x    ; Цикл по знакоместам           ret ;--------------------------------------------------------------  Scroll.stack.cr: ;Скроллинг всего экрана с цветом вверх или вниз на заданное число знакомест. ;Алгоритм pop/push. ;На выходе: de=ширина смещения в таблице ;Портит: все регистры, включая альтернативные. ;Использует: scr_move, AdrZNgr, AdrZNgr_end           ld a, (scr_move)           and a                 ;; if a=0           jr z, scroll.sp.cr.up ;; then движемся вверх              ld a, 2bh          ;  else движемся вниз              ld (scroll.sp.cr.IX+1), a ; для скроллинга в вниз              ld (scroll.sp.cr.IX+3), a ;  dec ix; dec ix              ld (scroll.sp.cr.IY+1), a ;; dec iy; dec iy              ld (scroll.sp.cr.IY+3), a ;;              ld hl, AdrZNGr_end-2 ;              push hl            ; для передачи в iy              sbc hl, de         ; hl=AdrZNgr_end-de Смещаемся в таблице на от куда              jr scroll.sp.cr.go1 scroll.sp.cr.up:           ld a, 23h             ; настройка подпрограммы           ld (scroll.sp.cr.IX+1), a ; для скроллинга в верх           ld (scroll.sp.cr.IX+3), a ;  inc ix; inc ix           ld (scroll.sp.cr.IY+1), a ;; inc iy; inc iy           ld (scroll.sp.cr.IY+3), a ;;           ld hl, AdrZNGr        ; Начало списка адресов пикс.линий куда           push hl               ; для передачи в iy           add hl, de            ; hl=AdrZNgr+de Смещаемся в таблице на от куда scroll.sp.cr.go1:           .ld ix, hl            ; ix=адрес в таблице от куда берем данные           ld a, (scr_step)      ; a=шаг скроллинга в знакоместах           ld b, a               ;;           ld a, 30              ;;a=30-scr_step           sub b                 ;;           ld b, a               ; b=Общее число перемещаемых знакомест           pop iy                ; iy=адрес в таблице куда кидаем данные scroll.sp.cr.x:                 ; Цикл по знакоместам              push bc              ld e, (ix+0)       ;; откуда данные будут браться              ld d, (ix+1)       ;; scroll.sp.cr.ix:              inc ix             ; <!> dec ix              inc ix             ; <!> dec ix              ld l, (iy+0)       ;; куда будут вставляться данные              ld h, (iy+1)       ;; scroll.sp.cr.iy:              inc iy             ; <!> dec iy              inc iy             ; <!> dec iy              ld b, 8            ; число пиксельных линий в знакоместе scroll.sp.cr.y:                 ; Цикл по пиксельным линиям                 .push <bc, de, hl>                 ld bc, 16       ; число переносимых за раз байт                 add hl, bc      ; +00 т.к. вставка в обратном порядке                 ld (scroll.sp.cr.01+1), hl ; +00                 res 5, h        ;Перешли на вторую   часть экрана                 ld (scroll.sp.cr.sp+(3+10)*1+1), hl ; +00                 add hl, bc      ; +16                 ld (scroll.sp.cr.sp+(3+10)*3+1), hl ; +16                 set 5, h        ;Вернулись на первую часть экрана                 ld (scroll.sp.cr.sp+(3+10)*5+1), hl ; +16                 ld a, 11000000B ; переходим на цвет                 xor h           ;                 ld h, a         ;                 ld (scroll.sp.cr.sp+(3+10)*7+1), hl ; +16                 res 5, h        ;Перешли на вторую   часть экрана                 ld (scroll.sp.cr.sp+(3+10)*9+1), hl ; +16                 sbc hl, bc      ; -16                 ld (scroll.sp.cr.sp+(3+10)*11+1), hl ;+00                 set 5, h        ;Вернулись на первую часть экрана                 ld (scroll.sp.cr.sp+(3+10)*13+1), hl ;+00                 ex de, hl                 ld (TempSP), sp ; <!!!>                 ld sp, hl       ; <!>                 +00                 res 5, h        ;Перешли на вторую   часть экрана                 ld (scroll.sp.cr.sp+1), hl          ; +00                 add hl, bc      ; +16                 ld (scroll.sp.cr.sp+(3+10)*2+1), hl ; +16                 set 5, h        ;Вернулись на первую часть экрана                 ld (scroll.sp.cr.sp+(3+10)*4+1), hl ; +16                 ld a, 11000000B ; переходим на цвет                 xor h           ;                 ld h, a         ;                 ld (scroll.sp.cr.sp+(3+10)*6+1), hl ; +16                 res 5, h        ;Перешли на вторую   часть экрана                 ld (scroll.sp.cr.sp+(3+10)*8+1), hl ; +16                 sbc hl, bc      ; -16                 ld (scroll.sp.cr.sp+(3+10)*10+1), hl ;+00                 set 5, h        ;Вернулись на первую часть экрана                 ld (scroll.sp.cr.sp+(3+10)*12+1), hl ;+00                 .pop <af, bc, de, hl>  ;                 exxa                   ;                 .pop <af, bc, de, hl>  ; scroll.sp.cr.01: ld sp, 0000           ;    <!>                 .push <hl, de, bc, af> ;                 exxa                   ;                 .push <hl, de, bc, af> ; scroll.sp.cr.sp:                 rept 7                    ld sp, 0000            ; <!>                    .pop <af, bc, de, hl>  ;                    exxa                   ;                    .pop <af, bc, de, hl>  ;                    ld sp, 0000            ; <!>                    .push <hl, de, bc, af> ;                    exxa                   ;                    .push <hl, de, bc, af> ;                 endm                 ld sp, (TempSP)    ; <!!!>                 .pop <hl, de, bc>                 inc h                 inc d              dec b              jp nz, scroll.sp.cr.y ; Цикл по пиксельным линиям           pop bc                   ; Счетчик           dec b           jp nz, scroll.sp.cr.x    ; Цикл по знакоместам           ret ;--------------------------------------------------------------  Scroll.ldi: ;Скроллинг всего экрана (независимо от наличия цвета) вверх или вниз на заданное число знакомест. ;Портит: a, hl, de, bc, ix, iy ;Использует: FColor, scr_step, scr_move, AdrZN, AdrZN_end           ld a, (scr_step)      ;; a=шаг в знакоместах           add a, a              ;  a=a*2 Так как адрес это 2 байта           ld e, a               ;; de=a ширина смещения в таблице           ld d, 0               ;;           ;           ld a, (FColor)        ; a=наличие цвета (0 - чб, 1-стандартные цвета, 2-палитра)           and a                 ;; if a<>0           jp nz, Scroll.ldi.cr  ;; then выводим цветную картинку           ;           ld a, (scr_move)           and a                  ; if a=0           jr z, Scroll.ldi.bw.up ; then движемся вверх              ld a, 2bh           ; else движемся вниз              ld (Scroll.ldi.bw.IX+1), a ; для скроллинга в вниз              ld (Scroll.ldi.bw.IX+3), a ;  dec ix; dec ix              ld (Scroll.ldi.bw.IY+1), a ;; dec iy; dec iy              ld (Scroll.ldi.bw.IY+3), a ;;              ld hl, AdrZNGr_end-2    ; Конец списка адресов знакомест куда              push hl              sbc hl, de         ; hl=AdrZN_end-de Смещаемся в таблице на от куда              jr Scroll.ldi.bw.go1 Scroll.ldi.bw.up:           ld a, 23h             ; настройка подпрограммы           ld (Scroll.ldi.bw.IX+1), a    ; для скроллинга в верх           ld (Scroll.ldi.bw.IX+3), a    ;  inc ix; inc ix           ld (Scroll.ldi.bw.IY+1), a    ;; inc iy; inc iy           ld (Scroll.ldi.bw.IY+3), a    ;;           ld hl, AdrZNGr        ; Начало списка адресов знакомест куда           push hl           add hl, de            ; hl=AdrZN+de Смещаемся в таблице на от куда Scroll.ldi.bw.go1:           .ld ix, hl            ; ix=адрес в таблице от куда берем данные           ld a, (scr_step)      ; a=шаг скроллинга в знакоместах           ld b, a               ;;           ld a, 30              ;;a=30-scr_step           sub b                 ;;           ld b, a               ; b=Общее число перемещаемых знакомест           pop iy                ; iy=адрес в таблице куда кидаем данные Scroll.ldi.bw.x:                ; Цикл по знакоместам              push bc              ld l, (ix+0)       ;; откуда данные будут браться              ld h, (ix+1)       ;; Scroll.ldi.bw.IX:              inc ix             ; <!> dec ix              inc ix             ; <!> dec ix              ld e, (iy+0)       ;; hl=Адрес строки откуда берем данные              ld d, (iy+1)       ;; Scroll.ldi.bw.IY:              inc iy             ; <!> dec ix              inc iy             ; <!> dec ix              ld b, 8            ; число пиксельных линий в знакоместе Scroll.ldi.bw.y:                ; Цикл по пиксельным линиям                 .push <bc, hl, de>                 rept 32                    ldi                 endm                 .pop <de, hl>                 .push <hl, de>                 res 5, h        ; Перешли на вторую часть экрана                 res 5, d        ; Перешли на вторую часть экрана                 rept 32                    ldi                 endm                 .pop <de, hl, bc>                 inc h                 inc d              dec b              jp nz, Scroll.ldi.bw.y ; Цикл по пиксельным линиям           pop bc           dec b           jp nz, Scroll.ldi.bw.x   ; Цикл по знакоместам           ret ;--------------------------------------------------------------  Scroll.ldi.cr: ;Скроллинг всего экрана с цветом вверх или вниз на заданное число знакомест. ;На выходе: de=ширина смещения в таблице ;Портит: a, hl, de, bc, ix, iy ;Использует: scr_move, AdrZN, AdrZN_end           ld a, (scr_move)           and a                  ; if a=0           jr z, Scroll.ldi.cr.up ; then движемся вверх              ld a, 2bh           ; else движемся вниз              ld (Scroll.ldi.cr.IX+1), a ; для скроллинга в вниз              ld (Scroll.ldi.cr.IX+3), a ;  dec ix; dec ix              ld (Scroll.ldi.cr.IY+1), a ;; dec iy; dec iy              ld (Scroll.ldi.cr.IY+3), a ;;              ld hl, AdrZNGr_end-2    ; Конец списка адресов знакомест куда              push hl              sbc hl, de         ; hl=AdrZN_end-de Смещаемся в таблице на от куда              jr Scroll.ldi.cr.go1 Scroll.ldi.cr.up:           ld a, 23h             ; настройка подпрограммы           ld (Scroll.ldi.cr.IX+1), a    ; для скроллинга в верх           ld (Scroll.ldi.cr.IX+3), a    ;  inc ix; inc ix           ld (Scroll.ldi.cr.IY+1), a    ;; inc iy; inc iy           ld (Scroll.ldi.cr.IY+3), a    ;;           ld hl, AdrZNGr        ; Начало списка адресов знакомест куда           push hl           add hl, de            ; hl=AdrZN+de Смещаемся в таблице на от куда Scroll.ldi.cr.go1:           .ld ix, hl            ; ix=адрес в таблице от куда берем данные           ld a, (scr_step)      ; a=шаг скроллинга в знакоместах           ld b, a               ;;           ld a, 30              ;;a=30-scr_step           sub b                 ;;           ld b, a               ; b=Общее число перемещаемых знакомест           pop iy                ; iy=адрес в таблице куда кидаем данные Scroll.ldi.cr.x:                ; Цикл по знакоместам              push bc              ld l, (ix+0)       ;; откуда данные будут браться              ld h, (ix+1)       ;; Scroll.ldi.cr.IX:              inc ix             ; <!> dec ix              inc ix             ; <!> dec ix              ld e, (iy+0)       ;; hl=Адрес строки откуда берем данные              ld d, (iy+1)       ;; Scroll.ldi.cr.IY:              inc iy             ; <!> dec ix              inc iy             ; <!> dec ix              ld b, 8            ; число пиксельных линий в знакоместе Scroll.ldi.cr.y:                ; Цикл по пиксельным линиям                 .push <bc, hl, de>                 rept 32                    ldi                 endm                 .pop <de, hl>                 .push <hl, de>                 res 5, h        ; Перешли на вторую часть экрана                 res 5, d        ; Перешли на вторую часть экрана                 rept 32                    ldi                 endm                 .pop <de, hl>                 .push <hl, de>                 ld a, 11000000B ; переходим на цвет                 xor h           ;                 ld h, a         ;                 ld a, 11000000B ; переходим на цвет                 xor d           ;                 ld d, a         ;                 .push <hl, de>                 rept 32                    ldi                 endm                 .pop <de, hl>                 res 5, h        ; Перешли на вторую часть экрана                 res 5, d        ; Перешли на вторую часть экрана                 rept 32                    ldi                 endm                 .pop <de, hl, bc>                 inc h                 inc d              dec b              jp nz, Scroll.ldi.cr.y ; Цикл по пиксельным линиям           pop bc           dec b           jp nz, Scroll.ldi.cr.x   ; Цикл по знакоместам           ret ;--------------------------------------------------------------  scr_step: db 01       ; Шаг скроллирования в знакоместах. scr_move: db 00       ; Направление скроллирования. 0 - вверх, 1 - вниз. TempSP:   dw 0000     ; Место для запоминания адреса программного стема.  ;--------------------------------------------------------------


ссылка на оригинал статьи https://habr.com/ru/articles/868528/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *