ПРИМЕНЕНИЕ АССЕМБЛЕРА ДЛЯ СОЗДАНИЯ БЫСТОРАБОТАЮЩИХ ПРОГРАММ
Перевод с английского Пашорина В.И.
Продолжение. Начало см. с. 9-12, 48-61, 97-103, 183-188
8. Команды ATTRIBUTE, SCREEN$ и POINT
Если Вы программировали игры на БЕЙСИКе, то вероятно сталкивались с тем, что Вам необходимо знать в определенной игровой ситуации текущее расположение заданного символа на экране. Это, например, нужно при проверке условия - достигла ли ракета цели, либо при выполнении посадки на Луну. В БЕЙСИКе для этого существуют три способа:
- использовать оператор ATTRIBUTE, который проверяет значение INK, PAPER, FLASH и BRIGHT в заданном знакоместе экрана и выдает число, соответствующее этим параметрам;
- использовать оператор SCREEN$, который определяет код символа в заданном знакоместе и выдает код этого символа, если он входит в диапазон 32...127. В противном случае считается, что ячейка пуста;
Примечание: процедуру в машинных кодах, которая может проверить не только символы ASCII, но и символы UDG-графики и символы блочной графики, т.е. из диапазона 32... 164 мы привели в первом томе, посвященном графике для "Спектрума" -"Элементарная графика" на с. 99 под названием SCANER. - ИНФОРКОМ.
- использование оператора POINT, который проверяет состояние одного пиксела экрана и выдает 1, если пиксел включен и 0, если он выключен.
Все эти три способа могут быть реализованы и в программах в машинных кодах с использованием специальных процедур ПЗУ.
8.1. ATTRIBUTE (строка, столбец).
Процедура ATTR, аналогичная по действию оператору ATTRIBUTE, находится в ПЗУ по адресу 9603. Перед ее вызовом необходимо записать координаты исследуемого знакоместа в регистровой паре BC (номер строки - в регистре B, а номер столбца - в регистре C). Результат проверки в виде числа 0...255 записывается на вершину стека калькулятора.
Определить атрибуты знакоместа, используя это число, можно по формуле:
ЧИСЛО=128*FLASH + 64*BRIGHT + 8*PAPER + INK значение
Это число можно переписать со стека калькулятора в регистр A, используя процедуру ПЗУ FP_TO_AA, находящуюся по адресу 11733, а затем сравнить его с заданным числом N с помощью команды CP N.
Пример реализации этого способа показан в программе 8.1. Программа проверяет каждое знакоместо экрана до тех пор, пока не найдет ячейку, атрибуты которой соответствуют PAPER 1, INK 7, FLASH 1, т.е. числу 143. После запуска этой программы на экране появляется ракета, медленно перемещающаяся по направлению к человечку. При совмещении ракеты с этим человечком происходит возврат в БЕЙСИК. Вы можете вместо команды возврата RET подключить процедуру, создающую звуковой эффект, сопровождающий попадание ракеты в человечка.
Такой способ проверки атрибутов символьных ячеек безусловно хорош, но требует, чтобы, например, все поражаемые объекты имели одинаковые атрибуты.
Листинг 8. 1.
АДРЕС МЕТКА МАШ.КОД АССЕМБЛЕР КОММЕНТАРИЙ
ORG 23760
23760 ED 5B 7B 5C LD DE,(23675) ;Адрес первого символа
|
|
|
|
|
|
|
|
графики пользователя. |
23764 |
|
21 |
42 |
5D |
|
|
LD HL,DATA 3 |
Конструкция символа UDG. |
23767 |
|
01 |
08 |
00 |
|
|
LD BC,8 |
8 байтов в символе. |
23770 |
|
ED |
B0 |
|
|
|
LDIR |
Установка символа UDG-"A" |
23772 |
|
3E |
02 |
|
|
|
LD A,2 |
Канал экрана - 2. |
23774 |
|
CD |
01 |
16 |
|
|
CALL 5633 |
Открываем канал печати на экран. |
23777 |
|
CD |
6B |
0D |
|
|
CALL 3435 |
Очистка экрана. |
23780 |
|
3E |
02 |
|
|
|
LD A,2 |
См. выше. |
23782 |
|
CD |
01 |
16 |
|
|
CALL 5633 |
|
23785 |
|
11 |
2E |
5D |
|
|
LD DE,DATA1 |
Данные для печати - DАТА1. |
23788 |
|
01 |
10 |
00 |
|
|
LD BC,16 |
Символов - 16. |
23791 |
|
CD |
3C |
20 |
|
|
CALL 8252 |
Вызов подпрограммы ПЗУ для печати символьной строки. |
23794 |
|
FD |
36 |
57 |
03 |
|
LD (IY+87),3 |
(IY+87) указывает на системную переменную PFLAG (23697). Установка в ней числа 3 означает включение режима INVERSE. |
23798 |
|
AF |
|
|
|
|
XOR A |
Обнуление аккумулятора. |
23799 |
|
32 |
40 |
5D |
|
|
LD (COL),A |
Обнуление столбца COL. |
23802 |
L1 |
11 |
3E |
5D |
|
|
LD DE,DATA2 |
Данные для печати - DATA2. |
23805 |
|
01 |
04 |
00 |
|
|
LD BC,4 |
Символов - 4. |
23808 |
|
CD |
3C |
20 |
|
|
CALL 8252 |
Печать строки DATA2. |
23811 |
DELAY |
21 |
FF |
FF |
|
|
LD HL,65535 |
Организация цикла |
23814 |
L2 |
2B |
|
|
|
|
DEC HL |
задержки для того, чтобы |
23815 |
|
7C |
|
|
|
|
LD A,H |
изменения на экране не |
23816 |
|
B5 |
|
|
|
|
OR L |
происходили слишком |
23817 |
|
20 |
FB |
|
|
|
JR NZ,L2 |
быстро. |
23819 |
|
11 |
3E |
5D |
|
|
LD DE,DATA2 |
Данные для печати - DATA2. |
23822 |
|
01 |
04 |
00 |
|
|
LD BC,4 |
Символов - 4. |
23825 |
|
CD |
3C |
20 |
|
|
CALL 8252 |
Печать строки DATA2. |
23828 |
|
3A |
40 |
5D |
|
|
LD A,(COL) |
Номер столбца позиции печати |
23831 |
|
3C |
|
|
|
|
INC A |
Переход к соседней позиции. |
23832 |
|
32 |
40 |
5D |
|
|
LD (COL),A |
Запомнили новую позицию. |
23835 |
|
ED |
4B |
3F |
5D |
|
LD BC,(LINE) |
Координаты позиции печати. |
23839 |
|
CD |
83 |
25 |
|
|
CALL 9603 |
Вызов процедуры ПЗУ для опре деления атрибутов в текущей координате. |
23842 |
|
CD |
D5 |
2D |
|
|
CALL 11733 |
Перенос результата со стека калькулятора в регистр A. |
23845 |
|
FE |
8F |
|
|
|
CP 143 |
Проверка на PAPER 1: INK 7: FLASH 1. |
23847 |
|
20 |
D1 |
|
|
|
JR NZ,L1 |
Возврат, если нужное знакоместо еще не найдено. |
23849 |
|
FD |
36 |
57 |
00 |
|
LD (IY+87),0 |
Выключение режима INVERSE. |
23853 |
|
C9 |
|
|
|
|
RET |
Возврат в БЕЙСИК. |
23854 |
DATA1 |
DEFB |
22 |
0 |
20 |
17 |
Эти данные эквивалентны: |
|
|
|
|
1 |
16 |
7 |
18 |
...AT 0,20; PAPER 1; INK 7; |
|
|
|
|
1 |
144 |
18 |
0 |
FLASH 1; "A"; PAPER 0; INK 7 |
|
|
|
|
17 |
7 |
16 |
0 |
FLASH 0. Здесь "А" - это сим вол UDG; размещенный на клавише A, т.е. CHR$(144). |
23870 |
DATA2 |
DEFB |
22 |
|
|
|
Эти данные эквивалентны: |
23871 |
LINE |
DEFB |
0 |
|
|
|
...AT 0,COL; CHR$(62). |
23872 |
COL |
DEFB |
0 |
|
|
|
Символ 62 - это ">", он и |
23873 |
DEFB |
62 |
|
|
|
|
|
изображает "ракету". |
23874 |
DATA3 |
DEFB |
24 |
153 |
126 |
153 |
Это данные для символа UDG, |
|
|
|
|
24 |
36 |
36 |
102 |
изображающего человечка. |
8.2 SCREEN$
Точно так же, как и при вызове процедуры ATTR, перед вызовом этой процедуры необходимо в регистровую пару BC записать координаты символьной ячейки. Процедура
SCREEN$ находится по адресу 9526. Параметры символа исследуемой символьной ячейки при вызове этой процедуры будут записываться на калькуляторный стек в пятибайтной форме. Использование калькуляторного стека для хранения параметров символа для Вас, наверное, открытие, поскольку до сих пор речь шла о хранении на стеке только чисел в пятибайтной форме.
Коды калькулятора при этом оперируют не с самими символами, а с их параметрами, записанными в пятибайтной форме. Для того, чтобы передать параметры символа на стек, необходимо записать в регистровую пару ВС размер символа в байтах, а в регистровую пару DE - начальный адрес участка памяти, где хранится вся информация о символе. Для стандартных символов в регистр A записывается 0.
Для передачи этих параметров на стек используется процедура, находящаяся в ПЗУ по адресу 10929. Процедура же по адресу 11249 выполняет обратную функцию - передает параметры символа со стека в соответствующие регистры A, B, C, D, Е. При этом размер символа будет записан в регистровую пару ВС, а начальный адрес участка памяти с информацией о символе - в регистровую пару DE.
Программа 8.2 показывает, как можно использовать эти процедуры. После выполнения программы, на экране появятся два символа @, соответственно в позициях 0,20 и 0,21. Вместо вызова процедуры PRINT STRING (адрес 6252), можно использовать команду LD A,(DE) для проверки значения символа в позиции 0,20, а затем выполнить необходимые действия.
Листинг 8.2.
При использовании БЕЙСИК-оператора SCREEN$ имеет место одно ограничение. Код распознаваемого символа на экране должен быть в диапазоне от 32 до 127. В противном случае считается, что это пустой символ. Такое ограничение существенно для игровых программ, где используются UDG, распознать которые в данном случае не удается. Для программ в машинных кодах такой проблемы не существует, поскольку UDG можно включить в стандартный символьный набор. Постоянно этот набор хранится в ПЗУ, начиная
АДРЕС
МЕТКА
.КОД
АССЕМБЛЕР ORG 23750
КОММЕНТАРИЙ
23760 |
3E |
02 |
|
LD A,2 |
Канал экрана - 2. |
23762 |
CD |
01 |
16 |
CALL 5633 |
Открываем канал печати на экран. |
23765 |
CD |
6B |
0D |
CALL 3435 |
Очистка экрана. |
23768 |
3E |
02 |
|
LD A,2 |
Канал экрана - 2. |
23770 |
CD |
01 |
16 |
CALL 5633 |
Открываем канал печати на экран. |
23773 |
3E |
16 |
|
LD A,22 |
|
23775 |
D7 |
|
|
RST 16 |
|
23776 |
3E |
0 |
|
LD A,0 |
Аналог ...AT 0,20;"@" |
23778 |
D7 |
|
|
RST 16 |
|
23779 |
3E |
14 |
|
LD A,20 |
|
23781 |
D7 |
|
|
RST 16 |
|
23782 |
3E |
40 |
|
LD A,64 |
|
23734 |
D7 |
|
|
RST 16 |
|
23785 |
06 |
14 |
|
LD B,20 |
Номер экранного столбца. |
23787 |
0E |
00 |
|
LD C,0 |
Номер строки экрана. |
23789 |
CD |
38 |
25 |
CALL 9528 |
Вызов процедуры ПЗУ для определения того, что находится в данном знакоместе. |
23792 |
CD |
F1 |
2B |
CALL 11249 |
Передача параметров символа со стека калькулятора в регистры процессора. |
23795 |
CD |
3C |
20 |
CALL 8252 |
Печать той символьной стро- |
ки, на местоположение которой указывает пара DE, а длина которой находится в ВС. В нашем случае - это символ "@".
с адреса 15360. Однако начальный адрес этого набора можно менять, меняя значение системной переменной CHARS (23606/7). Она указывает на 256 байтов ниже, чем начало символьного набора. Если переписать символьный набор из ПЗУ в ОЗУ таким образом, чтобы в этот набор вышел участок с информацией об UDG и чтобы UDG "А" соответствовал код 32, UDG "В" - код 33 и т.д., то можно вызывать процедуру SCREEN$ для идентификации UDG на экране.
Программа 8.3 работает точно так же, как и программа 8.1, но эта программа способна распознавать на экране символ, код которого равен 144. Выполнив копирование стандартного символьного набора в ОЗУ и переписав значение символьной переменной CHARS можно совсем отказаться от UDG. Теперь можно часть стандартных символов (например, строчные буквы и некоторые знаки пунктуации) заменить на символы, форму которых можем задать сами (это будет около 50 символов). Процедура SCREEN$ будет легко распознавать на экране любой символ, если он входит в символьный набор. И еще одно важное замечание. Необходимо всегда иметь свободный участок памяти, используемый как рабочий для стека. Если такого участка нет или он есть, но периодически не очищается, то при выполнении программы может появиться сообщение о переполнении памяти. Поэтому перед каждым вызовом процедуры SCREEN$ необходимо очищать специально зарезервированный рабочий участок.
Листинг 8.3.
МЕТКА МАШ.КОД
СМ. ЛИСТИНГ 8.1
ORG 23760
LD HL,(23675) DEC H
23843 23846
23849
23850 23852
23855
23858
CD 38 25 CD F1 2B
1A
FE 20
21 00 3C
22 36 5C
LD (23606),HL
CALL 9528 CALL 11249
LD A,(DE)
CP 32
LD HL,15360 LD (23606),HL
Адрес первого символа UDG. Отступили на 256 байтов. Мы помним, что CHARS указывает на 256 байтов ниже, чем начало шрифта. Переустановили CHARS так, она теперь указывает на область UDG.
Вызов процедуры SCREEN$. Передача параметров символа со стека в регистры. Проверка номера символа в текущем знакоместе. Проверка "не пробел ли это?" Расположение стандартного шрифта минус 256 б. Восстановили стандартное значение CHARS.
Далее, как в программе 8. 1
Процедура очистки рабочего участка имеется в ПЗУ по адресу 5823. Вызов этой процедуры в программе 8.3 можно производить после команды LD A,(DE), однако предварительно необходимо сохранить в памяти последнее значение регистра А.
8.3 POINT
Оператор POINT x,y в БЕЙСИК-программах выдает 1, если пиксел экрана с координатами x,y включен и выдает 0, если он выключен. Процедура ПЗУ, соответствующая этому оператору (адрес 8910), может быть вызвана, если предварительно записать в регистр B у-координату, а в регистр C, соответственно x координату пиксела. При этом результат зашлется на стек, а т.к. он равен 0 или 1, то мы можем переписать его в регистр A и сравнить с заданным значением. В программе 8.4 показан способ проверки пиксела верхнего левого угла (координаты 0,175).
Если в начале в символьную ячейку с координатами 0,0 занести черный квадрат, то результат работы процедуры будет равен 1, т.к. пиксел с координатами 0,175 входит в этот квадрат.
Листинг 8.4.
АДРЕС |
МЕТКА МАШ |
и.КОД |
АССЕМБЛЕР КОММЕНТАРИЙ |
|
|
|
|
|
ORG 23760 |
|
|
23760 |
3E |
02 |
|
LD A,2 |
Канал экрана - 2 |
|
23752 |
CD |
01 |
16 |
CALL 5633 |
Открываем канал экран. |
печати на |
23765 |
CD |
6B |
0D |
CALL 3435 |
Очистка экрана. |
|
23768 |
3E |
02 |
|
LD A,2 |
Канал экрана - 2 |
|
23770 |
CD |
01 |
16 |
CALL 5633 |
Открываем канал экран. |
печати на |
23773 |
3E |
8F |
|
LD A,143 |
"Черный квадрат" |
|
23775 |
D7 |
|
|
RST 16 |
Печать квадрата. |
|
23776 |
0E |
00 |
|
LD C,0 |
Координата x. |
|
23778 |
06 |
AF |
|
LD B,175 |
Координата y. |
|
23780 |
CD |
CE |
22 |
CALL 8910 |
Вызов процедуры из ПЗУ. |
POINT |
23783 |
CD |
D5 |
2D |
CALL 11733 |
Перенос результата со стека калькулятора в регистры. |
23786 |
FE |
01 |
|
CP 1 |
Проверка включен или нет. |
пиксел |
23788 |
C8 |
|
|
RET Z |
Выход, если да. |
|
9. ПРИНТЕР
Не так уж много существует программ, для работы которых необходим принтер. Всегда можно ограничиться для вывода информации только экраном дисплея. Но иногда, например, необходимо показать свой уровень, достигнутый в какой-нибудь игре друзьям или знакомым. В этом случае нужно сделать копию изображения экрана на принтере. Поэтому в этой главе мы рассмотрим три BASIC-команды: COPY, LPRINT, LLIST и их аналоги в машинных кодах, обеспечивающих работу с принтером.
9.1. COPY
При вызове этой процедуры только двадцать две строки экрана передаются на принтер. Программа в машинных кодах позволит Вам улучшить эту процедуру так, чтобы можно было передавать на принтер произвольное число строк, причем, начиная с любой строки на экране, например, передать на принтер три строки, начиная с седьмой.
В программе 9.1 показан способ передачи на принтер всех 24 строк экрана, поскольку для некоторых игр достигнутый уровень показан в нижних строках экрана и при обычном вызове процедуры COPY получить необходимую информацию с двух последних строк не удается.
Листинг 9.1
|
|
|
|
ORG 23760 |
23760 |
21 |
00 |
40 |
LD HL,16384 |
23763 |
06 |
C0 |
|
LD B,192 |
23765 |
F3 |
|
|
DI |
23756 |
CD |
B2 |
0E |
CALL 3762 |
23769 |
C9 |
|
|
RET |
Как Вы заметили, в этой программе перед вызовом процедуры, находящейся в ПЗУ по адресу 3762, необходимо в регистровую пару HL записать начальный адрес экранной области ОЗУ (т.к. нам необходима копия, начиная с первой строки, то этот адрес 16384), а в регистр B - число копируемых строк, умноженное на 8.
9.2 LPRINT
Эта команда имеет сходство с командой PRINT. Различие состоит в том, что не работают по этой команде управляющие коды и, конечно, используется другой канал для вывода информации на принтер, который необходимо открыть в программе в машинных кодах, прежде чем использовать команду RST 16. Например, чтобы вывести на принтер литеру "A", т.е. реализовать в машинных кодах LPRINT "A", необходимо первоначально открыть канал 3, затем записать в регистр A код литеры "A", ну а затем использовать команду RST 16.
Программа 9.2 демонстрирует этот способ для вывода на принтер символьной строки.
Листинг 9.2
|
|
|
ORG 23760 |
23760 |
3E 03 |
|
LD A,3 |
23762 |
CD 01 |
16 |
CALL 5633 |
23765 |
11 DF |
5C |
LD DE,DATA |
23768 |
01 09 |
00 |
LD BC,9 |
23771 |
CD 3C |
20 |
CALL 8252 |
23774 |
C9 |
|
RET |
23775 |
DATA DEFM |
PROGRAM |
2 |
Для выполнения табуляции выводимой на принтер информации, необходимо предварительно в системную переменную PRCC (23680) через POKE записать соответствующее число.
9.3 LLIST
Это третья БЕЙСИК-команда для работы с принтером. Реализовать ее в машинных кодах довольно просто. Достаточно только записать CALL 6133 и будет выполняться листинг строк BASIC-программы на принтере.