(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!
140.
;--------------------------------------------------------------;
;3 40 New Best Routines (graphic) ;
;3 (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 3;перемещать: 23 линии по 32 байта
LDIR 3;сдвигаем 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 3;перемещать: 23 линии по 32 байта
LDDR 3;сдвигаем 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 3;сдвигаем: 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 3;в линии - 32 байта
LD A,E ;В регистре DE находится адрес
ADD A,C ; верхней линии. В регистре
LD L,A ; HL необходимо получить адрес
LD A,D ; линии, лежащей ниже с шагом 8.
JR NC,GO_SSUP ; Для этого к регистру E прибав-
ADD A,#08 3; ляем 32 и заносим в L. Если про-
GO_SSUP LD H,A ; изошло переполнение, то H=D+8
LDIR 3;перенос одной линии (32 байта)
POP DE ;восстанавливаем адрес начала линии
LD A,H ;проверяем: а не пора ли нам закру-
CP #58 3; гляться? (перенесли все 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 3; всего: 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 3; (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 3;в одной линии - 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 3;сдвигать - 192 линии
LP_PSR1 OR A ;CF=0 для пустой колонки слева
LD B,#20 ;число байт в одной линии
LP_PSR2 RR (HL) ;сдвиг одного байта вправо
INC HL ;следующий байт линии изображения
DJNZ LP_PSR2 3;сдвигаем всю линию - 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 ;переброска изображения снизу-вверх
140. 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)
140. 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 ;если L0, то флаг 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 ;выход из процедуры
;--------------------------------------------------------------;
3; end of 40 New Best Routines (graphic) ;
;--------------------------------------------------------------;
2
Шестнадцатеричный дамп библиотеки
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
* * *