3.7. Печать точек на экране.
До сих пор мы манипулировали с экранными образами, коорди-
наты которых были заданы в знакоместах, но как Вы знаете, это
не единственный способ. Для графики высокого разрешения более
естественен способ задания координат - в пикселах. При этом
считаем, что экран имеет 256 пикселов по горизонтали (от 0 до
255) и 176 пикселов по вертикали (от 0 до 175). И начинается
графика высокого разрешения с привязкой изображения по пикселам
- именно с печати на экране обычной точки.
Конечно, в ПЗУ компьютера есть процедура для печати точек
в заданных координатах (см. стр. 85) и можно было бы на этом
вопросе и не останавливаться, но поскольку построение многих
и многих изображений имеет в своей основе печать точек и на
этом основана вся векторная графика, в учебных целях необходимо
рассмотреть алгоритм работы такой процедуры и дать несколько
примеров ее применения.
Мы назовем предлагаемую процедуру FN f(x,y). Здесь x
(0...255) - координата пиксела по горизонтали, а y (0...175) -
по вертикали. Нам надо помнить, что эти координаты отсчитывают-
ся не так, как координаты в знакоместах. Начальной координатой
(0,0) является левый нижний угол экрана (а не левый верхний,
как было ранее). Соответственно, правый верхний угол экрана
имеет координату (255,175). Кроме того, надо также иметь в
виду, что когда мы говорим о левом нижнем угле экрана (0,0), то
мы не затрагиваем две самые нижние системные строки компьютера,
которые лежат еще ниже, чем нижняя строка основного экрана.
Фактически координата (0,0) находится над ними.
10 REM *** Загрузчик машинного кода
20 LET adr=61500: LET long=60: 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
500 REM ***Данные для машинного кода
510 DATA 42, 11, 92, 1, 4
520 DATA 0, 9, 86, 14, 8
530 DATA 9, 94, 62, 175, 147
540 DATA 216, 95, 167, 31, 55
550 DATA 31, 167, 31, 171, 230
560 DATA 248, 171, 103, 122, 7
570 DATA 7, 7, 171, 230, 199
580 DATA 171, 7, 7, 111, 122
590 DATA 230, 7, 71, 4, 62
600 DATA 254, 15, 16, 253, 6
610 DATA 255, 168, 71, 126, 176
620 DATA 119, 201, 0, 0, 0
630 DATA 24, 0, 0, 0, 0
Дисассемблер программы:
61500 2A0B5C LD HL,(5C0BH) ;См. с.109...111.
61503 010400 LD BC,0004 ;Сдвиг от DEFADD на 4 бай-
61506 09 ADD HL,BC ;та (см. c.109...111).
61507 56 LD D,(HL) ;Координата x.
61508 0E08 LD C,08 ;Сдвиг на
61510 09 ADD HL,BC ;восемь байтов.
61511 5E LD E,(HL) ¦
61512 3EAF LD A,0AFH ¦ Расчет адреса по
61514 93 SUB E ¦ координатам.
61515 D8 RET C ¦
61516 5F LD E,A ¦
61517 A7 AND A ¦
61518 1F RRA ¦
61519 37 SCF ¦
61520 1F RRA ¦
61521 A7 AND A ¦
61522 1F RRA ¦
61523 AB XOR E ¦
61524 E6F8 AND 0F8H ¦
61526 AB XOR E ¦
61527 67 LD H,A ¦
61528 7A LD A,D ¦
61529 07 RLCA ¦
61530 07 RLCA ¦
61531 07 RLCA ¦
61532 AB XOR E ¦
61533 E6C7 AND C7 ¦
61534 AB XOR E ¦
61536 07 RLCA ¦
61537 07 RLCA ¦
61538 6F LD L,A ¦
Теперь мы знаем номер сегмента, ряда, столбца и линии. Все
эти данные хранятся в регистре HL. Последнее, что осталось
сделать - это вспомнить, что данная линия в своем знакоместе
имеет 8 пикселов, а включить нужно только один из них -
необходимый. Фактически его номер определяется остатком от
деления координаты x на 8, а вычисляется этот остаток - путем
маскирования пяти старших битов, но есть еще один нюанс. Дело в
том, что координата x изменяется слева направо 0,1,2... 175, а
номера битов в байте, соответствующем найденной нами линии идут
наоборот 7,6,5.....0. Поэтому нужно сделать преобразование. В
аккумулятор вводится число FE (11111110), имеющее 0 в крайней
правой позиции, после чего делается вращение этого байта N+1
раз, где N -остаток от деления x на 8.
61539 7A LD A,D ;Координата x.
61540 E607 AND 07 ;Маскирование.
61542 47 LD B,A ;Остаток от деления x на 8
61543 04 INC B ; + 1
61544 3EFE LD A,FE ;Ввели байт 1111 1110
61546 0F LOOP RRCA ;Вращение вправо столько
61547 10FD DJNZ,LOOP ;раз, сколько установлено
;в регистре B.
61549 06FF LD B,0FFH ;Инверсия, чтобы печать
61551 A8 XOR B ;точки была черным по бе-
;лому, а не наоборот.
61552 47 LD B,A ;Запомнили в B.
61553 7E LD A,(HL) ;В аккумулятор приняли то,
;что уже содержится на эк-
;ране в нужной линии.
61554 B0 OR B ;Включаем требуемый бит
61555 77 LD (HL),A ;Включаем требуемый пик-
;сел.
61556 C9 RET ;Выход из процедуры.
Примеры использования процедуры.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Продемонстрируем работу приведенной выше процедуры на
некоторых примерах. Эти примеры написаны на БЕЙСИКе и
вставляются в приведенную выше БЕЙСИК-программу (стр.145, 146)
между строками 80 и 500.
"Экспонента".
~~~~~~~~~~~~~~~
100 DEF FN f(x,y) = USR 61500
110 BORDER 6: PAPER 6: INK 0: CLS
120 FOR n=1.19 TO 1.80 STEP 0.01
130 FOR x= 0 TO 22 STEP 0.5
140 LET z= INT(x^n)
150 LET y= INT (x*8)
160 RANDOMIZE FN f(z,y)
170 RANDOMIZE FN f(255-z, 168-y)
180 NEXT x
190 NEXT n
"Планета"
~~~~~~~~~
100 DEF FN f(x,y) = USR 61500
110 BORDER 0: PAPER 0: INK 4: CLS
120 LET r=60: LET xc=127: LET yc=88
130 GO SUB 400
140 LET r=20: LET xc=75: LET yc=100
150 GO SUB 400
160 STOP
400 FOR y=-r TO r
410 FOR x1 = INT (SQR (r*r-y*y)
420 FOR x=-x1 TO x1
430 LET n=INT (RND*(1)*x1*2)+1
440 IF n
170 POKE k+i,13
175 REM ***** Печать звезд в 74-х верхних линиях экрана
180 FOR i=0 TO 298 STEP 2
190 LET x1=INT (255*RND)
200 LET y1=174-(INT(74*RND))
210 RESTORE FN f(x1,y1)
220 NEXT i
230 RANDOMUZE FN c(0,14,25,15,1,0,0)
235 REM ***** Изображение зданий заднего плана
240 FOR i=1 TO 50
250 RANDOMIZE
260 LET h1=2+INT (RND*4)
270 LET y1=10+INT(RND*15)
280 LET v1=25-y1
290 LET x1=INT(RND*27)
300 LET c1=2+INT (RND*6)
310 RANDOMIZE FN c(x1,y1,h1,v1,c1,0,0)
320 NEXT i
325 REM ***** Изображение зданий переднего плана
330 RANDOMIZE FN c(16,9,1,15,4,1,0)
340 RANDOMIZE FN c(17,6,2,18,4,1,1)
350 RANDOMIZE FN c(19,10,1,14,4,1,0)
360 RANDOMIZE FN b(17,6,2,18,1,0,1)
370 RANDOMIZE FN e(17,6)
380 RANDOMIZE FN c(1,11,5,14,2,0,0)
385 REM ****** Изображение окон в зданиях
390 FOR i=12 TO 22 STEP 2
400 RANDOMIZE FN c(2,i,1,1,7,1,0)
410 RANDOMIZE FN c(4,i,1,1,6,1,0)
420 NEXT i
430 RANDOMIZE FN c(23,18,9,7,3,0,0)
440 FOR i=24 TO 30 STEP 2
450 RANDOMIZE FN c(i,19,1,1,4,1,0)
460 RANDOMIZE FN c(i,21,1,1,4,1,0)
470 NEXT i
475 REM ***** Изображение метеора
480 INK 6: BRIGHT 1
490 FOR i=-6 TO 6 STEP 2
500 PLOT 250,165-i
510 DRAW -70,i-40: NEXT i
520 INK 7
525 REM ****** Изображение полумесяца
530 FOR i= 0 TO 7
540 PLOT 20+i,150
550 DRAW 0,-50,0.8*PI
560 NEXT i
570 PAUSE 0
3.8. Изображение линий.
Известный оператор БЕЙСИКа DRAW служит для рисования от-
резков прямых и дуг окружностей. Вам известно, что для того,
чтобы был нарисован нужный отрезок, предварительно должна быть
задана (PLOT) исходная точка и указаны относительные координаты
конца отрезка. Задание относительных координат (приращений по
горизонтали и вертикали) во многих случаях является не очень
удобным, а порой и просто трудоемкой операций, т.к. программис-
ту требуется сначала рассчитать эти приращения. Гораздо удобнее
было бы вводить абсолютные экранные координаты конца отрезка,
что и позволяет сделать предлагаемая здесь процедура FN g
(x,y,p,q).
x,y - координаты начала отрезка (x<256, y<176);
p,q - координаты конца отрезка (p<256, q<176)
Эта процедура работает много быстрее, чем традиционная
команда DRAW и быстрее, чем машиннокодовая процедура, содержа-
щаяся в ПЗУ для этой цели (т.к. последняя в своей работе ис-
пользует операции со встроенным калькулятором, а он ведет свои
расчеты тоже при поддержке процедур ПЗУ). Приведенная процедура
выполняет некоторые проверки, исключающие ввод координат, выхо-
дящих за пределы экрана, что позволяет практиковать при исполь-
зовании процедуры метод проб и ошибок без угрозы потерять и
БЕЙСИК-программу и процедуру вследствие зависания компьютера.
Тем не менее, если введенная Вами координата будет не только
выходить за пределы экрана, но при этом еще и выходить более,
чем на 255 пикселов, есть вероятность того, что программа может
зависнуть.
Для нас процедура рисования линий - это первый шаг в на-
правлении так называемой векторной графики. Многочисленные
поклонники программы ELITE знают, как выглядят такие изображе-
ния на экране. Прежде, чем заняться разбором машинного кода
процедуры, мы в нескольких словах укрупненно рассмотрим логику
ее работы.
Построение линии осуществляется точка за точкой, начиная
от исходной координаты. Каждый шаг выполняется со смещением не
более, чем на один пиксел как по горизонтали, так и по вертика-
ли до тех пор, пока не будет достигнута конечная точка.
1. Сначала вычисляются вектора от начальной точки до
конечной. Если и по x и по y приращения положительные, то в
специальной переменной SIGN выставляются +1 для x и +1 для y.
Если же какое-то из этих смещений отрицательное, то для него
выставляется -1 (то есть FF).
2. Проблемой является тот факт, что если одно из прираще-
ний больше другого, то его координата должна изменяться на каж-
дом шаге, а для меньшего - не на каждом. Чтобы это было так,
кроме SIGN вводится еще контрольная пара BC. В ней тому прира-
щению, которое больше по абсолютной величине, соответствует 01
или FF, а меньшему - 0. И теперь при построении новой точки шаг
по координатам берется то из SIGN, то из BC. Чем сильнее по
абсолютной величине отличаются приращения по x и y, тем чаще
построение новой точки делается с шагом, взятым из BC, когда
отрабатывается только одна координата, а вторая остается
неизменной. Эта логика организована в адресах 60782 - 60789.
3. В остальном логика работы процедуры достаточно обычна.
Отметим только, что сама печать точек по координатам, находя-
щимся в DE выполняется процедурой PLOT (60851), которая анало-
гична разобранной в предыдущем разделе.
10 REM *** Загрузчик машинного кода
20 LET adr=60700: LET long=210: 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
500 REM ***Данные для машинного кода
510 DATA 42, 11, 92, 1, 4
520 DATA 0, 9, 86, 14, 8
530 DATA 9, 94, 237, 83, 26
540 DATA 237, 205, 226, 237, 94
550 DATA 42, 26, 237, 217, 229
560 DATA 217, 237, 115, 175, 237
570 DATA 1, 1, 1, 122, 148
580 DATA 210, 70, 237, 6, 255
590 DATA 237, 68, 87, 123, 149
600 DATA 210, 80, 237, 14, 255
610 DATA 237, 68, 95, 122, 187
620 DATA 48, 10, 106, 237, 67
630 DATA 177, 237, 175, 71, 195
640 DATA 107, 237, 178, 202, 167
650 DATA 237, 107, 90, 237, 67
660 DATA 177, 237, 14, 0, 99
670 DATA 123, 31, 133, 218, 118
680 DATA 237, 188, 218, 128, 237
690 DATA 148, 87, 217, 237, 91
700 DATA 177, 237, 195, 132, 237
710 DATA 87, 197, 217, 209, 42
720 DATA 26, 237, 123, 133, 95
730 DATA 122, 60, 132, 218, 164
740 DATA 237, 202, 167, 237, 61
750 DATA 87, 237, 83, 26, 237
760 DATA 205, 179, 237, 217, 122
770 DATA 29, 32, 205, 195, 167
780 DATA 237, 202, 147, 237, 237
790 DATA 123, 175, 237, 217, 225
800 DATA 217, 201, 181, 214, 1
810 DATA 1, 62, 175, 147, 218
820 DATA 249, 36, 95, 167, 31
830 DATA 55, 31, 167, 31, 171
840 DATA 230, 248, 171, 103, 122
850 DATA 7, 7, 7, 171, 230
860 DATA 199, 171, 7, 7, 111
870 DATA 122, 230, 7, 71, 4
880 DATA 62, 254, 15, 16, 253
890 DATA 6, 255, 168, 71, 126
900 DATA 176, 119, 201, 229, 197
910 DATA 205, 179, 237, 193, 225
920 DATA 9, 86, 9, 201, 0
930 DATA 192, 0, 0, 0, 0
Дисассемблер программы:
60698 COORD DEFW ;Здесь создается програм-
;мная переменная, хранящая
;координаты исходной точки
60700 2A0B5C LD HL,(5C0BH) ;См. с.109...111.
60703 010400 LD BC,0004 ;Сдвиг от DEFADD на 4 бай-
60706 09 ADD HL,BC ;та (см. c.109...111).
60707 56 LD D,(HL) ;Координата x.
60708 0E08 LD C,08 ;Сдвиг на
60710 09 ADD HL,BC ;восемь байтов.
60711 5E LD E,(HL) ;Координата y.
60712 ED531AED LD(COORD),DE ;Запомнили x,y
60716 CDE2ED CALL BEGIN ;Вызов подпрограммы BEGIN
;(60898), которая выполнит
;печать исходной точки.
60719 5E LD E,(HL) ;Ввод координаты q.
60720 2A1AED LD HL,(ED1A) ;Приняли координаты x и y.
60723 D9 EXX ;Сохранение на стеке сос-
60724 E5 PUSH HL ;тояния альтернативной па-
60725 D9 EXX ;ры HL, поскольку при
;работе программы она мо-
;жет быть коррумпирована.
60726 ED73AFED LD(STK_P),SP ;Запомнили состояние ука-
;зателя стека в соответс-
;твующей переменной.
60730 010101 LD BC,0101 ;Исходная установка для
;инициализации переменной
;SIGN (60849).
60733 7A LD A,D ;Параметр p.
60734 94 SUB H ;Вычли из него параметр x.
60735 D246ED JP NC,CONT_1 ;Если p>x, то регистр B
;менять не надо и перехо-
;дим на метку CONT_1.
60738 06FF LD B,0FFH ;В противном случае в B
;выставляем FF (минус еди-
;ницу).
60740 ED44 NEG ;Изменение знака содержи-
;мого аккумулятора, теперь
;в нем абсолютная величина
;приращения по x, а знак -
;в регистре B.
60742 57 CONT_1 LD D,A ;Запомнили в D абс.велич.
;приращения по x.
60743 7B LD A,E ;Параметр q.
60744 95 SUB L ;Вычли из него параметр y.
60745 D250ED JP NC,CONT_2 ;Если q>y, то регистр C
;менять не надо и перехо-
;дим на метку CONT_2.
60748 0EFF LD C,0FFH ;В противном случае в C
;выставляем FF (минус еди-
;ницу).
60750 ED44 NEG ;Изменение знака содержи-
;мого аккумулятора, теперь
;в нем абсолютная величина
;приращения по y, а знак -
;в регистре C.
60752 5F CONT_2 LD E,A ;Запомнили в E абс.велич.
;приращения по y.
60753 7A LD A,D ;Сравниваем по абсолютной
60754 BB CP E ;величине приращения x и
;y.
60755 300A JR NC,CONT_3 ;Если приращение по x
;больше, то переход на
;метку CONT_3.
60757 6A LD L,D ;Запомнили меньшее.
60758 ED43B1ED LD (SIGN),BC ;Запомнили знаки прира-
;щений в переменной SIGN.
60762 AF XOR A ;Обнуление аккумулятора.
60763 47 LD B,A ;Обнуление B (раз прираще-
;ние по x - меньшее, то
;первый шаг по нему будет
;нулевым.
60764 C36BED JP CONT_4 ;Переход на CONT_4 для
;продолжения работы.
Сюда мы попадаем только в том случае, если абсолютная ве-
личина приращения по x больше, чем приращения по y.
60767 B2 CONT_3 OR D ;Поскольку и в аккумуля-
;торе и в регистре D на-
;ходится приращение по x,
;то эта операция фактиче-
;ски является проверкой
;флагов.
60768 CAA7ED JP Z,FINISH ;Если приращение уже рав-
;но нулю, то конец рабо-
;ты и переход на FINISH.
60771 6B LD L,E ;Приращение по y.
60772 5A LD E,D ;Приращение по x.
60773 ED43B1ED LD (SIGN),BC ;Запомнили знаки прира-
;щений в переменной SIGN.
60777 OE00 LD C,00 ;Переинициализация C. (Раз
;приращение по y меньше,
;то первый шаг по нему
;будет нулевым.
В итоге манипуляций, проведенных в строках 60753 - 60777
мы имеем в регистре E то приращение (из x и y), которое имеет
большую абсолютную величину, назовем ее - max а в регистре L -
то, которое имеет меньшую величину, назовем ее min.
60779 63 CONT_4 LD H,E ;Большее из приращений.
60780 7B LD A,E ;Большее из приращений.
60781 1F RRA ;Деление пополам.
60782 85 REPEAT ADD A,L ;0.5max+min
60783 DA76ED JP C,CONT_5 ;Здесь переполнение воз-
;можно только если
;min>0.5max.
60786 BC CP H ;Здесь переполнение воз-
60787 DA80ED JP C,CONT_6 ;можно только если
;min<0.5max.
60790 94 CONT_5 SUB H ;Получили min-0.5max
60791 57 LD D,A ;Запомнили в D
60792 D9 EXX ;Переход на альтернативный
;набор регистров, чтобы не
;портить DE.
60793 ED5BB1ED LD DE,(SIGN) ;В DE - знаки приращений
;по x и y.
60797 C384ED JP CONT_7
60800 57 CONT_6 LD D,A ;Запомнили в D min+0.5 max
60801 C5 PUSH BC ;Переброска через стек
60802 D9 EXX ;знака приращений из BC в
60803 D1 POP DE ;альтернативную пару DE.
60804 2A1AED CONT_7 LD HL,(COORD) ;Текущие координаты пози-
;ции печати очередной
;точки.
60807 7B LD A,E ;Знак приращения по y.
60808 85 ADD A,L ;Переход к новой точке по
;вертикали.
60809 5F LD E,A ;Запомнили ее.
60810 7A LD A,D ;Знак приращения по x.
60811 3C INC A ;Проверка на возможность
60812 84 ADD A,H ;выхода за пределы экра-
60813 DAA4ED JP C,CONT_8 ;на.
60816 CAA7ED JP Z,FINISH ;Подготовка к выходу.
60819 3D CONT_9 DEC A ;Восстановили A после
;вышеуказанной проверки
60820 57 LD D,A ;и запомнили в D.
60821 ED531AED LD (COORD),DE ;Запомнили новую текущую
;координату позиции пе-
;чати точки.
60825 CDB3ED CALL PLOT ;Печать точки
60828 D9 EXX ;Возврат к основному на-
;бору регистров.
60829 7A LD A,D
60830 1D DEC E ;Уменьшение того прираще-
;ния, которое было макси-
;мальным.
60831 20CD JR NZ,REPEAT ;Если оно не 0, то нужно
;работать дальше.
60833 C3A7ED JP FINISH ;В противном случае рабо-
;та завершается.
60836 CA93ED CONT_8 JP Z,CONT_9 ;Предел по x - близок, но
;еще одну точку напечатать
;можно - переход на CONT_9
60839 ED7BAFED FINISH LD SP,(EDAF) ;Восстановление указателя
;стека.
60843 D9 EXX ;Восстановление состояния
60844 E1 POP HL ;альтернативной пары HL
60845 D9 EXX ;перед выходом.
60846 C9 RET ;Генеральный выход.
60847 STK_P DEFW ;В этой программной пере-
;менной организуется хра-
;нение значения указателя
;машинного стека - SP.
60849 SIGN DEFW ;Два байта этой переменной
;выполняют роль знаков
;приращения координаты.
;Если xp, то он равен FF
;или -1. Второй байт уста-
;навливается в +1 или в FF
;в зависимости от соотно-
;шения q и y.
Процедура PLOT служит для печати на экране тех точек, из
которых состоит наш отрезок. Поскольку она практически соответ-
ствует рассмотренной ранее процедуре FN f(x,y), мы не будем
подробно рассматривать логику ее работы.
При вызове этой процедуры в регистре D выставлена коорди-
ната x предполагаемой позиции печати, а в регистре E - коорди-
ната y.
60851 3EAF PLOT LD A,0AFH ;AF=175
60853 93 SUB E ;Проверка не выходит ли за
;пределы допуска (175) ко-
;ордината y.
60854 DAF924 JP C,24F9 ;Если так, то переход в
;ПЗУ на адрес 9465, где
;записан вызов процедуры
;обработки ошибок RST 08
;с кодом перехвата 0A, что
;дает ошибку "Integer out
;of range".
60857 5F LD E,A ;Дополнение y до 175.
60858 A7 AND A ;Сброс флага переноса.
60859 1F RRA ;Ротация вправо.
60860 37 SCF ;Установка флага переноса
60861 1F RRA ;Ротация вправо.
60862 A7 AND A ;Сброс флага переноса.
60863 1F RRA ;Ротация вправо.
60864 AB XOR E ;Комплексная
60865 E6F8 AND F8 ;операция замещения
60867 AB XOR E ;битов 0,1 и 2.
60868 67 LD H,A ;Сформировали старший байт
;адреса в дисплейном файле
60869 7A LD A,D ;Координата x
60870 07 RLCA ;Вращение влево.
60871 07 RLCA ;Вращение влево.
60872 07 RLCA ;Вращение влево.
60873 AB XOR E ;Операция замещения
60874 E6C7 AND C7 ;для битов 3,4,5.
60876 AB XOR E ;
60877 07 RLCA ;Вращение влево.
60878 07 RLCA ;Вращение влево.
60879 6F LD L,A ;Сформировали младший байт
;адреса в дисплейном файле
60880 7A LD A,D ;Координата x.
60881 E607 AND 07H ;Маскирование.
60883 47 LD B,A ;Инициализация счетчика
60884 04 INC B ;оборотов в регистре B.
60885 3EFE LD A,0FEH ;Байт FE=254 интересен
;тем, что все биты, кро-
;ме одного равны едини-
;це. Вот эту "дырку" мы
;и вращаем, пока она не
;встанет на свое место.
60887 0F AGAIN RRCA ;Вращение вправо.
60888 10FD DJNZ AGAIN ;Повтор вращения.
60890 06FF LD B,0FFH ;Инверсия, чтобы печать
60892 A8 XOR B ;точки была черным по бе-
;лому, а не наоборот.
60893 47 LD B,A ;Запомнили в B.
60894 7E LD A,(HL) ;В аккумулятор приняли то,
;что уже содержится на эк-
;ране в нужной линии.
60895 B0 OR B ;Включаем требуемый бит
60896 77 LD (HL),A ;Включаем требуемый пик-
;сел.
60897 C9 RET ;Выход из процедуры.
60898 E5 BEGIN PUSH HL ;Сохранение на стеке
60899 C5 PUSH BC ;регистров HL и BC.
60900 CDB3ED CALL PLOT ;Вызов процедуры PLOT
; (60851)
60903 C1 POP BC ;Восстановление со стека
60904 E1 POP HL ;регистров HL и BC.
60905 09 ADD HL,BC ;Сдвиг от DEFADD на 8 бай-
;тов.
60906 56 LD D(HL) ;Ввод координаты p.
60907 09 ADD HL,BC ;Сдвиг от DEFADD на 8 бай-
;тов.
60910 C9 RET ;Возврат в вызывающую про-
;грамму.
Примеры использования процедуры.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Продемонстрируем работу приведенной выше процедуры на
некоторых примерах. Эти примеры написаны на БЕЙСИКе и
вставляются в приведенную выше БЕЙСИК-программу (стр. 154)
между строками 80 и 500.
"Куб".
~~~~~~~
100 DEF FN g(x,y,p,q) = USR 60700
110 BORDER 1: PAPER 1: INK 6
120 CLS
130 FOR j=0 TO 116 STEP 8
140 RANDOMIZE FN g(56,24+j,168,24+j)
150 RANDOMIZE FN g(56+j,24,56+j,136)
160 RANDOMIZE FN g(56+j,136,96+j,160)
170 RANDOMIZE FN g(168,24+j,208,48+j)
180 NEXT j
190 RANDOMIZE FN g(96,160,208,160)
200 RANDOMIZE FN g(208,160,208,48)
"Закат".
~~~~~~~~~
Эта программа использует также процедуру FN c(),
служащую для окраски части экрана цветом PAPER. Машинный код,
соответствующий этой процедуре предварительно должен быть
подгружен в отведенную для него область памяти.
100 DEF FN c(x,y,h,v,c,b,f)=USR 62600
110 DEF FN g(x,y,p,q) = USR 60700
120 BORDER 0: PAPER 1: INK 6
130 CLS
140 RANDOMIZE FN c(0,0,32,17,2,0,0)
150 FOR j=40 TO 174 STEP 12
160 RANDOMIZE FN g(0,j,128,40)
170 RANDOMIZE FN g(255,j,128,40)
180 NEXT j
190 FOR j=6 TO 255 STEP 12
200 RANDOMIZE FN g(j,175,128,40)
210 NEXT j
220 FOR j=36 TO 0 STEP -3
230 RANDOMIZE FN g((10+j*3),j,(248-j*3),j)
240 NEXT j
"Пирамиды".
~~~~~~~~~~~
Процедура, изображающая отрезки прямых, очень хорошо
подходит для изображения интерференционных узоров. Они образу-
ются, когда смещения семейства отрезков настолько малы, что в
результате не образуются ни отдельные линии, ни сплошная окра-
шенная поверхность. Приведенная программа показывает, как это
происходит и демонстрирует возможность получения очаровательно-
го пейзажа в восточном стиле. Каждая пирамида рисуется вызовом
подпрограммы из строки 400. Интерференционная картина наблюда-
ется на гранях пирамид, что создает эффект трехмерной графики.
100 DEF FN b(x,y,h,v,c,b,f)=USR 62800
110 DEF FN c(x,y,h,v,c,b,f)=USR 62600
120 DEF FN g(x,y,p,q) = USR 60700
130 BORDER 0: PAPER 1: INK 2
140 CLS
150 RANDOMIZE FN c(0,8,31,14,6,0,0)
160 LET tx=80: LET ty=136: LET by=24: LET a=16: LET b=128
170 GO SUB 400
180 LET tx=156: LET by=60: LET a=102: LET b=182
190 GO SUB 400: STOP
400 FOR x=a TO b STEP 2
410 RANDOMIZE FN g(tx,ty,x,by)
420 NEXT x
430 FOR x=b TO b+16
440 RANDOMIZE FN g(tx,ty,x,by)
450 LET by=by+1
460 NEXT x: RETURN
"Плетенка".
~~~~~~~~~~~
Еще один интересный и до некоторой степени парадок-
сальный эффект можно получить изображением семейства прямых с
малым смещением от линии к линии. Так, при движении одного из
концов отрезков нескольких прямых в точках пересечения образу-
ются довольно сложные кривые линии, а штриховой характер изоб-
ражения придает им сходство со сложными трехмерными поверхнос-
тями.
100 DEF FN g(x,y,p,q) = USR 60700
110 BORDER 0: PAPER 6: INK 0
120 CLS
130 FOR i=14 TO 2 STEP -1
140 FOR j=0 TO 150 STEP i
150 RANDOMIZE FN g(50+j,0,j,175)
160 RANDOMIZE FN g(255,j,j,0)
170 RANDOMIZE FN g(50+j,150,255,150-j)
180 RANDOMIZE FN g(50+j,150,j,0)
190 NEXT j
200 PAUSE 0: CLS
210 NEXT i
"Интерференция".
~~~~~~~~~~~~~~~~
Интересные интерференционные картины на экране могут
быть получены простым увеличением количества печатаемых отрез-
ков прямых. Попробуйте поэкспериментировать с приведенной ниже
программой, меняя параметр цикла 255 в строке 140.
100 DEF FN g(x,y,p,q) = USR 60700
110 BORDER 0: PAPER 1: INK 6
120 CLS
130 FOR i=16 TO 6 STEP -1
140 FOR j=0 TO 255 STEP i
150 RANDOMIZE FN g(0,0,j,175)
160 RANDOMIZE FN g(255,175,j,0)
170 RANDOMIZE FN g(255,0,j,175)
180 RANDOMIZE FN g(0,175,j,0)
190 NEXT j
200 PAUSE 0: CLS
210 NEXT i
3.9. Изображение прямоугольников.
Изображение прямоугольника в БЕЙСИКе выполняется одной
командой PLOT и четырьмя командами DRAW. Например:
10 PLOT 100,100
20 DRAW 50,0
30 DRAW 0,50
40 DRAW -50,0
50 DRAW 0,50
Точно так же изображением четырех линий мы можем получить
прямоугольник из машинного кода. В общем блоке рассматриваемых
нами процедур эта процедура (назовем ее FN h(x,y,h,v)) имеет ту
отличительную особенность, что не является абсолютно автономной
и при работе вызывает процедуру изображения отрезков прямых
FN g(x,y,p,q). Эта процедура вызывается по адресу 60723, и
это предполагает, что координаты исходной точки установлены в
HL, а координаты точки назначения (абсолютные) - в DE.
Параметры: x,y - координаты левого верхнего угла;
h - ширина
v - высота прямоугольника.
10 REM *** Загрузчик машинного кода
20 LET adr=60400: LET long=105: 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
500 REM ***Данные для машинного кода
510 DATA 42, 11, 92, 1, 4
520 DATA 0, 9, 86, 14, 8
530 DATA 9, 94, 237, 83, 82
540 DATA 236, 9, 86, 9, 94
550 DATA 237, 83, 84, 236, 237
560 DATA 91, 82, 236, 58, 85
570 DATA 236, 130, 103, 50, 87
580 DATA 236, 107, 34, 26, 237
590 DATA 205, 51, 237, 237, 91
600 DATA 82, 236, 58, 84, 236
610 DATA 131, 50, 86, 236, 111
620 DATA 98, 34, 26, 237, 205
630 DATA 51, 237, 42, 86, 236
640 DATA 58, 83, 236, 87, 58
650 DATA 86, 236, 95, 34, 26
660 DATA 237, 205, 51, 237, 42
670 DATA 86, 236, 58, 82, 236
680 DATA 95, 44, 58, 87, 236
690 DATA 87, 34, 26, 237, 205
700 DATA 51, 237, 201, 30, 3
710 DATA 60, 60, 90, 63, 0
720 DATA 86, 0, 0, 0, 0
Дисассемблер программы:
60400 2A0B5C LD HL,(5C0BH) ;См. с. 109...111
60403 010400 LD BC,0004 ;Сдвиг от DEFADD на 4 бай-
60406 09 ADD HL,BC ;та (см. c.109...111).
60407 56 LD D,(HL) ;Координата x.
60408 0E08 LD C,08 ;Сдвиг на
60410 09 ADD HL,BC ;восемь байтов.
60411 5E LD E,(HL) ;Координата y.
60412 ED5352EC LD(Y_UP),DE ;Запомнили x,y.
60416 09 ADD HL,BC ;Сдвиг на 8 байтов.
60417 56 LD D,(HL) ;Параметр h.
60418 09 ADD HL,BC ;Сдвиг на 8 байтов.
60419 5E LD E,(HL) ;Параметр v.
60420 ED5354EC LD(HIGHT),DE ;Запомнили h,v.
60424 ED5B52EC LD DE,(Y_UP) ;Параметры x,y
60428 3A55EC LD A,(WIDTH) ;Приняли ширину пр-ка.
60431 82 ADD A,D ;Вычисляем x+h и
60432 67 LD H,A ;запоминаем в регистре H
60433 3257EC LD (X_RGHT),A ;и в переменной 60503.
60436 6B LD L,E ;Параметр y.
60437 221AED LD (COORD),HL ;Запоминаем координаты
;левого верхего угла в
;ячейке 60698, откуда они
;могут быть использованы
;процедурой FN g() для
;рисования верхней сторо-
;ны прямоугольника.
60440 CD33ED CALL ED33 ;Вызов процедуры FN g()
;осуществляется по
;адресу 60723. Рисуем вер-
;хнюю сторону.
60443 ED5B52EC LD DE,(Y_UP) ;Параметры x,y.
60447 3A54EC LD A,(HIGHT) ;Высота пр-ка.
60450 83 ADD A,E ;Определяем y+v и запоми-
60451 3256EC LD (Y_DOWN),A ;наем в переменной 60502
60454 6F LD L,A ;и в регистре L.
60455 62 LD H,D ;Параметр x.
60456 221AED LD (COORD),HL ;Запоминаем координаты
;левого нижнего угла в
;ячейке 60698.
60459 CD33ED CALL ED33 ;Рисуем левую сторону.
60462 2A56EC LD HL,(Y_DOWN) ;Координаты правого ниж-
;него угла.
60465 3A53EC LD A,(X_LEFT) ;Коорд. x левая.
60468 57 LD D,A
60469 3A56EC LD A,(Y_DOWN) ;Коорд. y нижняя
60472 5F LD E,A
60473 221AED LD (COORD),HL ;Правый нижний угол.
60476 CD33ED CALL ED33 ;Рисуем нижнюю сторону.
60479 2A56EC LD HL,(Y_DOWN) ;Координаты правого ниж-
;него угла.
60482 3A52EC LD A,(Y_UP) ;Коорд. y верхней стороны.
60485 5F LD E,A
60486 2C INC L
60487 3A57EC LD A,(X_RGHT) ;Коорд. x правой стороны.
60490 57 LD D,A
60491 221AED LD (COORD),HL ;Правый нижний угол.
60494 CD33ED CALL ED33 ;Рисуем правую сторону.
60497 C9 RET ;Выход.
60498 Y_UP DEFB ;Координата y верхней
;стороны.
60499 X_LEFT DEFB ;Координата x левой
;стороны.
60500 HIGHT DEFB ;Высота прямоугольника.
60501 WIDTH DEFB ;Ширина прямоугольника.
60502 Y_DOWN DEFB ;Координата y нижней
;стороны.
60503 X_RGHT DEFB ;Координата x правой
;стороны.
60698 COORD DEFW ;Переменная, служащая для
;связи с процедурой печа-
;ти прямых линий.
Примеры использования процедуры.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Продемонстрируем работу приведенной выше процедуры на
некоторых примерах. Эти примеры написаны на БЕЙСИКе и
вставляются в приведенную выше БЕЙСИК-программу (стр. 166 )
между строками 80 и 500.
"Каменный цветок"
~~~~~~~~~~~~~~~~~
100 DEF FN h(x,y,h,v) = USR 60400
110 BORDER 3: PAPER 3: INK 6: CLS
120 FOR j=1 TO 43
130 RANDOMIZE FN h(j*2,j*3,j,j)
140 RANDOMIZE FN h(j*4,j*3,j,j)
150 RANDOMIZE FN h(j*4,j,j,j)
160 RANDOMIZE FN h(j*2,j,j,j)
170 NEXT j
Еще одну разновидность геометрических построений можно
получить применением следующей программы:
100 DEF FN h(x,y,h,v) = USR 60400
110 BORDER 0: PAPER 2: INK 6 :CLS
120 FOR j=5 TO 38 STEP 2
130 RANDOMIZE FN h(j*2,j,50,50)
140 RANDOMIZE FN h(j*2,130-j,50,50)
150 RANDOMIZE FN h(206-j*2,130-j,50,50)
160 RANDOMIZE FN h(206-j*2,j,50,50)
170 NEXT j
3.10. Изображение треугольников.
В предыдущей программе мы использовали при вычислении ко-
координат прямоугольника тот факт, что его стороны параллельны.
Очередной шаг вперед для изображения произвольных многоугольни-
ков мы сделаем, если рассмотрим прием построения треугольника.
Для этого служит приводимая здесь процедура FN i(x,y,p,q,r,s).
x,y - координаты первой вершины;
p,g - координаты второй вершины;
r,s - координаты третей вершины треугольника.
Как Вы увидите, для работы этой процедуры создаются три
программных переменных PLOT_1...PLOT_3, хранящие координаты
вершин треугольника. Несложно развить этот прием и для любых
других многоугольных геометрических форм. Как и программа по-
строения прямоугольников, эта процедура использует вызов
процедуры FN g(), строящей отрезки прямых. Этот вызов происхо-
дит по адресу 60723, для чего координаты исходной точки должны
быть установлены в HL, а координаты точки назначения (абсолют-
ные) - в DE. Для обмена между процедурами FN i() и FN g()
служит переменная COORD, расположенная по адресу 60698.
10 REM *** Загрузчик машинного кода
20 LET adr=60300: LET long=75: 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
500 REM ***Данные для машинного кода
510 DATA 42, 11, 92, 1, 4
520 DATA 0, 9, 86, 14, 8
530 DATA 9, 94, 237, 83, 208
540 DATA 235, 9, 86, 9, 94
550 DATA 237, 83, 210, 235, 9
560 DATA 86, 9, 94, 237, 83
570 DATA 212, 235, 42, 210, 235
580 DATA 34, 26, 237, 205, 51
590 DATA 237, 237, 91, 208, 235
600 DATA 42, 212, 235, 34, 26
610 DATA 237, 205, 51, 237, 237
620 DATA 91, 210, 235, 42, 208
630 DATA 235, 34, 26, 237, 205
640 DATA 51, 237, 201, 40, 6
650 DATA 40, 143, 80, 123, 0
660 DATA 68, 0, 0, 0, 0
Дисассемблер программы:
60300 2A0B5C LD HL,(5C0BH) ;См. с. 109...111
60303 010400 LD BC,0004 ;Сдвиг от DEFADD на 4 бай-
60306 09 ADD HL,BC ;та (см. c.109...111).
60307 56 LD D,(HL) ;Координата x.
60308 0E08 LD C,08 ;Сдвиг на
60310 09 ADD HL,BC ;восемь байтов.
60311 5E LD E,(HL) ;Координата y.
60312 ED53D0EB LD(PLOT_1),DE ;Запомнили x,y.
60316 09 ADD HL,BC ;Сдвиг на 8 байтов.
60317 56 LD D,(HL) ;Параметр p.
60318 09 ADD HL,BC ;Сдвиг на 8 байтов.
60319 5E LD E,(HL) ;Параметр q.
60320 ED53D2EB LD(PLOT_2),DE ;Запомнили p,q.
60324 09 ADD HL,BC ;Сдвиг на 8 байтов.
60325 56 LD D,(HL) ;Параметр r.
60326 09 ADD HL,BC ;Сдвиг на 8 байтов.
60327 5E LD E,(HL) ;Параметр s.
60328 ED53D4EB LD(PLOT_3),DE ;Запомнили r,s.
60332 2AD2EB LD HL,(PLOT_2) ;Коорд. 2-ой вершины.
60335 221AED LD (COORD),HL ;Запоминаем координаты
;угла в ячейке 60698.
60338 CD33ED CALL ED33 ;Вызов процедуры FN g().
;Рисуем сторону 2-3.
60341 ED5BD0EB LD DE,(PLOT_1) ;Координаты 1-ой вершины.
60345 2AD4EB LD HL,(PLOT_3) ;Координаты 3-ей вершины.
60348 221AED LD (COORD),HL ;
60351 CD33ED CALL ED33 ;Рисование стороны 1-3.
60354 ED5BD2EB LD DE,(PLOT_2) ;Координаты 2-ой вершины.
60358 2AD0EB LD HL,(PLOT_1) ;Координаты 1-ой вершины.
60361 221AED LD (ED1A),HL ;
60364 CD33ED CALL ED33 ;Рисование стороны 1-2.
60367 C9 RET ;Выход из процедуры.
60368 PLOT_1 DEFW ;Коорд. первой вершины
60370 PLOT_2 DEFW ;Коорд. второй вершины
60372 PLOT_3 DEFW ;Коорд. третьей вершины
60698 COORD DEFW ;Переменная, служащая для
;связи с процедурой печа-
;ти прямых линий.
Примеры использования процедуры.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Возможность получения изображения треугольника является
очень важной для программиста. К сожалению, в стандартном БЕЙ-
СИКе оператора для этой цели не предусмотрено, а ведь треуголь-
ник служит базовым элементом для многих и многих геометрических
форм и прочих изображений - пирамиды, горы, деревья, кустарники
и пр. и пр. Список можно было бы продолжить. Простейшая про-
грамма "Прожектор" покажет Вам каких оригинальных эффектов
можно достичь изображением только треугольников.
"Прожектор".
~~~~~~~~~~~~
100 DEF FN i(x,y,p,q,r,s) = USR 60300
110 BORDER 0: PAPER 0: INK 6:
120 CLS
130 LET s=4: LET a=0: LET ad=s*PI/128
140 LET x1=162: LET y1 = 20
150 FOR i=0 TO 255 STEP s
160 LET x=x1+INT (90*SIN a)
170 LET y=y1+INT (10*COS a)
180 LET x2=x1+INT (90*SIN (a+PI))
190 RANDOMIZE FN i(x,y,x2,y,5,170)
200 LET a=a+ad
210 NEXT i
"Калейдоскоп".
~~~~~~~~~~~~~~~~
Если Вы в работе используете цветной телевизор (мони-
тор), то не пожалеете, что набрали следующую программу, но не
забудьте, что предварительно должен быть подгружен машинный код
процедуры FN b(x,y,h,v,c,b,f).
100 DEF FN b(x,y,h,v,c,b,f) = USR 62800
110 DEF FN i(x,y,p,q,r,s) = USR 60300
120 BORDER 4: PAPER 4: INK 1:
130 CLS
140 FOR a=0 TO 75 STEP 4
150 RANDOMIZE FN i(a,0,0,175-a,0,0)
160 RANDOMIZE FN i(a,175,0,a,0,175)
170 RANDOMIZE FN i(255-a,175,255,a,255,175)
180 RANDOMIZE FN i(255-a,0,255,175-a,255,0)
190 NEXT a
200 RANDOMUZE FN b(8,4,16,14,2,0,0)
210 FOR a= 78 TO 178 STEP 4
220 RANDOMIZE FN i(a,38,178,a-40,256-a,138)
230 RANDOMIZE FN i(256-a,138,78,216-a,a,38)
240 NEXT a