3. ПРАКТИКУМ ПО ГРАФИКЕ В МАШИННЫХ КОДАХ
Эта обширная глава имеет триединую цель. Во-первых, она
служит как бы практикумом по тому материалу, который был рас-
смотрен нами ранее. Во-вторых, это коллекция полезных идей,
приемов и алгоритмов, являющихся учебным пособием для самопод-
готовки. И, наконец, в-третьих, тот материал, который здесь
представлен, может быть легко инкорпорирован Вами в Ваши про-
граммы для достижения профессиональных графических эффектов.
Если Вы дочитали книгу до этого раздела, то уже хорошо се-
бе представляете, что говоря о графике мы прибегаем к БЕЙСИКу
только на уровне идей и концепций, а как только дело доходит до
конкретных практических применений, нам приходится задейство-
вать машинный код. И все-таки жаль расставаться с БЕЙСИКом,
ведь с ним все так просто, что не использовать эту простоту
было бы неразумно.
Желая сохранить простоту БЕЙСИКа и быстродействие машинно-
кодовых процедур, мы организовали их совместное использование в
рамках данной главы. БЕЙСИКу отводится роль связующего звена, а
также блока логики верхнего уровня, а конкретные операции
выполняет машинный код, представленный разными процедурами.
Существуют различные способы объединения в единой структу-
ре и БЕЙСИКа и машинного кода. Так, на страницах наших книг и
журналов мы не раз говорили о введении машинного кода в БЕЙСИК-
строку за оператором REM. Вместе с тем, один из широко распро-
страненных приемов, связанный с использованием функций пользо-
вателя FN до сих пор не нашел должного освещения в наших рабо-
тах, о чем приходится сожалеть. Дело в том, что это тоже очень
удобный прием, который становится особенно незаменимым в тех
случаях, когда при вызове машиннокодовой процедуры из БЕЙСИКа
надо передать в нее ряд параметров.
Обычный прием по передаче параметров из БЕЙСИКа в машинный
код состоит в том, что в оперативной памяти Вы выделяете нес-
колько ячеек памяти для хранения этих параметров, т.е. исполь-
зуете эти адреса, как адреса программных переменных. Затем, пе-
ред вызовом машиннокодовой процедуры, выставляете с помощью
оператора POKE нужный Вам параметр в его ячейке памяти NN, вы-
зываете процедуру, она "подхватывает" Ваш параметр из этой
ячейки, отправляет в регистр процессора, работает с ним и, если
нужно вернуть какой-то результат работы, тоже отправляет его в
заранее зарезервированную ячейку памяти, например MM, после
чего возвращается в БЕЙСИК. В БЕЙСИКе Вы можете с помощью опе-
ратора PEEK MM "прощупать", что же там наработала Ваша про-
цедура.
Это стандартный прием. Его используют, например, большие
программы, целиком написанные в машинном коде. Так передаются
параметры из одних процедур в другие. Для того, чтобы все это
хорошо работало, программист обычно отводит солидный блок
памяти (несколько сотен байтов) для хранения в нем таких
программных переменных и строго следит, чтобы ничто этот блок
во время работы программы не затерло.
Неудобство вполне очевидно - надо все время помнить, какой
переменной какая ячейка памяти отведена, что откуда брать и ку-
да класть результат, а также следить, чтобы временно использо-
ванные и по каким-то причинам измененные значения, своевременно
восстанавливались и инициализировались. В принципе авторы выда-
ющихся программ ведут целые досье (картотеки) на свои подпро-
граммы и на использованные для их работы переменные.
Если для программ, целиком написанных в машинном коде, это
неудобство является необходимым злом, то для нашего случая,
когда мы объединяем БЕЙСИК и машинный код в одной структуре,
есть элегантный прием для передачи параметров, основанный на
использовании в БЕЙСИКе функций, определенных пользователем и
мы его сейчас рассмотрим, но сначала остановимся на вопросе о
том, как хранятся в БЕЙСИКе функции пользователя.
ВНИМАНИЕ! Перед тем, как начинать работу с этой главой,
необходимо внимательно изучить раздел 2.9.
3.1. Стандартный формат +-----------------+
функции пользователя. ¦ ЫЯЯЯЯЬ ЯЫЯ ¦
¦ Ы Ы Ы ¦
Давайте немного поэкспериментируем. ¦ ЫЯЯЯЯЬ Ы ¦
Наберите на БЕЙСИКе следующую программу: ¦ Ы Ы Ь ЬЫЬ Ь ¦
+-----------------+
10 DEF FN a(x,y,z)=x+y+z
20 FOR i=23755 TO 24000
30 PRINT i, PEEK i
40 NEXT i
Вы, очевидно знаете, что начиная с адреса 23755 в памяти
компьютера располагается текст БЕЙСИК-программы, поэтому выше-
приведенная программа делает ничто иное, как распечатывает этот
текст байт за байтом. Результат ее работы будет следующим:
23755 0 Дї
23756 10 ДЩ - номер строки БЕЙСИКа;
23757 34 Дї
23758 0 ДЩ - длина этой строки (34 байта);
23759 206 - код оператора DEF FN;
23760 97 - код буквы a;
23761 40 - код открывающей скобки;
23762 120 - код буквы x;
23763 14 - код, свидетельствующий, что следующие 5
байтов выражают некоторое действительное
число, записанное в интегральной форме;
23764 121 Дї
23765 44 Ё полная "абракадабра", совершенно непо-
23766 122 ГД - нятно, что это за число. То ли очень
23767 41 Ё большое, то ли очень маленькое.
23768 120 ДЩ
23769 44 - код запятой;
23770 121 - код буквы y;
................... и так далее.
Давайте поставим второй эксперимент:
10 DEF FN a(x,y,z)=x+y+z
15 LET test=FN a(3,8,5*2-7)
20 FOR i=23755 TO 24000
30 PRINT i, PEEK i
40 NEXT i
Мы добавили в программу строку 15, а теперь опять распе-
чатаем текст строки 10, дав команду RUN. Вы получите результат:
...........................................
23762 120 - код буквы x;
23763 14
23764 0 Дї
23765 0 Ё
23766 3 ГД - код числа 3.
23767 0 Ё
23768 0 ДЩ
23769 44 - код запятой;
23770 121 - код буквы y;
23771 14
23772 0 Дї
23773 0 Ё
23774 8 ГД - код числа 8.
23775 0 Ё
23776 0 ДЩ
................... - и т.далее
То, что мы сейчас увидели - довольно интересный результат.
Фактически наличие в программе строки с номером 15 изменило
содержание строки 10. С таким нечасто удается сталкиваться. А
происходит это потому, что в "Спектруме", когда Вы задаете
определение функции DEF FN и указываете ее параметры x,y,z,...
и пр., за каждым именем параметра сразу резервируются 5 байтов,
в которых впоследствии будут располагаться числовые значения
этих параметров.
Как только встретился вызов функции FN в строке 15, эти
параметры были вычислены и подставлены или просто подставлены,
если их не надо вычислять, в забронированное место в строке 10.
Таким образом, непосредственно в БЕЙСИК-строке организован
буфер, в котором хранятся текущие значения параметров. Сравните
с программными переменными, текущие значения которых хранятся в
специально отведенной области памяти, на которую указывает
системная переменная VARS (23627 = 5C4B).
Так как ко времени вычисления функции пользователя пара-
метры расставлены по своим местам, то их можно было бы исполь-
зовать для передачи данных в машинный код, если бы удалось
точно определить адреса этих мест или привязать их к чему-либо.
И эта возможность имеется.
В наборе системных переменных компью- +-----------------+
тера есть системная переменная DEFADD ¦ ЬЬЬЬ ЬЬЬ ¦
(23563 = 5C0BH), которая в момент расчета ¦ Ы Ы Ы ¦
функции пользователя содержит в себе адрес, ¦ ЯЬ Ы ¦
с которого начинаются параметры этой функ- ¦ ЯЬ Ы ¦
ции. В прочие моменты времени там хранится ¦ Ы Ы Ы ¦
0. Если бы Вам удалось распечатать ее ¦ ЯЯЯЯ Я ЯЯЯ Я ¦
содержимое в момент расчета фунуции FN a,то +-----------------+
Вы получили бы 23762, т.е. она указывает на имя первого пара-
метра - "x". А дальше все просто. Если мы будем передавать че-
рез параметры процедуры целые числа, лежащие в интервале от 0
до 255, то первое число будет находиться по адресу (DEFADD)+4,
там сейчас стоит число "3", второе - (DEFADD)+12 - там сечас
стоит параметр y, равный восьми, третье - (DEFADD)+20 и т.д. с
шагом по восемь байтов (один байт на букву, обозначающую имя
этого параметра, один байт на запятую, разделяющую параметры,
один байт на символ CHR14 и пять байтов на интегральную форму
числа.)
Так можно использовать передачу параметров из БЕЙСИКа в
машинный код через параметры функции пользователя. В нашем
случае мы будем передавать целые числа от 0 до 255, т.е. один
байт, но конечно можно передавать и любые действительные числа,
что делает этот метод наиболее удобным. В этом случае передан-
ная пятибайтная форма готова для обработки в калькуляторе.
3.2 Очистка заданного окна экрана.
Мы разберм эту процедуру самым подробнейшим образом. В от-
личие от обычной процедуры CLS, она позволяет очищать не весь
экран, а заданное окно, параметры которого выставляются в
БЕЙСИКе в функции FN a(x,y,h,v). В качестве начального адреса
процедуры мы выбрали 63000, ее длина - 92 байта и еще четыре
байта в конце процедуры отведены для хранения рабочих парамет-
ров. Список параметров:
x - горизонтальная координата левого верхнего угла окна,
подлежащего очистке (задается в знакоместах 0...31);
y - вертикальная координата левого верхнего угла окна,
подлежащего очистке (задается в знакоместах 0...23);
h - горизонтальный размер окна в знакоместах (h+x<32);
v - вертикальный размер окна в знакоместах (v+y<24).
Эта процедура может обслуживать весь экран, то есть все
двадцать четыре символьных ряда, а не только главную его часть,
т.е. 22 ряда. Если Вы зададите начальный параметр y больше, чем
23, процедура вернется в БЕЙСИК не работая, но на параметры x,
h и v перехват ошибок не поставлен, чтобы не усложнять машинный
код, так что здесь ошибка может привести к зависанию или сбросу
компьютера, впрочем, при практическом использовании такой про-
цедуры Вы сможете поставить перехват например в БЕЙСИКе перед
вызовом FN a.
10 REM *** Загрузчик машинного кода
20 LET adr=63000: LET long=95: LET z=0
30 FOR i=0 TO long-1: READ a
40 POKE (adr+i),a: LET z=z+a
50 NEXT i
60 LET z=INT (((z/long)-INT (z/long))*long)
70 READ a
80 IF a<>z THEN PRINT "??": STOP
90 REM
100 REM *** Пример использования процедуры
110 DEF FN a(x,y,h,v)=USR 63000
120 BORDER 4: PAPER 1: INK 6: CLS
130 FOR n=0 TO 703
140 PRINT "ЯЬ";
150 NEXT n
160 PAUSE 100
170 FOR n=1 TO 5
180 RANDOMIZE FN a(n*6-5,n*3-1,5,5)
190 NEXT n
200 REM *** Данные для машинного кода
210 DATA 42, 11, 92, 1, 4
220 DATA 0, 9, 86, 1, 8
230 DATA 0, 9 94, 237, 83
240 DATA 116, 246, 9, 86, 9
250 DATA 94, 237, 83, 118, 246
260 DATA 237, 91, 116, 246, 123
270 DATA 254, 23, 240, 237, 83
280 DATA 116, 246, 123, 230, 24
290 DATA 246, 64, 103, 123, 230
300 DATA 7, 183, 31, 31, 31
310 DATA 31, 130, 111, 58, 118
320 DATA 246, 71, 197, 229, 6
330 DATA 8, 197, 229, 58, 119
340 DATA 246, 71, 175, 119, 35
350 DATA 16, 252, 225, 193, 36
360 DATA 16, 240, 225, 193, 62
370 DATA 32, 133, 111, 48, 4
380 DATA 62, 8, 132, 103, 16
390 DATA 222, 201, 0, 0, 0
400 DATA 82
Дисассемблер машинного кода представлен ниже. Таким обра-
зом, фактически здесь и далее Вы имеете распечатку каждой про-
цедуры, повторенную трижды. Первый раз в десятиричном виде в
строках DATA, второй раз - то же самое в шестнадцатиричном коде
и третий раз - в виде мнемоник АССЕМБЛЕРа. Такой тройной повтор
сделан не только для того, чтобы каждый мог работать с тем ко-
дом, который ему удобнее, но и из соображений надежности, в ка-
честве дополнительной меры по борьбе с опечатками.
На первом этапе процедура принимает параметры x,y,h и v и
перебрасывает их в ячейки 63092...63095.
63000 2A0B5C LD HL,(5C0BH) ;DEFADD - системная пере-
;менная, указывающая на
;то, где находятся пара-
;метры функции пользова-
;теля. Ее адрес 5C0BH
;(23563).
63003 010400 LD BC,0004 ;Сдвиг от DEFADD на 4 бай-
63006 09 ADD HL,BC ;та (см. выше).
63007 56 LD D,(HL) ;Координата x.
63008 010800 LD BC,0008 ;Сдвиг от DEFADD еще на 8
63012 09 ADD HL,BC ;байтов (см. выше).
63013 5E LD E,(HL) ;Координата y.
63014 ED5374F6 LD(COORD),DE ;Переброска параметров y
;и x в адреса 63092,63093.
63018 09 ADD HL,BC ;Еще сдвиг на 8 байтов.
63019 56 LD D,HL ;Ширина окна.
63020 09 ADD HL,BC ;Еще сдвиг на 8 байтов.
63021 5E LD E,(HL) ;Высота окна.
63022 ED5376F6 LD(PARAM),DE ;Переброска параметров v
;и h в адреса 63094,63095.
Теперь процедура проверяет не выходит ли параметр y за
допустимый предел и начинает вычислять адрес в экранной области
памяти, соответствующий координатам левого верхнего угла
заданного окна.
63026 ED5B74F6 LD DE,(F674) ;Координаты x и y.
63030 7B LD A,E ;Координата y.
63031 FE17 CP 17 ;Проверка на <=23.
63033 F0 RET P ;Выход, если больше.
63034 ED5374F6 LD(F674),DE ;Координаты x и y.
63038 7B LD A,E ;Координата y.
63039 E618 AND 18 ;є Расчет адреса
63041 F6 OR 40 ;є по координатам.
63042 67 LD H,A ;є
63043 7B LD A,E ;є
63044 E607 AND 07 ;є
63046 B7 OR A ;є
63047 1F RRA ;є
63048 1F RRA ;є
63049 1F RRA ;є
63050 1F RRA ;є
63051 82 ADD A,D ;є
63052 6F LD L,A ;є
Теперь регистры H и L полностью соответствуют рис. 16 на
стр. 86, т.е. мы выставили в HL адрес дисплейной памяти, со-
ответствующий нулевой линии левого верхнего угла окна. Наша
следующая задача - непосредственная очистка окна, которую мы
будем делать, засылая нули в ячейки памяти, соответствующие
линиям входящих в окно знакомест.
Здесь нам придется организовать несколько вложенных цик-
лов. Первый цикл (внешний) - по вертикали, то есть по рядам от
1 до параметра v. Второй цикл (средний) - по линиям в ряду - от
1 до 8. И третий цикл (внутренний) - по горизонтали, то есть по
столбцам - от 1 до параметра h.
63053 3A76F6 LD A,(F676) ;Параметр v
63056 47 LD B,A ;В регистре "В" он будет
;счетчиком цикла.
63057 C5 LOOP_V PUSH BC ;Сохранили его на стеке
63058 E5 PUSH HL ;Сохранили текущий адрес.
63059 0608 LD B,08 ;Счетчик цикла по линиям.
63061 C5 LOOP_8 PUSH BC ;Сохранили его на стеке
63062 E5 PUSH HL ;Сохранили на стеке
;текущий адрес.
63063 3A77F6 LD A,(F677) ;Параметр h
63066 47 LD B,A ;Счетчик цикла по столбцам
63067 AF XOR A ;Это простейший способ об-
;нуления аккумулятора.
63068 77 LOOP_H LD(HL),A ;Начало цикла по столбцам.
;Очистка одной линии.
63069 23 INC HL ;Переход на соседнее зна-
;коместо враво.
63070 10FC DJNZ LOOP_H ;Конец цикла по столбцам.
63072 E1 POP HL ;Восстановление данных со
63073 C1 POP BC ;стека.
63074 24 INC H ;Переход к следующей линии
;в данном ряду.
63075 10F0 DJNZ LOOP_8 ;Конец цикла по линиям.
63077 E1 POP HL ;Восстановление данных со
63078 C1 POP BC ;стека.
63079 3E20 LD A,20 ;Переход
63081 85 ADD A,L ;на следующий
63082 6F LD L,A ;ряд.
63083 3004 JR NC,NO_SEG ;Здесь может возникнуть
;переход из сегмента в се-
;гмент. Если этого нет, то
;переход на метку NO_SEG.
63085 3E08 LD A,08 ;Корректировка
63087 84 ADD A,H ;в HL
63088 67 LD H,A ;номера сегмента.
63089 10DE NO_SEG DJNZ LOOP_V ;Конец цикла по рядам.
63091 C9 RET ;Выход из программы.
63092 0000 COORD DEFW 0000
63094 0000 PARAM DEFW 0000
3.3. Окрашивание заданного окна цветом INK.
Аналогично предыдущей процедуре, параметры заданного окна,
выставляются в БЕЙСИКе с помощью пользовательской функции
FN b(x,y,h,v,c,b,f). Начальным адресом взят для примера адрес
62800, длина процедуры - 127 байтов и еще пять байтов в конце
отведены для хранения рабочих переменных. Список параметров:
x - горизонтальная координата левого верхнего угла окна,
подлежащего очистке (задается в знакоместах 0...31);
y - вертикальная координата левого верхнего угла окна,
подлежащего очистке (задается в знакоместах 0...23);
h - горизонтальный размер окна в знакоместах (h+x<32);
v - вертикальный размер окна в знакоместах (v+y<24).
c - заданный цвет INK (0...7);
b - признак яркости BRIGHT (0,1);
f - признак мигания FLASH (0,1).
Эта процедура также может обслуживать весь экран - все 24
строки, а не только 22 строки основной его части.
10 REM *** Загрузчик машинного кода
20 LET adr=62800: LET long=130: LET z=0
30 FOR i=0 TO long-1: READ a
40 POKE (adr+i),a: LET z=z+a
50 NEXT i
60 LET z=INT (((z/long)-INT (z/long))*long)
70 READ a
80 IF a<>z THEN PRINT "??": STOP
90 REM
100 REM *** Пример использования процедуры
110 DEF FN b(x,y,h,v,c,b,f)=USR 62800
120 BORDER 0: PAPER 0: INK 4: CLS
130 FOR n=0 TO 703
140 PRINT "ЯЬ";
150 NEXT n
160 PAUSE 100
170 FOR n=1 TO 7
180 RANDOMIZE FN b(0,n*3-3,32,3,8-n,0,0)
190 NEXT n
200 FOR n=1 TO 7
210 RANDOMIZE FN b(n*4-1,0,2,22,n,0,0)
220 NEXT n
300 REM *** Данные для машинного кода
310 DATA 42, 11, 92, 1, 4
320 DATA 0, 9, 86, 1, 8
330 DATA 0, 9 94, 237, 83
340 DATA 210, 245, 9, 86, 9
350 DATA 94, 237, 83, 208, 245
360 DATA 9, 126, 230, 7, 50
370 DATA 207, 245, 9, 126, 230
380 DATA 1, 40, 8, 58, 207
390 DATA 245, 246, 64, 50, 207
400 DATA 245, 9, 126, 230, 1
410 DATA 40, 8, 58, 207, 245
420 DATA 246, 128, 50, 207, 245
430 DATA 237, 91, 210, 245, 58
440 DATA 208, 245, 254, 0, 200
450 DATA 237, 83, 210, 245, 123
460 DATA 230, 24, 203, 63, 203
470 DATA 63, 203, 63, 246, 88
480 DATA 103, 123, 230, 7, 183
490 DATA 31, 31, 31, 31, 130
500 DATA 111, 58, 208, 245, 71
510 DATA 197, 229, 58, 209, 245
520 DATA 71, 126, 230, 56, 79
530 DATA 58, 207, 245, 177, 119
540 DATA 35, 16, 244, 225, 1
550 DATA 32, 0, 9, 193, 16
560 DATA 230, 201, 0, 0, 0
570 DATA 46, 0
Основным отличием этой процедуры от предыдущей является
то, что она демонстрирует не операции с памятью дисплейного
файла, а операции с файлом атрибутов. Теперь начальный адрес в
HL выставляется в соответствии с файлом атрибутов (см. рис.17
на стр. 89).
Здесь для выполнения работы достаточно не трех циклов, как
при обслуживании дисплейного файла, а только двух - внешнего
цикла по вертикали (параметр v) и внутреннего - по горизонтали
(параметр h), т.к. цикл по восьми линиям уже не нужен.
Обратите внимание на то, что под три параметра c,b и f
отводится всего одна ячейка памяти. В ней параметру c отведены
биты 0,1,2, параметру b - бит 6 и параметру f - бит 7. Все в
полном соответствии с раскладкой атрибутов по битам (рис. 8).
Дисассемблер программы:
62800 2A0B5C LD HL,(5C0BH) ;См. с. 109...111?
62803 010400 LD BC,0004 ;Сдвиг от DEFADD на 4 бай-
62806 09 ADD HL,BC ;та (см. c. 109...111).
62807 56 LD D,(HL) ;Координата x.
62808 010800 LD BC,0008 ;Сдвиг от DEFADD еще на 8
62811 09 ADD HL,BC ;байтов (см.c.109...111)
62812 5E LD E,(HL) ;Координата y.
62813 ED53D2F5 LD(COORD),DE ;Переброска параметров y
;и x в адреса 62930,62931.
62817 09 ADD HL,BC ;Еще сдвиг на 8 байтов.
62818 56 LD D,HL ;Ширина окна.
62819 09 ADD HL,BC ;Еще сдвиг на 8 байтов.
62820 5E LD E,(HL) ;Высота окна.
62821 ED53D0F5 LD(PARAM),DE ;Переброска параметров v
;и h в адреса 62928,62929.
62825 09 ADD HL,BC ;Следующий параметр (INK)
62826 7E LD A,(HL) ;помещен в аккумулятор.
62827 E607 AND 07 ;и переправлен в отведен-
62829 32CFF5 LD (INK),A ;ную ему ячейку 62927.
62832 09 ADD HL,BC ;Параметр (BRIGHT)
62833 7E LD A,(HL) ;принимается,
62834 E601 AND 01 ;выделяется,
62836 2808 JR Z,SKIP_1 ;и, если не равен нулю,
62838 3ACFF5 LD A,(INK) ;то в параметре INK
62841 F640 OR 40 ;включается 6-ой бит
62843 32CFF5 LD (INK),A ;и INK сохраняется.
62846 09 SKIP_1 ADD HL,BC ;Параметр (FLASH)
62847 7E LD A,(HL) ;принимается,
62848 E601 AND 01 ;выделяется,
62850 2808 JR Z,SKIP_2 ;и, если не равен нулю,
62852 3ACFF5 LD A,(INK) ;то в параметре INK
62855 F680 OR 80 ;включается 7-ой бит
62857 32CFF5 LD (INK),A ;и INK сохраняется.
62860 ED5BD2F5 SKIP_2 LD DE,(COORD) ;Координаты x,y.
62864 3AD0F5 LD A,(F5D0) ;Высота окна (v)
62867 FE00 CP 00 ;Проверка высоты на ноль.
62869 C8 RET Z ;Выход, если так
62870 ED53D2F5 LD (COORD),DE ;Координаты y,x.
62874 7B LD A,E є
62875 E618 AND 18 є
62877 CB3F SRL A є Расчет адреса по
62879 CB3F SRL A є координатам.
62881 CB3F SRL A є
62883 F658 OR 58 є
62885 67 LD H,A є
62886 7B LD A,E є
62887 E607 AND 07 є
62889 B7 OR A є
62890 1F RRA є
62891 1F RRA є
62892 1F RRA є
62893 1F RRA є
62894 82 ADD A,D є
62895 6F LD L,A є
62896 3AD0F5 LD A,(F5D0) ;Параметр v
62899 47 LD B,A ;становится параметром
62900 C5 LOOP_V PUSH BC ;цикла и сохраняется на
62901 E5 PUSH HL ;стеке вместе с адресом.
62902 3AD1F5 LD A,(F5D1) ;Параметр h становится
62905 47 LD B,A ;параметром цикла.
62906 7E LOOP_H LD A,(HL) ;Сняли с экрана атрибуты
;знакоместа.
62907 E638 AND 38 ;Оставили включенными
;только биты, отвечающие
;за цвет PAPER.
62909 4F LD C,A ;Временно запомнили в "С".
62910 3ACFF5 LD A,(INK) ;Параметры c,b,f.
62913 B1 OR C ;Наложили их на PAPER.
62914 77 LD (HL),A ;Изменили атрибуты.
62915 23 INC HL ;Следующее знакоместо.
62916 10F4 DJNZ LOOP_H ;Конец цикла по горизон-
;тали.
62918 E1 POP HL ;Адрес начала ряда.
62919 012000 LD BC,0020 ;Переход на
62922 09 ADD HL,BC ;следующий ряд.
62923 C1 POP BC ;Параметр цикла по v.
62924 10E6 DJNZ LOOP_V ;Конец цикла по v.
62926 C9 RET ;Выход.
62927 00 INK DEFB 00 ;Атрибуты
62928 0000 PARAM DEFW 0000 ;Параметры v и h.
62930 0000 COORD DEFW 0000 ;Параметры y и x.
3.4. Окрашивание заданного окна цветом PAPER.
Процедура работает совершенно аналогично предыдущей, за
исключением того, что вместо цвета INK, в окне устанавливается
заданный цвет PAPER. Параметры окна выставляются в БЕЙСИКе с
помощью пользовательской функции FN c(x,y,h,v,c,b,f). Началь-
ным адресом взят для примера адрес 62600, длина процедуры -
139 байтов и еще пять байтов в конце отведены для хранения
рабочих переменных. Список параметров тот же, что и в предыду-
щей процедуре (см.с. 116), единственное отличие состоит в том,
что параметр "c" содержит информацию о цвете PAPER (от 0 до 7),
а не INK, как было ранее.
Эта процедура также может обслуживать весь экран - все 24
строки, а не только 22 строки основной его части.
10 REM *** Загрузчик машинного кода
20 LET adr=62600: LET long=145: LET z=0
30 FOR i=0 TO long-1: READ a
40 POKE (adr+i),a: LET z=z+a: NEXT i
60 LET z=INT (((z/long)-INT (z/long))*long)
70 READ a
80 IF a<>z THEN PRINT "??": STOP
90 REM *** Пример использования процедуры
100 DEF FN c(x,y,h,v,c,b,f)=USR 62600
110 BORDER 1: PAPER 4: CLS
120 FOR i=1 TO 120
130 LET x1=INT (RND*17)
140 LET y1=INT (RND*10)
150 LET h1=INT (RND*16)
160 LET v1=INT (RND*15)
170 LET c1=INT (RND*7)
180 RESTORE FN c(x1,y1,h1,v1,c1,0,0)
190 NEXT i: PAUSE 0 : REM пауза до нажатия клавиши.
200 REM *** Данные для машинного кода
210 DATA 42, 11, 92, 1, 4
220 DATA 0, 9, 86, 1, 8
230 DATA 0, 9 94, 237, 83
240 DATA 22, 245, 9, 86, 9
250 DATA 94, 237, 83, 20, 245
260 DATA 9, 126, 230, 7, 203
270 DATA 39, 203, 39, 203, 39
280 DATA 50, 19, 245, 9, 126
290 DATA 230, 1, 40, 8, 58
300 DATA 19, 245, 246, 64, 50
310 DATA 19, 245, 9, 126, 230
320 DATA 1, 40, 8, 58, 19
330 DATA 245, 246, 128, 50, 19
340 DATA 245, 237, 91, 22, 245
350 DATA 58, 20, 245, 254, 0
360 DATA 200, 58, 21, 245, 254
370 DATA 0, 200, 237, 83, 22
380 DATA 245, 123, 230, 24, 203
390 DATA 63, 203, 63, 203, 63
400 DATA 246, 88, 103, 123, 230
410 DATA 7, 183, 31, 31, 31
420 DATA 31, 130, 111, 58, 20
430 DATA 245, 71, 197, 229, 58
440 DATA 21, 245, 71, 126, 230
450 DATA 7, 79, 58, 19, 245
460 DATA 177, 119, 35, 16, 244
470 DATA 225, 1, 32, 0, 9
480 DATA 193, 16, 230, 201, 0
490 DATA 0, 0, 0, 0, 0
500 DATA 12
Демонстрационная программа выполняет печать случайных
цветных окон, окрашенных цветом PAPER. Мы бы хотели обратить
Ваше особое внимание на то, что процедура ЙННННННННННННННННН"
вызывается не через RANDOMIZE USR, а через є ЫЯЯЯЯЬ ЯЫЯ є
RESTORE USR. В чем здесь разница? С точки є Ы Ы Ы є
зрения самой процедуры, ей это все равно, є ЫЯЯЯЯЬ Ы є
для нее разницы нет, но при использовании є Ы Ы Ь ЬЫЬ Ь є
RANDOMIZE USR возникает косвенный эффект, ИНННННННННННННННННј
который состоит в том, что всякий раз, когда компьютер встреча-
ет оператор RANDOMIZE, он переустанавливает содержимое систем-
ной переменной SEED (23670=5C76H), содержащей базовую величину
для генерации случайных чисел. Если в большинстве случаев нам
это безразлично, то здесь мы используем случайные числа для
того, чтобы задавать параметры окрашиваемого окна (строки 130..
.170). Если бы на каждом шаге функция RANDOMIZE одинаково
переинициализировала бы нам системную переменную SEED, то мы
ничего нового на экране не увидели бы. Таким образом, если Вы
имеете дело со случайными числами, вычисляемыми через RND, то
использовать RANDOMIZE USR нельзя - пользуйтесь по обстоятель-
ствам RESTORE USR, PRINT USR и т.п.
Замечательный эффект в духе Малевича и Мондриана Вы
сможете получить, например, если используете эту процедуру со
следующей БЕЙСИк-программой. Подобные приемы придадут Вашим
программам неповторимый колорит.
100 DEF FN c(x,y,h,v,c,b,f)=USR 62600: BORDER 7: PAPER 7: CLS
110 RANDOMIZE FN c(4,10,1,14,0,0,0)
120 RANDOMIZE FN c(16,0,1,24,0,0,0)
130 RANDOMIZE FN c(24,0,1,24,0,0,0)
140 RANDOMIZE FN c(0,9,32,1,0,0,0)
150 RANDOMIZE FN c(0,17,32,1,0,0,0)
160 RANDOMIZE FN c(14,3,18,1,0,0,0)
170 RANDOMIZE FN c(0,0,16,9,4,1,0)
180 RANDOMIZE FN c(5,18,11,6,6,1,0)
190 RANDOMIZE FN c(25,0,7,9,2,1,0)
195 RANDOMIZE FN c(17,10,7,7,1,1,0): PAUSE 0
Дисассемблер программы:
62600 2A0B5C LD HL,(5C0BH) ;См. с.109...111.
62603 010400 LD BC,0004 ;Сдвиг от DEFADD на 4 бай-
62606 09 ADD HL,BC ;та (см. c.109...111 ).
62607 56 LD D,(HL) ;Координата x.
62608 010800 LD BC,0008 ;Сдвиг от DEFADD еще на 8
62611 09 ADD HL,BC ;байтов (см.c.109...111).
62612 5E LD E,(HL) ;Координата y.
62613 ED5316F5 LD(COORD),DE ;Переброска параметров y
;и x в адреса 62742,62743.
62617 09 ADD HL,BC ;Еще сдвиг на 8 байтов.
62618 56 LD D,HL ;Ширина окна.
62619 09 ADD HL,BC ;Еще сдвиг на 8 байтов.
62620 5E LD E,(HL) ;Высота окна.
62621 ED5314F5 LD(PARAM),DE ;Переброска параметров v
;и h в адреса 62740,62741.
62625 09 ADD HL,BC ;Параметр PAPER помещен в
62626 7E LD A,(HL) ;аккумулятор 00000???
62627 E607 AND 07 ;Выделение PAPER. 00000???
62629 CB27 SLA A ;Сдвиг влево. 0000???0
62631 CB27 SLA A ;Сдвиг влево. 000???00
62633 CB27 SLA A ;Сдвиг влево. 00???000
62635 3213F5 LD (PAPER),A ;
62638 09 ADD HL,BC ;Параметр (BRIGHT)
62639 7E LD A,(HL) ;принимается,
62640 E601 AND 01 ;выделяется,
62642 2808 JR Z,SKIP_1 ;и, если не равен нулю,
62644 3A13F5 LD A,(PAPER) ;то в параметре PAPER
62647 F640 OR 40 ;включается 6-ой бит
62649 3213F5 LD (PAPER),A ;и PAPER сохраняется.
62652 09 SKIP_1 ADD HL,BC ;Параметр (FLASH)
62653 7E LD A,(HL) ;принимается,
62654 E601 AND 01 ;выделяется,
62656 2808 JR Z,SKIP_2 ;и, если не равен нулю,
62658 3A13F5 LD A,(PAPER) ;то в параметре PAPER
62661 F680 OR 80 ;включается 7-ой бит
62663 3213F5 LD (PAPER),A ;и PAPER сохраняется.
62666 ED5B16F5 SKIP_2 LD DE,(COORD) ;Координаты x,y.
62670 3A14F5 LD A,(F514) ;Высота окна (v).
62673 FE00 CP 00 ;Проверка высоты на ноль.
62675 C8 RET Z ;Выход, если так
62676 3A15F5 LD A,(F515) ;Ширина окна (h).
62679 FE00 CP OO ;Проверка ширины на ноль.
62681 C8 RET Z ;Выход, если так
62682 ED5322F5 LD (COORD),DE ;Координаты y,x.
62686 7B LD A,E ¦
62687 E618 AND 18 ¦ Определение адреса
62689 CB3F SRL A ¦ по координатам.
62691 CB3F SRL A ¦
62693 CB3F SRL A ¦
62695 F658 OR 58 ¦
62697 67 LD H,A ¦
62698 7B LD A,E ¦
62699 E607 AND 07 ¦
62701 B7 OR A ¦
62702 1F RRA ¦
62703 1F RRA ¦
62704 1F RRA ¦
62705 1F RRA ¦
62706 82 ADD A,D ¦
62707 6F LD L,A ¦
62708 3A14F5 LD A,(F514) ;Параметр v
62711 47 LD B,A ;становится параметром
62712 C5 LOOP_V PUSH BC ;цикла и сохраняется на
62713 E5 PUSH HL ;стеке вместе с адресом.
62714 3A15F5 LD A,(F515) ;Параметр h становится
62717 47 LD B,A ;параметром цикла.
62718 7E LOOP_H LD A,(HL) ;Сняли с экрана атрибуты
;знакоместа.
62719 E607 AND 07 ;Оставили включенными
;только биты, отвечающие
;за цвет INK.
62721 4F LD C,A ;Временно запомнили в "С".
62722 3A13F5 LD A,(PAPER) ;Параметры c,b,f.
62725 B1 OR C ;Наложили их на INK.
62726 77 LD (HL),A ;Изменили атрибуты в
;знакоместе.
62727 23 INC HL ;Следующее знакоместо.
62728 10F4 DJNZ LOOP_H ;Конец цикла по горизон-
;тали.
62730 E1 POP HL ;Адрес начала ряда.
62731 012000 LD BC,0020 ;Переход на
62734 09 ADD HL,BC ;следующий ряд.
62735 C1 POP BC ;Параметр цикла по v.
62736 10E6 DJNZ LOOP_V ;Конец цикла по v.
62738 C9 RET ;Выход.
62739 00 PAPER DEFB 00 ;Атрибуты PAPER, BRIGHT,
;FLASH.
62740 0000 PARAM DEFW 0000 ;Параметры v и h.
62742 0000 COORD DEFW 0000 ;Параметры y и x.