ZX Forum #4 Из журнала ZX - FORUM.

Новые "40 лучших процедур"


Цвет
Насыщенность
Размер

(c) Колотов Сергей, г.Шадринск,
SerzhSoft, июль, 1997.

   НОВЫЕ "40 ЛУЧШИХ ПРОЦЕДУР"

   В  1992  году  на   страницах
ZX-РЕВЮ был  опубликован  сокра-
щенный перевод книги  Дж.Хардма-
на и Э.Хьюзон  "40 лучших проце-
дур". Публикация  вызвала  самые
восторженные отклики  читателей,
ведь многие  начинающие  синкле-
ристы именно с ее помощью  нако-
нец-то смогли  преодолеть  труд-
ности "барьера машинного  кода".
Но вот проходит ровно пять  лет,
и старожилы ZX РЕВЮ с удивлением
замечают в первом номере за 1997 
год до боли знакомое название.
   Все  многочисленные   доводы,
так убедительно  изложенные  ИН-
ФОРКОМОМ, конечно же, во  многом 
правильны. Но на мой  (да  и  не
только на мой) взгляд, они так и
не дают права издателям народно-
го журнала  тратить  драгоценные
страницы ZX-РЕВЮ на  печать  уже 
опубликованного в свое время ма-
териала.
   Вместо рубрики "РЕТРО" гораз-
до полезнее и актуальнее было бы
ввести раздел "РЕМЕЙК". А в  нем
печатать  не  просто  старые ма-
териалы, а рассматривать их  бо-
лее  глубоко, приводить  примеры
более  эффективной    реализации
программ и отдельных процедур.
   Взять хотя бы те же  "40 про-
цедур". Да, данная работа  очень
полезна для начинающих.  Но  уже
чуть  более опытные  знатоки ас-
семблера сразу заметят, что  чем
больше  по обьему приводимая ав-
торами процедура, тем  неизменно
она имеет и больше  недостатков,
более  неэффективно реализована,
имеет большее  количество  "лиш-
них"  команд...   Да,  некоторые
"лучшие  процедуры"  сокращаются
более чем вдвое!  И еще: коммен-
тарии к  программам  расположены
за самими листингами, что  очень
затрудняет   понимание  "а   что
именно происходит  при  выполне-
нии  процедуры  на  определенном
этапе". Конечно,  дано  описание
типа "регистр C копируется в ре-
гистр B", но  вот  та  смысловая
нагрузка, которая за этим стоит,
понятна далеко не всегда...
   Далее будет приведена библио-
течка, в  которую  интегрированы
графические   процедуры,  заново
переписанные по  принципу "соро-
ка лучших". Объем занимаемой па-
мяти уменьшился с 1444  байт  до
861 байта!  Каждая процедура де-
тально прокомментирована в самом
ее листинге, так  сказать, - "не
отходя от кассы".

   см. Листинг и шестнадцатерич-
ный дамп.

   Многие процедуры в своей  ра-
боте требуют  некоторые  заранее
определенные значения - констан-
ты.  Под  эти  значения  выделе-
на специальная  область  памяти,
адресуемая меткой CONSTS. В дан-
ном случае CONSTS  указывает  на
адрес  23296,  но,  конечно  же,
этот адрес можно изменить на лю-
бой другой. Длина  области  кон-
стант 8 байт. При выполнении лю-
бой из процедур ни одна из  кон-
стант  не  изменяется.  А  иначе
пришлось бы их называть перемен-
ными...
   В  процедурах, манипулирующих
с координатами точек на  экране,
остчет идет в отличие от  бейси-
ка не снизу-вверх, а  наоборот -
сверху-вниз.  Такой отсчет коор-
динат гораздо удобнее и  исполь-
зуется  во многих других компью-
терах.  Теперь  можно  указывать 
координату Y от 0 до 191 (вместо  
175),  т.е.  появляется  возмож-
ность указывать  координаты  тех 
точек на экране, которые находя-
тся в двух нижних  строчках, от-
водимых под сообщения  об  ошиб-
ках. При отсчете же снизу-вверх,
максимальной Y-координатой явля-
ется  Y=175, а  до  нижних  двух
строк вообще не добраться.

   КРАТКОЕ  ОПИСАНИЕ ПРОЦЕДУР

   1.  ASRL_LF - процедура сдви-
гает весь экран атрибутов  (цве-
тов) влево. Правая  колонка  за-
полняется атрибутом из ячейки по
адресу  CONSTS  (23296).   Длина
процедуры  22  байта  (было - 23
байта). Оттранслирована под  ад-
рес 62000 (HEX: #F230).

   2.  ASRL_RG  -  сдвиг   всего
экрана атрибутов  вправо.  Левая
колонка заполняется атрибутом из
CONSTS. Длина процедуры  21 байт
(19 байт).  Располагается по ад-
ресу 62022 (#F246).
   3.  ASRL_UP  -  сдвиг   всего
экрана атрибутов  вверх.  Нижняя
строка атрибутов заполняется  из
CONSTS. Длина процедуры  19 байт
(21 байт). Адрес: 62043 (#F25B).
   4.  ASRL_DN  -  сдвиг   всего
экрана атрибутов  вниз.  Верхняя
строка заполняется атрибутом  из
CONSTS.   Длина:  20  байт   (21
байт). Адрес: 62062 (#F26E).

   5.  SSRL_LF  -  сдвиг   всего
экрана  влево  на  один   символ
(графика). Правая колонка знако-
мест очищается.  Длина  20  байт
(21 байт). Адрес: 62082 (#F282).
   6.  SSRL_RG  -  сдвиг   всего
экрана вправо  на  один  символ.
Левая колонка  знакомест  очища-
ется.  Длина 19 байт (22 байта).
Адрес: 62102 (#F296).
   7.  SSRL_UP  -  сдвиг   всего
экрана  вверх  на  один  символ.
Нижняя строка  знакомест  очища-
ется.  Длина  55 байт (68 байт).
Адрес: 62121 (#F2A9).

   8.  SSRL_DN  -  сдвиг   всего
экрана  вниз  на  один   символ.
Верхняя  строка  знакомест  очи-
щается.  Длина 55 байт  (73 бай-
та). Адрес: 62176 (#F2E0).
   9.  PSRL_LF  -  сдвиг   всего
экрана  влево  на  один   пиксел
(графика). Правая колонка пиксе-
лей очищается. Длина 16 байт (17
байт). Адрес: 62231 (#F317).
   10. PSRL_RG  -  сдвиг   всего
экрана вправо  на  один  пиксел.
Левая  колонка  пикселей  очища-
ется.  Длина 17 байт  (17 байт).
Адрес: 62247 (#F327).

   11. PSRL_UP  -  сдвиг   всего
экрана вверх на одну линию  пик-
селей.  Нижняя  строка  пикселей
очищается.  Длина  38  байт  (91
байт). Адрес: 62264 (#F338).
   12. PSRL_DN  -  сдвиг   всего
экрана вниз на одну линию пиксе-
лей.  Верхняя  строка   пикселей
очищается.  Длина  38  байт  (90
байт). Адрес: 62302 (#F35E).
   13. SCR_MRG  -  слияние  двух
картинок  (графика, по  принципу
ИЛИ). Двухбайтная  константа  по
адресу CONSTS  должна  содержать
адрес размещения второй  картин-
ки в памяти (накладываемой). Ре-
зультат  помещается  на   экран.
Длина процедуры 17 байт  (раньше
- 21  байт).  Адрес  размещения:
62340 (#F384).

   14. SCR_INV  - инвертирование
экрана   (графика,  по  принципу
НЕ).  Все  пикселы  меняют  свое
значение   на   противоположное.
Длина процедуры 12 байт  (было -
18 байт). Адрес: 62357 (#F395).
   15. SINV_UD   -  инвертирова-
ние символа  вертикально.  Стре-
лка, направленная  вверх, стано-
вится направленной вниз, и  нао-
борот. В двухбайтной  переменной
по адресу CONSTS  должен  содер-
жаться адрес изменяемого  симво-
ла. Длина процедуры 20 байт (так
и было). Адрес размещения: 62369
(#F3A1).
   16. SINV_LR   -  инвертирова-
ние символа горизонтально. Стре-
лка, направленная  влево, стано-
вится направленной вправо, и на-
оборот. В двухбайтной переменной 
по адресу  CONSTS  должен содер-
жаться  адрес изменяемого симво-
ла. Длина процедуры 17 байт (бы-
ло - 19 байт). Адрес размещения: 
62389 (#F3B5).

   17. SROTATE  -  вращение сим-
вола  по часовой стрелке  на  90
градусов. В двухбайтной перемен-
ной по адресу CONSTS должен  со-
держаться адрес изменяемого сим-
вола. Длина 26 байт (раньше - 42
байта). Адрес: 62406 (#F3C6).
   18. ACHANGE - изменение  зна-
чения  атрибутов  всех  символов
экрана.  Побитная  операция.   В
ячейке по адресу  CONSTS  должна
содержаться маска битов: те  би-
ты, которые установлены в маске,
остаются в атрибутах прежними, а
биты, которые в маске имеют  ну-
левое значение - будут иметь ну-
левое значение и в атрибутах (о-
перация И (не "Ы"!)).  В  ячейке
по адресу CONSTS+1 должен  нахо-
диться байт, включенные биты ко-
торого будут введены во все  ат-
рибуты экрана, т.е. если  в этом
байте какой-либо бит включен, то
он будет установлен  и  во  всех
атрибутах  (операция ИЛИ). Длина
процедуры 16 байт (21 байт). Ад-
рес: 62432 (#F3E0).
   19. AREPLC  -  поиск  атрибу-
тов с определенным  значением  и
замена каждого найденного  тако-
го атрибута новым  значением.  В
ячейке по адресу  CONSTS  должно
содержаться значение байта, под-
лежащего замене (что искать).  В
ячейке по адресу  CONSTS+1  дол-
жно находиться значение замещаю-
щего байта (чем заменять).  Дли-
на процедуры 18 байт (22 байта).
Адрес: 62448 (#F3F0).

   20. PAINT - закрашивание  оп-
ределенной области экрана, огра-
ниченной линией пикселей (залив-
ка).  Стартовая  точка  задается
помещением ее  координаты  X  по
адресу CONSTS, а  координаты Y - 
по адресу CONSTS+1. Если коорди-
ната Y больше 191  или  точка  в
указанных координатах уже  уста-
новлена, то  программа экстренно 
прерывается.  Эта  процедура  не
перемещаема из-за вызовов проце-
дуры POINT. При закраске активно 
используется стек - на нем запо-
минаются координаты линий залив-
ки.  Когда закрашивается большая  
область сложной формы, необходи-
мо и большее свободное простран-
ство  в  ОЗУ - между  окончанием 
BASIC-программы  и  адресом, ус-
тановленным   оператором   CLEAR
(содержимое системной переменной
RAMTOP).  Если места в памяти не 
хватит, то может произойти сбой.  
Процедура  занимает  88  байт, а
вместе  с процедурой POINT - 123  
байта, что более чем  в два раза  
меньше длины процедуры 1992 года  
(263 байта!)  Адрес PAINT: 62466
(#F402).
   21. POINT - вычисление адреса
точки в экране по заданным коор-
динатам  и  проверка   состояния
этой точки  (ON/OFF).  Внимание!
Эта процедура может быть исполь-
зована только из  машинных кодов
(запуск  из  бейсика  ничего  не
даст).  Перед вызовом необходимо
установить  в  регистре E  коор-
динату X (0..255), а  в регистре 
D - координату Y (0..191) прове-
ряемой точки. На выходе процеду-
ра установит в  регистровой паре 
HL адрес байта на экране, в  ко-
тором находится точка, а в реги-
стре C - маску точки в этом бай-
те  (один  бит, установленный  в
единицу). В зависимости от того, 
включена точка или нет, устанав-
ливается флаг нуля: Z - точка не
включена, NZ  -  точка включена. 
Если точка установлена (видима), 
то регистр A (аккумулятор)  сов-
падает  по своему значению с ре-
гистром C, а если точка не уста-
новлена, то  A  обнуляется.  Ре-
гистр B всегда на выходе из про-
цедуры равен нулю.  Длина проце-
дуры 35 байт (в оригинале заняла
бы около 70 байт).  Адрес разме-
щения: 62554 (#F45A).
   22. PFIGURE - построение  лю-
бой  ранее  определенной  фигуры
(шаблона) на  экране.  Координа-
ты  начальной  (стартовой)  точ-
ки задаются аналогично процедуре
PAINT. Шаблон задается в строко-
вой переменной BASIC'а A$ (можно
изменить  на  любую другую, чуть
скорректировав ассемблерный лис-
тинг или дамп).  Строка символов
имеет  следующий формат (немного  
отличается от оригинала):

 "5" - уменьшить X-координату
 "6" - увеличить Y-координату
       (отсчет идет сверху-вниз)
 "7" - уменьшить Y-координату
 "8" - увеличить X-координату
 "0" - поместить точку.

   Любые другие символы  игнори-
руются.  Если строковая перемен-
ная не существует или  не содер-
жит никакой информации, то  про-
грамма  прерывает  свою  работу.
Контроля на  выход начальной  Y-
координаты нет, т.к. часть фигу-
ры все же может быть видна.
   Поэтому проверка на выход  за
пределы  экрана  введена  в  сам
цикл формирования шаблона.  Воз-
можность 'wrap-round' сохранена,
т.е.  по  прежнему,  при  выходе
X-координаты за  левую часть эк-
рана, шаблон появляется  справа, 
и наоборот.
   Процедура   не   перемещаема.
Длина PFIGURE: 98 байт, а  вмес-
те с используемой  подпрограммой
POINT - 133 байта, что все  рав-
но гораздо меньше оригинала (196
байт).  Адрес:  62589   (#F47D).
Если "раскрыть" вызов  процедуры
POINT, то PFIGURE станет переме-
щаемой  и  будет  занимать  при-
мерно 125 байт!
   23. PSCALER - копирование ча-
сти экрана в другую область это-
го же экрана с возможным  увели-
чением копии по  X  и/или по  Y.
Используются константы:

Адрес    Имя    Комментарий

CONSTS   X1_OLD одна из двух начальных X-координат прямоуг-ка
CONSTS+1 Y1_OLD одна из двух начальных Y-координат прямоуг-ка
CONSTS+2 X2_OLD одна из двух начальных X-координат прямоуг-ка
CONSTS+3 Y2_OLD одна из двух начальных Y-координат прямоуг-ка
CONSTS+4 XSCALE масштабность увеличения по X
CONSTS+5 YSCALE масштабность увеличения по Y
CONSTS+6 X_NEW  координаты левого верхнего угла области экрана,
CONSTS+7 Y_NEW    в которую производится копирование.

   Координаты начального прямоу-
гольника для копирования задают-
ся в константах  X1_OLD, Y1_OLD,
X2_OLD,  Y2_OLD,  причем,  могут
располагаться в любой последова-
тельности. Процедура сама  опре-
делит  наименьшую  и  наибольшую
координаты.
   Экстренный выход из процедуры
происходит в следующих случаях:

1. XSCALE=0 - масштаб увеличения
   по X равен нулю
2. YSCALE=0 - масштаб увеличения
   по Y равен нулю
3. Y_NEW>191 - новая  координата
   Y выходит за пределы экрана

4. Y1_OLD>191 - старая координа-
   та Y1 выходит за пределы  эк-
   рана
5. Y2_OLD>191 - старая координа-
   та Y2 выходит за пределы  эк-
   рана.

   Как и в оригинале, в програм-
ме  нет  контроля, который  про-
верял бы возможность  размещения
новой картинки на  экране.  Если
этого  не  получается, то  может
произойти сбой.
   При выполнении процедура вна-
чале закидывает на стек  битовый
образ копируемого прямоугольника
экрана и лишь затем прорисовыва-
ет его в новом месте, увеличивая  
при необходимости. Поэтому, если
на стеке недостаточно места, то,
как  и  в  процедуре  PAINT, мо-
жет произойти  зависание, сброс,
ошибка...
   Если Вы хотите, чтобы  скопи-
рованный кусок экрана  имел  тот
же  размер,  что  и  данный,  то
необходимо  установить   масштаб
1:1 - занести в константы XSCALE
и YSCALE по единичке.  При двой-
ном размере там должны быть дво-
ечки, ну и так далее...
   Процедура   не    перемещаема
из-за использования  подпрограм-
мы POINT. PSCALER  занимает  174
байта, а  вместе  с  POINT - 209
байт. В любом случае, это гораз-
до меньше, чем  оригинал  -  335
байт!  Адрес  размещения:  62687
(#F4DF).

   Итак, вот Вы и ознакомились с
новой реализацией "лучших проце-
дур".  Но  советую  не  обольща-
ться - некоторые  процедуры  на-
верняка можно  сократить  еще...
Правда, при этом неизбежно  при-
дется чем-то  пожертвовать: ско-
ростью  выполнения, перемещаемо-
стью или, наконец, просто потра-
ченным  временем.  Надеюсь,  что
приведенные  в этой статье прог-
раммы будут Вам полезны, в край-
нем  случае,  -  может быть, они
просто  натолкнут Вас на  какие-
нибудь мысли...  Начинающие  мо-
гут попробовать сравнить  проце-
дуры 1992 года с новыми, изучить
принципы создания  более  эффек-
тивных программ, приемы для  ин-
теграции   множества   различных
процедур в одну большую  библио-
теку... Опытные же программисты, 
возможно,  получат  немало  удо-
вольствия,  ехидно   посмеиваясь
над данной работой. Но  это тоже 
плюс:  повеселить  народ - очень
нужное дело!  Так что желаю всем  
читателям  ПРИЯТНОЙ РАБОТЫ С ЛЮ-
БИМЫМ SPECCY!

;--------------------------------------------------------------;
;               40 New Best Routines (graphic)                 ;
;         (c) SerzhSoft, Shadrinsk, may-june, 1997             ;
;   old length: 1444 bytes         new length:  861 bytes      ;
;--------------------------------------------------------------;
        ORG     62000        ;адрес ассемблирования
;--------------------------------------------------------------;
CONSTS  EQU     23296        ;адрес буфера констант (8 байт)

;--------------------------------------------------------------;
;Сдвиг атрибутов влево (22<=23)
;--------------------------------------------------------------;
ASRL_LF
        LD      DE,#5800     ;DE=адрес первого байта атрибутов
LP_ASLF LD      H,D          ;скопировали DE в HL
        LD      L,E          ; и увеличили HL на единицу:
        INC     HL           ; HL=адрес второго байта атрибутов
        LD      BC,#001F     ;<длина линии атрибутов> - 1
        LDIR                 ;сдвиг линии атрибутов влево
        LD      A,(CONSTS)   ;цвет заполнения после сдвига
        LD      (DE),A       ;устанавливаем новый атрибут
        INC     DE           ;переход к следующей линии снизу
        LD      A,D          ;если атрибуты уже кончились,
        CP      #5B          ; и мы набрели на буфер принтера,
        JR      C,LP_ASLF    ; то STOP, иначе сдвигаем дальше
        RET                  ;выход из процедуры
;--------------------------------------------------------------;
;Сдвиг атрибутов вправо (21<=23)
;--------------------------------------------------------------;
ASRL_RG
        LD      DE,#5AFF     ;адрес последнего байта атрибутов
LP_ASRG LD      H,D          ;скопировали DE в HL -
        LD      L,E          ; последний байт линии атрибутов
        DEC     HL           ;предпоследний байт линии атрибутов
        LD      BC,#001F     ;<длина линии атрибутов> - 1
        LDDR                 ;сдвиг линии атрибутов вправо
        LD      A,(CONSTS)   ;цвет заполнения после сдвига
        LD      (DE),A       ;устанавливаем новый атрибут
        DEC     DE           ;переход к следующей линии сверху
        BIT     3,D          ;если мы все еще в атрибутах,
        JR      NZ,LP_ASRG   ; то повторяем цикл для сл. линии
        RET                  ;выход из процедуры
;--------------------------------------------------------------;
;Сдвиг атрибутов вверх (19<=21)
;--------------------------------------------------------------;
ASRL_UP
        LD      HL,#5820     ;адрес второй линии атрибутов
        LD      DE,#5800     ;адрес первой линии атрибутов
        LD      BC,#02E0     ;перемещать: 23 линии по 32 байта
        LDIR                 ;сдвигаем 23 нижние линии вверх
        LD      A,(CONSTS)   ;цвет для заполнения нижней линии
LP_ASUP LD      (DE),A       ;устанавливаем новый атрибут
        INC     E            ;если заполнили всю последнюю линию
        JR      NZ,LP_ASUP   ; (E=0), то прерываем цикл
        RET                  ;выход из процедуры

;--------------------------------------------------------------;
;Сдвиг атрибутов вниз (20<=21)
;--------------------------------------------------------------;
ASRL_DN
        LD      HL,#5ADF     ;адрес конца второй линии снизу
        LD      DE,#5AFF     ;адрес конца самой нижней линии
        LD      BC,#02E0     ;перемещать: 23 линии по 32 байта
        LDDR                 ;сдвигаем 23 верхние линии вниз
        LD      A,(CONSTS)   ;цвет для заполнения верхней линии
LP_ASDN LD      (DE),A       ;устанавливаем новый атрибут
        DEC     E            ;если дошли до самого первого байта
        JR      NZ,LP_ASDN   ; области атрибутов (E=0), то STOP
        LD      (DE),A       ; и устанавливаем этот байт
        RET                  ;выход из процедуры
;--------------------------------------------------------------;
;Сдвиг влево на один символ (20<=21)
;--------------------------------------------------------------;
SSRL_LF
        LD      DE,#4000     ;начало области графики
LP_SSLF LD      H,D          ;адрес первого
        LD      L,E          ; байта линии
        INC     HL           ;адрес второго байта линии
        LD      BC,#001F     ;сколько байт сдвигать
        LDIR                 ;сдвиг линии влево на 1 байт
        XOR     A            ;обнулили аккумулятор и занесли
        LD      (DE),A       ; в последний (правый) байт линии
        INC     DE           ;переход к следующей линии (снизу)
        LD      A,D          ;если атрибуты
        CP      #58          ; "еще не видать",
        JR      C,LP_SSLF    ; то повторяем цикл для сл. линии
        RET                  ;выход из процедуры
;--------------------------------------------------------------;
;Сдвиг вправо на один символ (19<=22)
;--------------------------------------------------------------;
SSRL_RG
        LD      DE,#57FF     ;последний байт области графики
LP_SSRG LD      H,D          ;адрес последнего байта
        LD      L,E          ; текущей линии
        DEC     HL           ;адрес предпоследнего байта
        LD      BC,#001F     ;сдвигаем: 31 байт
        LDDR                 ;сдвиг линии графики вправо
        XOR     A            ;очищаем аккумулятор и затем
        LD      (DE),A       ; первый (левый) байт текущей линии
        DEC     DE           ;переход к следующей линии выше
        BIT     6,D          ;если мы еще не "набрели" на ПЗУ,
        JR      NZ,LP_SSRG   ; то продолжаем крутить цикл
        RET                  ;выход из процедуры
;--------------------------------------------------------------;
;Сдвиг вверх на один символ (55<=68)
;--------------------------------------------------------------;

SSRL_UP
        LD      DE,#4000     ;начало экранной области
LP_SSU1 PUSH    DE           ;сохраняем адрес линии на стеке
        LD      BC,#0020     ;в линии - 32 байта
        LD      A,E          ;В регистре DE находится адрес
        ADD     A,C          ; верхней линии. В регистре
        LD      L,A          ; HL необходимо получить адрес
        LD      A,D          ; линии, лежащей ниже с шагом 8.
        JR      NC,GO_SSUP   ; Для этого к регистру E прибав-
        ADD     A,#08        ; ляем 32 и заносим в L. Если про-
GO_SSUP LD      H,A          ; изошло переполнение, то H=D+8
        LDIR                 ;перенос одной линии (32 байта)
        POP     DE           ;восстанавливаем адрес начала линии
        LD      A,H          ;проверяем: а не пора ли нам закру-
        CP      #58          ; гляться? (перенесли все 23 ряда)
        JR      NC,LP_SSU2   ;если да, то переход на очистку
        INC     D            ;---------------------------------;
        LD      A,D          ;             DOWN_DE
        AND     #07          ; стандартная последовательность
        JR      NZ,LP_SSU1   ;  команд для перехода на линию
        LD      A,E          ;     вниз в экранной области
        ADD     A,#20        ;        (для регистра DE)
        LD      E,A          ;
        JR      C,LP_SSU1    ; на входе:  DE - адрес линии
        LD      A,D          ; на выходе: DE - адрес линии ниже
        SUB     #08          ;  используется аккумулятор
        LD      D,A          ;
        JR      LP_SSU1      ;---------------------------------;
LP_SSU2 XOR     A            ;очистка аккумулятора
LP_SSU3 LD      (DE),A       ;и с его помощью -
        INC     E            ; очистка одной линии изображения
        JR      NZ,LP_SSU3   ; всего: 32 байта
        LD      E,#E0        ;переход к следующей
        INC     D            ; (нижней) линии изображения
        BIT     3,D          ;заполнили весь последний ряд?
        JR      Z,LP_SSU2    ;если нет, то продолжаем заполнять
        RET                  ;выход из процедуры
;--------------------------------------------------------------;
;Сдвиг вниз на один символ (55<=73)
;--------------------------------------------------------------;
SSRL_DN
        LD      DE,#57FF     ;адрес последнего байта графики
LP_SSD1 PUSH    DE           ;сохранили адрес конца линии
        LD      BC,#0020     ;длина одной линии изображения
        LD      A,E          ;в регистре HL
        SUB     C            ; получаем адрес
        LD      L,A          ; конца линии,
        LD      A,D          ; лежащей выше
        JR      NC,GO_SSDN   ; исходной с шагом
        SUB     #08          ; в 8 пикселей (линий):
GO_SSDN LD      H,A          ; HL=откуда копировать; DE=куда
        LDDR                 ;перенос одной линии графики
        POP     DE           ;восстанавливаем адрес конца линии
        BIT     6,H          ;если мы уже не в экране,
        JR      Z,LP_SSD2    ; то переходим на очистку
        LD      A,D          ;---------------------------------;
        DEC     D            ;              UP_DE
        AND     #07          ; стандартная последовательность
        JR      NZ,LP_SSD1   ;  команд для перехода на линию
        LD      A,E          ;    вверх в экранной области
        SUB     #20          ;        (для регистра DE)
        LD      E,A          ;
        JR      C,LP_SSD1    ; на входе:  DE - адрес линии
        LD      A,D          ; на выходе: DE - адрес линии выше
        ADD     A,#08        ;  используется аккумулятор
        LD      D,A          ;
        JR      LP_SSD1      ;---------------------------------;
LP_SSD2 XOR     A            ;очистка аккумулятора
LP_SSD3 LD      (DE),A       ;очистка одной
        DEC     E            ; линии изображения:
        JR      NZ,LP_SSD3   ; (31 байт)
        LD      (DE),A       ;очищаем самый первый байт линии
        LD      E,#1F        ;переход к следующей (верхней)
        DEC     D            ; линии ряда из восьми линий
        BIT     6,D          ;мы еще не попали в ПЗУ?
        JR      NZ,LP_SSD2   ;если нет, то очищаем дальше
        RET                  ;выход из процедуры
;--------------------------------------------------------------;
;Сдвиг влево на один пиксел (16<=17)
;--------------------------------------------------------------;
PSRL_LF
        LD      HL,#57FF     ;адрес последнего байта графики
LP_PSL1 OR      A            ;сбрасываем флаг переноса CF
        LD      B,#20        ;в одной линии - 32 байта
LP_PSL2 RL      (HL)         ;CF<-[сдвигаемый байт]<-CF (влево)
        DEC     HL           ;переход к предыдущему байту линии
        DJNZ    LP_PSL2      ;цикл сдвига для одной линии
        BIT     6,H          ;мы еще в экране?
        JR      NZ,LP_PSL1   ;если да, то сдвигаем след. линию
        RET                  ;выход из процедуры
;--------------------------------------------------------------;
;Сдвиг вправо на один пиксел (17)
;--------------------------------------------------------------;
PSRL_RG
        LD      HL,#4000     ;адрес первого байта графики
        LD      C,#C0        ;сдвигать - 192 линии
LP_PSR1 OR      A            ;CF=0 для пустой колонки слева
        LD      B,#20        ;число байт в одной линии
LP_PSR2 RR      (HL)         ;сдвиг одного байта вправо
        INC     HL           ;следующий байт линии изображения
        DJNZ    LP_PSR2      ;сдвигаем всю линию - 32 байта
        DEC     C            ;уменьшаем счетчик линий
        JR      NZ,LP_PSR1   ;если сдвинули все линии, то STOP
        RET                  ;выход из процедуры

;--------------------------------------------------------------;
;Сдвиг вверх на один пиксел (38<=91)
;--------------------------------------------------------------;
PSRL_UP
        LD      DE,#4000     ;адрес начала графики (верх. линия)
LP_PSU1 LD      H,D          ;скопировали адрес начала
        LD      L,E          ; линии графики в HL
        LD      BC,#0020     ;размер одной линии
        INC     H            ;---------------------------------;
        LD      A,H          ;            DOWN_HL
        AND     #07          ; стандартная последовательность
        JR      NZ,GO_PSUP   ;  команд для перехода на линию
        LD      A,L          ;     вниз в экранной области
        ADD     A,C          ;        (для регистра HL)
        LD      L,A          ; (здесь ADD A,C вместо ADD A,#08)
        JR      C,GO_PSUP    ; на входе:  HL - адрес линии
        LD      A,H          ; на выходе: HL - адрес линии ниже
        SUB     #08          ;  используется аккумулятор
        LD      H,A          ;---------------------------------;
GO_PSUP PUSH    HL           ;сохраняем адрес нижней линии
        LDIR                 ;переброска изображения снизу-вверх
        POP     DE           ;DE - адрес нижней линии
        LD      A,H          ;мы еще находимся в области графики
        CP      #58          ; или уже наткнулись на атрибуты?
        JR      C,LP_PSU1    ;если все еще графика, то повтор
        XOR     A            ;обнуляем аккумулятор и с его
LP_PSU2 LD      (DE),A       ; помощью очищаем самую
        INC     E            ; нижнюю линию изображения
        JR      NZ,LP_PSU2   ; после сдвига экрана вверх
        RET                  ;выход из процедуры
;--------------------------------------------------------------;
;Сдвиг вниз на один пиксел (38<=90)
;--------------------------------------------------------------;
PSRL_DN
        LD      DE,#57FF     ;адрес последнего байта графики
LP_PSD1 LD      H,D          ;скопировали адрес последнего
        LD      L,E          ; байта линии в HL
        LD      BC,#0020     ;ширина одной линии изображения
        LD      A,H          ;---------------------------------;
        DEC     H            ;              UP_HL
        AND     #07          ; стандартная последовательность
        JR      NZ,GO_PSDN   ;  команд для перехода на линию
        LD      A,L          ;    вверх в экранной области
        SUB     C            ;        (для регистра HL)
        LD      L,A          ;   (здесь SUB C вместо SUB #08)
        JR      C,GO_PSDN    ; на входе:  HL - адрес линии
        LD      A,H          ; на выходе: HL - адрес линии выше
        ADD     A,#08        ;  используется аккумулятор
        LD      H,A          ;---------------------------------;
GO_PSDN PUSH    HL           ;сохраняем адрес верхней линии
        LDDR                 ;переносим 1 линию сверху - вниз
        POP     DE           ;адрес верхней линии стал текущим
        BIT     6,H          ;пока еще не попали в ПЗУ -
        JR      NZ,LP_PSD1   ; продолжаем цикл по линиям
        XOR     A            ;очищаем аккумулятор и с его
LP_PSD2 LD      (DE),A       ; помощью - самую верхнюю линию
        DEC     E            ; изображения после сдвига
        JR      NZ,LP_PSD2   ; всего экрана вниз
        LD      (DE),A       ;очистка самого первого байта
        RET                  ;выход из процедуры
;--------------------------------------------------------------;
;Слияние картинок (17<=21)
;--------------------------------------------------------------;
SCR_MRG
        LD      HL,(CONSTS)  ;взяли адрес картинки из ячейки
        LD      DE,#4000     ;адрес экранной области
LP_SCRM LD      A,(DE)       ;байт изображения с экрана
        OR      (HL)         ;"слили" с байтом картинки в памяти
        LD      (DE),A       ;и поместили назад в экран
        INC     HL           ;следующий байт картинки в памяти
        INC     DE           ;следующий байт экранной области
        LD      A,D          ;проверка на окончание
        CP      #58          ; экранной области
        JR      C,LP_SCRM    ;если не кончилась, то повторяем
        RET                  ;выход из процедуры
;--------------------------------------------------------------;
;Инвертирование экрана (12<=18)
;--------------------------------------------------------------;

SCR_INV
        LD      HL,#57FF     ;последний байт экранной области
LP_SCRI LD      A,(HL)       ;взяли байт изображения с экрана
        CPL                  ;проинвертировали его
        LD      (HL),A       ;и поставили обратно
        DEC     HL           ;двигаемся к началу области
        BIT     6,H          ;если "перевалили" через начало,
        JR      NZ,LP_SCRI   ; то STOP, иначе крутим цикл
        RET                  ;выход из процедуры
;--------------------------------------------------------------;
;Инвертирование символа вертикально (20)
;--------------------------------------------------------------;
SINV_UD
        LD      HL,(CONSTS)  ;взяли из ячейки адрес символа
        LD      D,H          ;сохранили этот
        LD      E,L          ; адрес в DE
        LD      B,#08        ;в символе - 8 байт
LP_SIU1 LD      A,(HL)       ;берем один байт символа
        PUSH    AF           ;и заталкиваем на стек
        INC     HL           ;переход к следующему байту символа
        DJNZ    LP_SIU1      ;повторяем цикл для восьми байт
        LD      B,#08        ;сколько байт будем считывать
LP_SIU2 POP     AF           ;извлекаем байт со стека и в обрат-
        LD      (DE),A       ; ном порядке записываем в символ
        INC     DE           ;следующий байт символа
        DJNZ    LP_SIU2      ;крутим цикл восемь раз
        RET                  ;выход из процедуры
;--------------------------------------------------------------;
;Инвертирование символа горизонтально (17<=19)
;--------------------------------------------------------------;
SINV_LR
        LD      HL,(CONSTS)  ;берем из ячейки адрес символа
        LD      B,#08        ;модифицируем: 8 байт

LP_SIL1 LD      A,#01        ;устанавливаем нулевой бит A в 1
LP_SIL2 RR      (HL)         ;вращаем байт символа вправо
        RLA                  ;а аккумулятор - влево (через CF)
        JR      NC,LP_SIL2   ;пока нулевой бит не окажется в CF
        LD      (HL),A       ;записываем измененный байт
        INC     HL           ;следующий байт символа
        DJNZ    LP_SIL1      ;повторяем цикл 8 раз
        RET                  ;выход из процедуры
;--------------------------------------------------------------;
;Вращение символа по часовой стрелке (26<=42)
;--------------------------------------------------------------;
SROTATE
        LD      HL,(CONSTS)  ;адрес вращаемого символа из ячейки
        LD      B,#08        ;8 вертикальных колонок в символе
LP_SRO1 PUSH    HL           ;сохранили адрес на стеке
        LD      A,#80        ;включили 7-ой бит в аккумуляторе
LP_SRO2 RR      (HL)         ;вращаем байты символа вправо
        RRA                  ; и по одному биту из каждого байта
        INC     HL           ; постепенно заполняем аккумулятор
        JR      NC,LP_SRO2   ; пока 7 вкл. бит не попадет в CF
        POP     HL           ;восстанавливаем адрес символа
        PUSH    AF           ;вертик. колонку символа - на стек
        DJNZ    LP_SRO1      ;крутим цикл по числу колонок
        LD      B,#08        ;колонки стали линиями - байтами
LP_SRO3 POP     AF           ;стаскиваем байт со стека
        LD      (HL),A       ;и это - уже новая линия символа
        INC     HL           ;следующий байт символа
        DJNZ    LP_SRO3      ;повторяем по числу линий (8 байт)
        RET                  ;выход из процедуры
;--------------------------------------------------------------;
;Изменение атрибута (16<=21)
;--------------------------------------------------------------;

ACHANGE
        LD      HL,(CONSTS)  ;L - маска (AND), H - добавка (OR)
        LD      DE,#5AFF     ;последний байт области атрибутов
LP_ACHN LD      A,(DE)       ;взяли текущее значение атрибута
        AND     L            ;отбросили лишние биты
        OR      H            ;добавили необходимые
        LD      (DE),A       ;и записали на старое место
        DEC     DE           ;движемся к началу атрибутов
        BIT     3,D          ;а не графика ли уже?
        JR      NZ,LP_ACHN   ;если нет, то крутим цикл
        RET                  ;выход из процедуры
;--------------------------------------------------------------;
;Смена атрибута (18<=22)
;--------------------------------------------------------------;
AREPLC
        LD      DE,(CONSTS)  ;E - что искать, D - чем заменять
        LD      HL,#5AFF     ;последний байт области атрибутов
LP_ARPL LD      A,(HL)       ;взяли байт из области атрибутов
        CP      E            ;не тот ли, что ищем?
        JR      NZ,GO_ARPL   ;нет, перепрыгиваем изменение
        LD      (HL),D       ;да, изменяем на новое значение
GO_ARPL DEC     HL           ;движемся к началу обл-ти атрибутов
        BIT     3,H          ;атрибуты еще не кончились?
        JR      NZ,LP_ARPL   ;если нет, то проверяем следующий
        RET                  ;выход из процедуры
;--------------------------------------------------------------;
;Закрашивание контура (123<=263)
; 123=88+35 - вместе с процедурой POINT
;--------------------------------------------------------------;
PAINT
        LD      HL,(CONSTS)  ;координаты начальной точки
        LD      A,H          ;проверяем координату Y на выход
        CP      #C0          ; за пределы экрана:
        RET     NC           ; если Y>=192, то экстренный выход
        SBC     A,A          ;т.к. CF=1, то SBC A,A дает A=#FF -
        PUSH    AF           ; это будет указатель конца стека
        PUSH    HL           ;запоминаем координаты первой точки
LP_PNT1 POP     DE           ;берем со стека X,Y след. точки
        INC     D            ;если Y=#FF, то стек исчерпан,
        RET     Z            ; и тогда выходим из процедуры
        DEC     D            ;восстанавливаем исходн. значение Y
        CALL    POINT        ;проверяем точку с коорд-ми (E,D)
        JR      NZ,LP_PNT1   ;если включена, то переход к след.
        EX      AF,AF'       ;A'=0, CF=0 - вспомогат. признаки
LP_PNT2 LD      A,E          ;взяли координату X
        OR      A            ;если она равна нулю,
        JR      Z,GO_PNT1    ; то прыжок через движение назад
        DEC     E            ;иначе - уменьшаем координату X
        CALL    POINT        ;и проверяем предыдущую точку
        JR      Z,LP_PNT2    ;если "нет препятствия", повторяем
LP_PNT3 INC     E            ;переход на точку вправо (X=X+1)
        JR      Z,LP_PNT1    ;если X>255, то сл. точка со стека
GO_PNT1 CALL    POINT        ;проверяем следующую правую точку
        JR      NZ,LP_PNT1   ;если включена, то след. со стека
        LD      A,(HL)       ;если точка не установлена,
        OR      C            ; то берем байт с экрана, включаем
        LD      (HL),A       ; нужный бит и ставим назад
        LD      A,D          ;проверяем координату Y:
        OR      A            ;если она равна нулю,
        JR      Z,GO_PNT4    ; то не проверяем лежащ. выше линию
        DEC     D            ;переход к линии выше (Y=Y-1)
        CALL    POINT        ;проверка вышележащей точки
        JR      Z,GO_PNT2    ;если не включена, то переход
        EX      AF,AF'       ;взяли вспомогательные флаги
        LD      A,B          ;разрешили запоминать точку в стеке
        JR      GO_PNT3      ;переход на продолжение
GO_PNT2 EX      AF,AF'       ;взяли вспомогательные флаги
        INC     A            ;если A>0, то это означает запрет
        DEC     A            ; на сохранение координаты новой
        JR      NZ,GO_PNT3   ; точки в стеке, -> перепрыгиваем
        LD      A,C          ;иначе - запрещаем сохранять коор-
        PUSH    DE           ; динаты, но одну пихаем на стек
GO_PNT3 EX      AF,AF'       ;сохранили вспомогательные флаги
        INC     D            ;возвращаемся на нижнюю линию
GO_PNT4 LD      A,D          ;проверяем координату Y:
        CP      #BF          ;если - последняя (ниже не бывает),
        JR      NC,LP_PNT3   ; то переход к след. точке справа
        INC     D            ;иначе - спускаемся на линию ниже
        CALL    POINT        ;проверяем нижележащую точку
        JR      Z,GO_PNT5    ;если не включена, то переход
        EX      AF,AF'       ;взяли вспомогательные флаги
        AND     A            ;разрешили запоминать точку в стеке
        JR      GO_PNT6      ;переход на продолжение
GO_PNT5 EX      AF,AF'       ;взяли вспомогательные флаги
        JR      C,GO_PNT6    ;если нельзя сохранять, то переход
        SCF                  ;запрещаем сохранять точку на стеке
        PUSH    DE           ;но одну точку на стек запихиваем
GO_PNT6 EX      AF,AF'       ;сохранили вспомогательные флаги
        DEC     D            ;возвращаемся на верхнюю линию
        JR      LP_PNT3      ;переход к следующей точке справа
;--------------------------------------------------------------;
;Проверка состояния точки и вычисление адреса в экране (35<=70)
;--------------------------------------------------------------;
POINT   ;если точка выключена, то ZF=1 (Z), иначе ZF=0 (NZ)
        LD      B,#07        ;часто используемая маска (#07)
        LD      A,D          ;взяли Y-координату
        RRA                  ;разделили ее на 8
        SCF                  ; и начали формировать
        RRA                  ; старший байт
        RRA                  ; адреса пиксела
        AND     #5F          ; в экране (регистр H):
        LD      H,A          ;  %010yyyyy
        XOR     E            ;далее формируем
        AND     B            ; младший байт
        XOR     E            ; адреса
        RRCA                 ; пиксела
        RRCA                 ; в экране
        RRCA                 ; (регистр L):
        LD      L,A          ;  %yyyxxxxx
        LD      A,D          ;заканчиваем
        XOR     H            ; формирование
        AND     B            ; старшего байта
        XOR     H            ; адреса пиксела
        LD      H,A          ; в экране (регистр H)
        LD      A,E          ;начинаем формировать
        AND     B            ; маску пиксела в байте
        LD      B,A          ; изображения (соответствующий
        LD      A,#80        ; бит включен). Включаем 7-ой бит
        JR      Z,GO_PNT     ;если это как раз то что надо,
LP_PNT  RRCA                 ; то перепрыгиваем через сдвиг
        DJNZ    LP_PNT       ; включенного бита вправо
GO_PNT  LD      C,A          ;сохраняем маску вкл. бита в рег. C
        AND     (HL)         ;проверяем пиксел в экране
        RET                  ;выход из процедуры

;--------------------------------------------------------------;
;Построение шаблонов (98<=196)
; 98+35=133 - вместе с процедурой POINT
;--------------------------------------------------------------;
PFIGURE
        LD      DE,(CONSTS)  ;координаты начальной точки
        LD      HL,(23627)   ;адрес начала переменных бейсика
LP_PFG1 LD      A,(HL)       ;первый байт какой-то переменной
        INC     HL           ;переход к следующему байту
        LD      BC,#0012     ;размер переменной цикла FOR...NEXT
        CP      #E0          ;нашли переменную цикла FOR...NEXT?
        JR      NC,GO_PFG2   ;если да, то переход к след. перем.
        CP      #80          ;переменные бейсика закончились?
        RET     Z            ;если да, то выходим из процедуры
        LD      C,#05        ;длина числ. перем. с одним симв.
        JR      NC,GO_PFG3   ;массив или числ. пер. с неск. сим.
        CP      #60          ;числ перем. с одним симв. в имени?
        JR      NC,GO_PFG2   ;да, переход к следующей переменной
        CP      "A"          ;имя символьной переменной (A$)
        JR      Z,GO_PFG4    ;ура, все-таки нашли-и-и-и-и!!!
GO_PFG1 LD      C,(HL)       ;получаем
        INC     HL           ; размер исследуемой
        LD      B,(HL)       ; переменной
        INC     HL           ; в байтах и,
GO_PFG2 ADD     HL,BC        ; прибавив к адресу,
        JR      LP_PFG1      ; переходим к следующей переменной
GO_PFG3 BIT     5,A          ;переменная массива?
        JR      Z,GO_PFG1    ;да, перепрыгиваем ее
LP_PFG2 BIT     7,(HL)       ;проверка на конец имени числ. пер.
        INC     HL           ;следующий байт имени
        JR      NZ,GO_PFG1   ;имя закончилось, переход
        JR      LP_PFG2      ;продолжаем просматривать имя
GO_PFG4 LD      C,(HL)       ;взяли длину найденной
        INC     HL           ; строковой переменной
        LD      B,(HL)       ; с данными по шаблону
LP_PFG3 INC     HL           ;следующий символ данных шаблона
        LD      A,B          ;проверяем: а не исчерпали ли
        OR      C            ; мы все данные по шаблону?
        RET     Z            ;если да (длина=0), то выход
        DEC     BC           ;уменьшили длину
        LD      A,(HL)       ;взяли символ данных шаблона
        CP      "0"          ;а не "поставить ли точку"?
        JR      NZ,GO_PFG6   ;если нет, переход на продолжение
        LD      A,D          ;y-координата текущей точки
        CP      #C0          ;если выходит за нижнюю кромку
        JR      NC,LP_PFG3   ; экрана, то точку не изображаем
        PUSH    HL           ;иначе - сохраняем некоторые
        PUSH    BC           ; регистры, чтоб не портились
        CALL    POINT        ;вызываем процедуру проверки точки
        LD      A,(HL)       ;по вычисленным значениям
        OR      C            ; изображаем точку на экране
        LD      (HL),A       ; пользуясь тем, что HL=адрес,
        POP     BC           ; а регистр C содержит маску точки
        POP     HL           ;восстанавливаем сохр-ные регистры
        JR      LP_PFG3      ;обработка след. символа шаблона
GO_PFG6 SUB     "5"          ;сместить перо влево?
        JR      NZ,GO_PFG7   ;если нет, то оставить все как есть
        DEC     E            ;иначе - уменьшить x-координату
GO_PFG7 DEC     A            ;двигаемся вниз?
        JR      NZ,GO_PFG8   ;нет, переход
        INC     D            ;да, увеличиваем y-координату
GO_PFG8 DEC     A            ;направление "вверх"?
        JR      NZ,GO_PFG9   ;нет, перепрыгиваем
        DEC     D            ;да, уменьшаем y-координату
GO_PFG9 DEC     A            ;может надо сдвинуться вправо?
        JR      NZ,LP_PFG3   ;нет, переход к след. символу шабл.
        INC     E            ;да, увеличиваем x-координату
        JR      LP_PFG3      ; и переходим к след. символу шабл.
;--------------------------------------------------------------;
;Увеличение экрана и копирование (174<=335)
; 174+35=209 - вместе с процедурой POINT
;--------------------------------------------------------------;
PSCALER
        LD      HL,(CONSTS+4);масштабность увеличения по x и y
        INC     L            ;проверка x-координаты
        DEC     L            ; на нулевое значение
        RET     Z            ;если равна 0, то ошибка (выход)
        INC     H            ;проверка y-координаты
        DEC     H            ; на нулевое значение
        RET     Z            ;если равна 0, то ошибка (выход)
        LD      HL,(CONSTS+6);новые x-,y-координаты ("куда")
        LD      A,#BF        ;максимально возможная y-координата
        CP      H            ;проверяем новую y-координату
        RET     C            ;если не в экране - выход
        LD      HL,(CONSTS)  ;x1-,y1-координаты ("откуда")
        CP      H            ;проверяем y1 на вхождение в экран
        RET     C            ;если за экраном, то выходим
        LD      DE,(CONSTS+2);x2-,y2-координаты ("откуда")
        CP      D            ;y2 находится в экране?
        RET     C            ;если нет, то выход из процедуры
        LD      A,E          ;координата x2
        CP      L            ;сравнили с координатой x1
        JR      NC,GO_PSC1   ;если L больше E, то все нормально
        EX      DE,HL        ;иначе - поменяли их местами
GO_PSC1 LD      A,D          ;координата y2
        CP      H            ;сравнили с координатой y1
        JR      NC,GO_PSC2   ;если H больше D, то перепрыгиваем
        LD      D,H          ;иначе - меняем
        LD      H,A          ; их местами
GO_PSC2 LD      A,D          ;большая из y-координат
        SUB     H            ;вычитаем меньшую y-координату
        INC     A            ;и прибавляем 1
        EXX                  ;перешли к альтернативным регистрам
        LD      B,A          ;величина смещения по y (высота)
        EXX                  ;вернулись к основным регистрам
        LD      A,E          ;большая из x-координат
        SUB     L            ;вычитаем меньшую x-координату
        INC     A            ;прибавили единичку
        EXX                  ;сменили набор регистров
        LD      C,A          ;величина смещения по x (ширина)
        EXX                  ;вернулись к осн. набору регистров
        PUSH    AF           ;закинули один байт на стек (любой)
        INC     SP           ; - это необходимо для завершения
        LD      C,#08        ;число бит в одном байте
LP_PSC1 LD      A,E          ;запоминаем в альтернативном акку-
        EX      AF,AF'       ; муляторе коорд. x окончания линии
LP_PSC2 PUSH    HL           ;запоминаем регистры HL,BC на стеке
        PUSH    BC           ; чтобы не испортились
        CALL    POINT        ;вызов процедуры проверки точки
        POP     BC           ;восстанавливаем сохраненные
        POP     HL           ; регистры BC,HL со стека
        ADD     A,#FF        ;если A>0, то флаг CF включится
        RR      B            ;"вкручиваем" этот бит в регистр B
        DEC     C            ;уменьшаем счетчик количества битов
        JR      NZ,GO_PSC3   ;если не ноль, то перепрыгиваем
        PUSH    BC           ;иначе - забрасываем на стек
        INC     SP           ; регистр B (только 1 байт)
        LD      C,#08        ; и устанавливаем счетчик битов
GO_PSC3 LD      A,E          ;текущая x-координата
        DEC     E            ;движемся по линии влево
        CP      L            ;проверка на окончание линии
        JR      NZ,LP_PSC2   ;крутим цикл по линии
        EX      AF,AF'       ;восстанавливаем значение
        LD      E,A          ; x-координаты из альтернативного A
        LD      A,D          ;текущая y-координата
        DEC     D            ;движемся по строчкам вверх
        CP      H            ;это была последняя линия?
        JR      NZ,LP_PSC1   ;если нет, то крутим цикл по линиям
        LD      A,#08        ;число битов в байте
        SUB     C            ;A=число заполненных битов в рег. B
        JR      NZ,GO_PSC4   ;если не ноль, то перепрыгиваем
        LD      A,C          ;A=C=8 - число бит в байте
        DEC     SP           ;снимаем со стека последний
        POP     BC           ; заброшенный туда байт
GO_PSC4 LD      C,A          ;сколько битов данных в посл. байте
        LD      DE,(CONSTS+6);новые x-,y-координаты ("куда")
LP_PSC3 LD      A,E          ;сохраняем x-координату начала
        EX      AF,AF'       ; линии изображения в A'
        EXX                  ;переход к альтернативным регистрам
        LD      E,C          ;это будет счетчик точек по x
LP_PSC4 EXX                  ;вернулись к осн. набору регистров
        EX      AF,AF'       ;переход к альтерн. флаг. регистру
        RLC     B            ;флаг CF - выводить/не_выв. точку
        EX      AF,AF'       ;вернулись к нормальным флагам
        PUSH    BC           ;сохраняем байт данных и сч. битов
        LD      HL,(CONSTS+4);масштабность увеличения по x и y
        LD      B,H          ;сохранили масштабы увеличения
        LD      C,L          ; в регистрах C и B (x и y)
        PUSH    DE           ;сохраняем коор-ты (цикл по линиям)
LP_PSC5 PUSH    DE           ;сохраняем коор-ты (цикл по точкам)
LP_PSC6 PUSH    HL           ;сохраняем регистры HL и BC
        PUSH    BC           ; перед вызовом процедуры POINT
        CALL    POINT        ;расчет адреса в экране и маски
        LD      A,C          ;маска точки (бит включен)
        POP     BC           ;восстановили BC со стека
        EX      AF,AF'       ;проверяем альтернативный флаг CF
        JR      C,GO_PSC5    ;если он включен, то перепрыгиваем
        EX      AF,AF'       ;сохраняем этот флаг CF
        CPL                  ;инвертируем маску бита точки
        AND     (HL)         ; и с ее помощью сбрасываем пиксел
        JR      GO_PSC6      ;переход на продолжение
GO_PSC5 EX      AF,AF'       ;флаг CF делаем снова альтерн-ным
        OR      (HL)         ;включаем пиксел
GO_PSC6 LD      (HL),A       ;запись измененного байта в экран
        POP     HL           ;восстанавливаем HL (сч. масштаба)
        INC     E            ;переход к сл. точке в линии экрана
        DEC     L            ;уменьшаем счетчик масштаба по x
        JR      NZ,LP_PSC6   ;пока не ноль, продолжаем цикл
        LD      L,C          ;восстанавливаем значение x-масшт.
        POP     DE           ;восстан-ем координаты начала линии
        INC     D            ;переход к следующей линии в экране
        DEC     H            ;уменьшаем счетчик масштаба по y
        JR      NZ,LP_PSC5   ;и крутимся, пока он не достигнет 0
        LD      H,B          ;восстанавливаем значение y-масшт.
        POP     DE           ;восстан-ем координаты начала точки
        LD      A,E          ;переход к началу следующего
        ADD     A,L          ; прямоугольника, изображающего
        LD      E,A          ; одну точку изображения (вправо)
        POP     BC           ;восстанавливаем байт д-х и счетчик
        DEC     C            ;уменьшаем счетчик битов в байте B
        JR      NZ,GO_PSC7   ;если есть еще биты, то переход
        DEC     SP           ;иначе - считываем со стека
        POP     BC           ; следующий байт данных в регистр B
        LD      C,#08        ;устанавливаем счетчик битов
GO_PSC7 EXX                  ;переход к альтернативным регистрам
        DEC     E            ;уменьшаем счетчик точек в строчке
        JR      NZ,LP_PSC4   ;если еще есть точки, то крутимся
        EXX                  ;вернулись к осн. набору регистров
        EX      AF,AF'       ;восстанавливаем из альтернативного
        LD      E,A          ; аккумулятора x-координату строчки
        LD      A,D          ;переход к началу следующего прямо-
        ADD     A,H          ; угольника, изображающего одну
        LD      D,A          ; точку изображения (вниз)
        EXX                  ;переход к альтернативным регистрам
        DEC     B            ;уменьшаем счетчик строк спрайта
        EXX                  ;вернулись к осн. набору регистров
        JR      NZ,LP_PSC3   ;цикл, если строчки не закончились
        RET                  ;выход из процедуры
;--------------------------------------------------------------;
; end of         40 New Best Routines (graphic)                ;
;--------------------------------------------------------------;
    Шестнадцатеричный дамп библиотеки

    F270: 5A 11 FF 5A 01 E0 02 ED :F6
    F278: B8 3A 00 5B 12 1D 20 FC :02
    F280: 12 C9 11 00 40 62 6B 23 :8E
    F288: 01 1F 00 ED B0 AF 12 13 :0B
    F290: 7A FE 58 38 F0 C9 11 FF :53
    F298: 57 62 6B 2B 01 1F 00 ED :E6
    F2A0: B8 AF 12 1B CB 72 20 F1 :74
    F2A8: C9 11 00 40 D5 01 20 00 :AA
    F2B0: 7B 81 6F 7A 30 02 C6 08 :87
    F2B8: 67 ED B0 D1 7C FE 58 30 :81
    F2C0: 12 14 7A E6 07 20 E5 7B :BF
    F2C8: C6 20 5F 38 DF 7A D6 08 :6E
    F2D0: 57 18 D9 AF 12 1C 20 FC :03
    F2D8: 1E E0 14 CB 5A 28 F4 C9 :E6
    F2E0: 11 FF 57 D5 01 20 00 7B :AA
    F2E8: 91 6F 7A 30 02 D6 08 67 :CB
    F2F0: ED B8 D1 CB 74 28 12 7A :4B
    F2F8: 15 E6 07 20 E6 7B D6 20 :63
    F300: 5F 38 E0 7A C6 08 57 18 :21
    F308: DA AF 12 1D 20 FC 12 1E :FF
    F310: 1F 15 CB 72 20 F3 C9 21 :71
    F318: FF 57 B7 06 20 CB 16 2B :4A
    F320: 10 FB CB 74 20 F4 C9 21 :5B
    F328: 00 40 0E C0 B7 06 20 CB :D1
    F330: 1E 23 10 FB 0D 20 F5 C9 :5A
    F338: 11 00 40 62 6B 01 20 00 :6A
    F340: 24 7C E6 07 20 09 7D 81 :E7
    F348: 6F 38 04 7C D6 08 67 E5 :8C
    F350: ED B0 D1 7C FE 58 38 E3 :9E
    F358: AF 12 1C 20 FC C9 11 FF :1D
    F360: 57 62 6B 01 20 00 7C 25 :39
    F368: E6 07 20 09 7D 91 6F 38 :26
    F370: 04 7C C6 08 67 E5 ED B8 :A2
    F378: D1 CB 74 20 E4 AF 12 1D :5D
    F380: 20 FC 12 C9 2A 00 5B 11 :00
    F388: 00 40 1A B6 12 23 13 7A :4D
    F390: FE 58 38 F6 C9 21 FF 57 :47
    F398: 7E 2F 77 2B CB 74 20 F8 :31
    F3A0: C9 2A 00 5B 54 5D 06 08 :A0
    F3A8: 7E F5 23 10 FB 06 08 F1 :3B
    F3B0: 12 13 10 FB C9 2A 00 5B :21
    F3B8: 06 08 3E 01 CB 1E 17 30 :28
    F3C0: FB 77 23 10 F5 C9 2A 00 :40
    F3C8: 5B 06 08 E5 3E 80 CB 1E :B0
    F3D0: 1F 23 30 FA E1 F5 10 F3 :08
    F3D8: 06 08 F1 77 23 10 FB C9 :38
    F3E0: 2A 00 5B 11 FF 5A 1A A5 :81
    F3E8: B4 12 1B CB 5A 20 F7 C9 :C1
    F3F0: ED 5B 00 5B 21 FF 5A 7E :7E
    F3F8: BB 20 01 72 2B CB 5C 20 :AB
    F400: F6 C9 2A 00 5B 7C FE C0 :72
    F408: D0 9F F5 E5 D1 14 C8 15 :07
    F410: CD 5A F4 20 F7 08 7B B7 :70
    F418: 28 09 1D CD 5A F4 28 F6 :93
    F420: 1C 28 E9 CD 5A F4 20 E4 :60
    F428: 7E B1 77 7A B7 28 13 15 :43
    F430: CD 5A F4 28 04 08 78 18 :03
    F438: 07 08 3C 3D 20 02 79 D5 :24
    F440: 08 14 7A FE BF 30 D9 14 :A4
    F448: CD 5A F4 28 04 08 A7 18 :4A
    F450: 05 08 38 02 37 D5 08 15 :B4
    F458: 18 C6 06 07 7A 1F 37 1F :26
    F460: 1F E6 5F 67 AB A0 AB 0F :24
    F468: 0F 0F 6F 7A AC A0 AC 67 :C2
    F470: 7B A0 47 3E 80 28 03 0F :BE
    F478: 10 FD 4F A6 C9 ED 5B 00 :7F
    F480: 5B 2A 4B 5C 7E 23 01 12 :54
    F488: 00 FE E0 30 13 FE 80 C8 :E3
    F490: 0E 05 30 0F FE 60 30 08 :6C
    F498: FE 41 28 12 4E 23 46 23 :DF
    F4A0: 09 18 E1 CB 6F 28 F5 CB :B8
    F4A8: 7E 23 20 F0 18 F9 4E 23 :CF
    F4B0: 46 23 78 B1 C8 0B 7E FE :85
    F4B8: 30 20 11 7A FE C0 30 F1 :66
    F4C0: E5 C5 CD 5A F4 7E B1 77 :1F
    F4C8: C1 E1 18 E5 D6 35 20 01 :87
    F4D0: 1D 3D 20 01 14 3D 20 01 :B1
    F4D8: 15 3D 20 D5 1C 18 D2 2A :43
    F4E0: 04 5B 2C 2D C8 24 25 C8 :65
    F4E8: 2A 06 5B 3E BF BC D8 2A :22
    F4F0: 00 5B BC D8 ED 5B 02 5B :78
    F4F8: BA D8 7B BD 30 01 EB 7A :4C
    F500: BC 30 02 54 67 7A 94 3C :E8
    F508: D9 47 D9 7B 95 3C D9 4F :6A
    F510: D9 F5 33 0E 08 7B 08 E5 :84
    F518: C5 CD 5A F4 C1 E1 C6 FF :54
    F520: CB 18 0D 20 04 C5 33 0E :2F
    F528: 08 7B 1D BD 20 E9 08 5F :EA
    F530: 7A 15 BC 20 E0 3E 08 91 :47
    F538: 20 03 79 3B C1 4F ED 5B :5C
    F540: 06 5B 7B 08 D9 59 D9 08 :2C
    F548: CB 00 08 C5 2A 04 5B 44 :A2
    F550: 4D D5 D5 E5 C5 CD 5A F4 :01
    F558: 79 C1 08 38 05 08 2F A6 :A9
    F560: 18 02 08 B6 77 E1 1C 2D :CE
    F568: 20 E9 69 D1 14 25 20 E2 :DB
    F570: 60 D1 7B 85 5F C1 0D 20 :E3
    F578: 04 3B C1 0E 08 D9 1D 20 :99
    F580: C5 D9 08 5F 7A 84 57 D9 :A8
    F588: 05 D9 20 B6 C9 00 00 00 :FA

Сохранение: SAVE "nbestg.c" CODE 62000,861

           *   *   *

        
          (с) Vladik Ermolin



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



ZXBOOKS — Журналы, газеты и книги для ZX Spectrum
© 2008-2013 Ermolin Vladislav

Hosted by uCoz