Продолжаем цикла статей по работе с расширенным экраном компьютера «Profi». Второй практикум.
В предыдущих статьях была описана структура расширенного экрана компьютера «Profi», работа с файлами в OS CP/M, а так же загрузка и вывод картинки из файла на экран. Теперь давайте рассмотрим методы работы с расширенным экраном и попробуем проскроллировать его.
Статья была опубликована в 2023 году в 44 номере журнала по «Downgrade».
Статья написана в соавторстве с Вадимом Чертковым.
В журнале «3aRulem» выходил цикл наших статей посвященных компьютеру «Profi». В рамках этого материала освящались разные аспекты работы с этой машиной. В том числе есть материал по расширенному экрану компьютера «Profi», в котором предоставлялась подробная информация о его строении, а так же началась публикация практикума по работе с ним. К сожалению, редакция журнала «3aRulem» временно прекратила работу над подготовкой новых номеров, сосредоточилась на иных своих проектах. По согласованию со всеми сторонами, публикацию цикла статье было решено перенести в этот журнал. В тех местах, где это необходимо будут даваться ссылки на номера журнала «3aRulem». И так начнём наш практикум…
Скроллинг экрана — одна из самых распространенных операций с ним. Давайте рассмотрим вертикальный скроллинг расширенного экрана, а горизонтальный скроллинг разберем в одной из следующих статей.
Скроллинг всего экрана представляет собой обычную переброску данных из одного места в другое, только в экранной памяти. Реализовать этот процесс можно следующими методами:
-
Побайтная переброска через регистр A. Самый медленный способ, но позволяющий работать со сложными структурами данных. Мы использовали его при выводе на расширенный экран картинки из файла формата GRF.
-
Переброска данных с использованием команды LDI. Один из самых быстрых методов переброски. В идеальном случае на переброску одного байта тратится 16 тактов.
-
Переброска через стек, с использованием конструкции 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. Не смотря на то, что в статье приведен материал для скроллинга цветного изображения, в таблице приведу так же данные для скроллирования только графики (черно белого изображения). Считаю, что упростить приведенные примеры не составит труда.
Как видно из таблицы, метод с использованием команды ассемблера 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 знакоместо при разных частотах работы процессора.
Как видно на скорости процессора в 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/
Добавить комментарий