ПРИМЕНЕНИЕ АССЕМБЛЕРА ДЛЯ СОЗДАНИЯ БЫСТРОРАБОТАЮЩИХ ПРОГРАММ
Продолжение. (Начало см. в "ZX-РЕВЮ-ЭЗ" N 1,2)
Заканчивая рассмотрение вопросов выдачи информации на экран, мы должны еще обсудить возможность печати в нижней части экрана, т.е. в строках с номерами 22 и 23.
Для этого применяют те же способы, что были описаны выше для вывода на главную часть экрана. Отличие состоит лишь в том, что для вывода в нижнюю часть экрана необходимо открывать другой канал, прежде чем применять команду печати RST 16. Как это сделать, показано в демонстрационной программе 1.8.
Здесь для вывода в нижнюю часть экрана сначала открывается канал с номером "минус 3" (253). Чтобы информационное сообщение "0:0.K.", появляющееся после исполнения программы не переместило выведенную нами на экран строку, необходимо иметь эквивалент команды PAUSE 0 в машинных кодах.
Останов программы можно реализовать, если использовать команду ассемблера HALT, которая приостанавливает работу процессора до очередного прерывания (до очередного сканирования клавиатуры, выполняемого процедурой ПЗУ KEYSCAN). Для реализации команды, аналогичной PAUSE 0, необходимо после команды HALT выполнить проверку состояния 5-го бита системной переменной FLAGS, который включается при нажатии произвольной клавиши. Если этот бит сброшен, то выполняется возврат к команде HALT, в противном случае программа выполняется дальше. Обратите внимание на то, что здесь 22-я строка считается нулевой, а 23-я - первой.
При выводе данных на экран весьма полезной для Вас может оказаться одна процедура из ПЗУ, находящаяся там по адресу 3652. Она позволяет уладить заданное число строк с экрана, причем отсчет строк начинается с 23-й строки, т.е. с ее помощью Вы можете как стирать информацию в системном окне, так и на основном экране. Пользоваться этой процедурой придется, по-видимому очень часто.
Перед вызовом процедуры необходимо записать в регистр B число удаляемых строк. Атрибуты экрана остаются такими, какие записаны в системной переменной ATTR-P. Так, например, нижние две строки экрана могут быть очищены с помощью следующей короткой программы: LD B,2 CALL 3652 RET
Листинг 1.8
АДРЕС |
МАШ |
. КОД |
АССЕМБЛЕР КОММЕНТАРИЙ |
|
|
|
|
ORG 23760 |
|
23760 |
3E |
FD |
|
LD A,253 |
Подготовка к открыванию канала. |
23762 |
CD |
01 |
16 |
CALL 5633 |
Открываем канал "системного окна компьютера" |
23765 |
11 |
EA |
5С |
LD DE,DATA |
Адрес начала выводимых данных. |
23768 |
01 |
1E |
00 |
LD BC,30 |
Длина данных с учетом управляющих кодов. |
23771 |
CD |
3C |
20 |
CALL 8252 |
Вызов процедуры ПЗУ для печати сообщения. |
WAIT |
|
|
|
|
Начало процедуры "задержки", аналогичной PAUSE 0. |
23774 |
76 |
|
|
HALT |
Прекращение работы процессора до прихода системного прерывания |
23775 |
FD |
CB |
01 6E |
BIT 5,(IY+1) |
Проверка пятого бита системной переменной FLAGS. |
23779 |
28 |
F9 |
|
|
|
JR Z,WAIT |
Если он выключен - клавиша не нажималась, то возврат на метку WAIT. |
23781 |
FD |
CB |
01 |
AE |
|
RES 5,(IY+1) |
Если он включен, то выключаем его и продолжаем работу |
23785 |
C9 |
|
|
|
|
RET |
Выход из процедуры. |
DATA |
22 |
00 |
10 |
|
|
|
Аналог "AT 0, 10" |
|
73 |
78 |
80 |
85 |
84 |
|
текстовое сообщение |
|
32 |
76 |
73 |
78 |
69 |
|
"INPUT LINE 0", записан |
|
32 |
48 |
|
|
|
|
ное в кодах ASCII. |
|
22 |
01 |
10 |
|
|
|
Аналог "AT 1, 10" |
|
73 |
78 |
80 |
85 |
84 |
|
Текстовое сообщение |
|
32 |
76 |
73 |
78 |
69 |
|
"INPUT LINE 1", записан- |
|
32 |
49 |
|
|
|
|
ное в кодах ASCII. |
2. Команды PLOT, DRAW и CIRCLE PLOT
Команда PLOT x,y позволяет поставить на экране точку (включить пиксел), координата которой определяются значениями x и у, а цвет зависит от установленного значения INK. Координата x определяет номер столбца, а координата у - номер строки той точки экрана, где будет включен пиксел.
Пиксел, координаты которого равны 0,0, находится в нижнем левом углу экрана, а пиксел с координатами 175,255 - в верхнем правом углу экрана.
Для этой цели в ПЗУ компьютера имеется процедура PLOT, у которой есть две возможные точки входа.
Первая точка входа имеет адрес 8933 DEC (22E5 HEX). Перед вызовом этой процедуры по команде CALL 8933, необходимо в регистр B записать значение координаты у (0...175), а в регистр C - значение координаты x (0...255). Пример записи показан в программе 2.1.
Листинг 2.1
АДРЕС |
МАШ |
и. КОД |
АССЕМБЛЕР ORG 23760 |
КОММЕНТАРИЙ |
|
23760 |
08 |
7D |
LD B,125 |
;Координата y= |
=125. |
23762 |
0E |
4B |
LD C,75 |
;Координата x= |
=75. |
23764 |
CD |
E5 22 |
CALL 8933 |
;Печать точки |
(125,75) |
23767 |
C9 |
|
RET |
;Возврат. |
|
Использование десятиричных чисел вместо шестнадцатиричных мы применяем только для того, чтобы начинающие читатели быстрее адаптировались к сути работы в машинном коде. Они более наглядны, если применяется раздельная загрузка регистров B и C. Однако, если записывать координаты сразу же в регистровую пару BC, то лучше все же использовать шестнадцатиричные числа - при такой записи проще представить положение пиксела на экране.
Вторая точка входа имеет адрес 8924 DEC (22DC HEX). При этом значения координат x и у должны быть предварительно записаны не в регистровую пару BC, а на стек так называемого встроенного калькулятора, причем значение у должно находиться на вершине стека, а значение x - под ним.
Начинающий программистам надо сказать и несколько слов о стеке калькулятора.
Для работы с действительными числами (числами с десятичной точкой) целочисленных регистров процессора явно недостаточно. Более мощные машины применяют для этого математический сопроцессор. В "Спектруме" же реализован другой, более удобный и дешевый подход - в ПЗУ внедрена обширная программа-калькулятор, имеющая свою оригинальную систему команд. Когда Вы работаете в БЕЙСИКе, калькулятор работает автоматически и для Вас все происходит незаметно. При работе в машинном коде
приходится программировать его вручную.
Основным местом для хранения исходных данных и результатов расчета калькулятора является его стек, хотя кроме этого калькулятор имеет еще и несколько ячеек памяти. Подробно с использованием калькулятора Вы можете познакомиться в нашей книге "Программирование в машинных кодах. Первые шаги. Практикум. Справочник."
Остается, правда, пока открытым вопрос, а как же записать координаты своей точки на стек калькулятора? Очень просто, для этой цели тоже используется процедура ПЗУ компьютера? Она находится по адресу 11560 DEC (2D28 HEX). Вам нужно поместить желаемое число в регистр A процессора и вызвать эту процедуру. Число будет передано на стек, чтобы отправить туда два числа, операцию надо сделать дважды. Естественно, что то число, которое будет передано последним и будет размещено на вершине стека.
На первый взгляд кажется, что работа с первой точкой входа намного удобнее, т.к. не надо ничего перебрасывать через стек калькулятора. Но это только на первый взгляд. На самом же деле в программах очень редко приходится что-то печатать в заранее известных координатах. Почти всегда программа сама и рассчитывает эти координаты. Делается это с помощью встроенного калькулятора и потому взять результаты расчета со стека и использовать их напрямую оказывается весьма удобно.
Листинг 2.2
КОММЕНТАРИЙ
Координата x=75. Поместили x на стек калькулятора. Координата y=125 Поместили ее на стек. Печать точки (125,75) Возврат.
АДРЕС
23760 23762
23765 23767 23770 23773
МАШ. КОД
3E 4B CD 28 2D
3E 7D CD 28 2D CD DC 22 C9
АССЕМБЛЕР ORG 23760 LD A,75 CALL 11560
LD A,125 CALL 11560 CALL 8924 RET
Листинг 2.3
АДРЕС |
МАШ |
. КОД |
АССЕМБЛЕР |
КОММЕНТАРИЙ |
|
|
|
ORG 23760 |
|
|
23760 |
3E |
03 |
LD A,3 |
|
|
23762 |
FD |
77 57 |
LD (IY+87) |
A |
Включение двух младших битов сист. перем. P_FLAG |
23765 |
06 |
28 |
LD B,40 |
|
Координата у=40. |
23767 |
0E |
19 |
LD C,25 |
|
Координата х=25 |
23769 |
CD |
E5 22 |
CALL 8933 |
|
Печать точки (40,25) в ре жиме OVER 1. |
23772 |
AF |
|
XOR A |
|
Очистка регистра A. |
23773 |
FD |
77 57 |
LD (IY+87) |
A |
Восстановление P_FLAG. |
23776 |
C9 |
|
RET |
|
Возврат. |
АССЕМБЛЕР ORG 23760 LD L,64 LD H,00 ADD HL,HL ADD HL,HL ADD HL,HL LD DE,(23606)
АДРЕС
23760 23762
23764
23765
23766
23767
МАШ. КОД
2E 7F 26 00 29 29 29
ED 5B 36 5C
ADD HL,DE
LD B,8
Листинг 2.4
КОММЕНТАРИЙ
;Номер символа "@"=64 DEC. ;Теперь в паре HL число 64 ;Умножили его на 2. ;Умножили его на 4. ;умножили его на 8. ;Загрузили в DE адрес, на ;который указывает системная переменная CHARS. ;Прибавили к нему "смещение" нашего символа от ;начала символьного набо-;ра, ранее вычисленное в ;регистровой паре HL. ;Счетчик строк в шаблоне символа, равный 8.
Запомнили адрес начала шаблона на машинном стеке Счетчик столбцов в шаблоне символа, равный 8. Принимаем в аккумулятор байт шаблона символа. Запомнили содержимое BC, чтобы освободить BC для других дел.
Ввод в BC координат позиции печати.
Ротация регистра A влево. Содержимое старшего бита при этом переходит во флаг CARRY регистра F. Запомнили регистры A и F на машинном стеке. Если флаг CARRY выключен, то точку печатать не надо. Делаем обход печати. Перед печатью точки запоминаем координаты. Вызов процедуры PLOT. Восстановили координаты. Переход к очередному столбцу символа. Запомнили новую позицию печати.
Восстановление пары AF. Восстановление счетчиков в BC Уменьшение счетчика столбцов. Если он еще не обнулился возвращаемся для печати соседней точки в ряду. Если же он равен 0, то с этим рядом закончили и надо переходить к новому. Сперва временно запомним счетчики. Возьмем координаты. Уменьшим номер ряда. Число 248 - это то же самое, что и "минус 8". В результате сложения из содержимого C (коорд. x) будет вычтено 8. Результат оставлен в A. Перенесем его в C и запомним в переменной COORD. Восстановили счетчики рядов и столбцов. Восстановление указателя. Приняли из символьного набора конструкцию очередного ряда. Уменьшаем на единицу счетчик рядов в B и если он еще не обнулялся, то возвращаемся назад. Выход
PUSH HL LD C,B LD A,(HL) PUSH BC
E5
0E 08
7E
C5
ED 4B 0E 5D 17
LD BC,(COORD) RLA
PUSH AF JR NC,23792
F5
30 05
C5
CD E5 22 C1
0C
ED 43 0E 5D
F1 C1 0D
20 E8
PUSH BC
CALL 8933 POP BC INC C
LD (COORD),BC
POP AF POP BC DEC C
JR NZ,23778
LD BC,(COORD) DEC B LD A,248
ADD A,C
LD C,A
LD (COORD),BC POP BC POP HL INC HL
DJNZ 23774
4F
ED 43 0E 5D
C1
E1
23
10 D1
C9 RET
;Координата y=100 ;Координата x=20
тма 2.2 показывает использование процедуры PLOT со второй точкой входа C HEX). Процедура PLOT снимет два верхних числа со стека калькулятора и как координаты для включения пиксела на экране. Числа на стеке при этом не
Использование калькулятора будет еще обсуждаться более подробно при рассмотрении программы 2.8. А пока рассмотрим программу 2.3, в которой показано, как в машинных кодах реализовать команду
PLOT OVER 1; x,y.
В этой программе перед вызовом процедуры PLOT необходимо включить нулевой и первый биты системной переменной P-FLAG (IY+87). А затем, после вызова процедуры PLOT, восстановить прежнее значение этих битов. Если этого не сделать, то и все последующие точки и символы будут выводиться на экран в режиме OVER 1.
Включение режима INVERSE аналогично включению режима OVER. Отличие состоит лишь в том, что теперь необходимо включить 2-ой и 3-ий биты системной переменной P-FLAG, т.е. надо записать в эту переменную число 12.
Используя точку входа 8933, мы можем написать программу для построения символа в любом месте экрана по точкам. При этом код ASCII символа должен быть в диапазоне 32... 127, а шаблон символа (его конструкция) должен быть задан в участке символьного набора (см. программу 2.4).
Чтобы определить, где хранятся конструкции (шаблоны) символов, служит системная переменная CHARS (23606 DEC = 5C36 HEX). Тот адрес, который в ней хранится, указывает на 256 байтов ниже, чем начало символьного набора.
В программе 2.4 код выводимого на экран символа записывается в регистр L. Затем это значение умножается на 8 (т.к. в символьном наборе на конструкцию каждого символа использовано по 8 байтов) и результат суммируется с числом, которое хранится в системной переменной CHARS. Таким образом определяется начальный адрес восьми байтов, описывающих форму нужного вам символа.
Вы знаете, что первые 32 символа (с 0 по 31) являются непечатными, поэтому очень удобно, что CHARS указывает на 256 байтов ниже, чем начало набора, т.к. благодаря этому они-то как раз и оказываются пропущенными (32*8=256).
Теперь регистровая пара HL используется как указатель адреса очередного байта, а регистровая пара BC - как счетчик элементов матрицы 8x8 битов, описывающей форму символа. Здесь в B хранится номер текущего ряда, а в C - номер столбца. Регистр A используется для манипуляций с текущим байтом.
Листинг 2.5
АДРЕС |
МАШ |
. КОД |
|
АССЕМБЛЕР КОММЕНТАРИЙ |
|
|
|
|
|
ORG 23760 |
|
23760 |
2E |
7F |
|
|
LD L,87 |
Номер символа "W"=87 DEC |
23762 |
|
|
|
|
|
Строки 23762... 23791 |
|
|
|
|
|
|
этой программы соответ- |
23791 |
|
|
|
|
|
ствуют программе 2/4. |
23792 |
04 |
|
|
|
INC B |
Переход к очередной стро- |
|
|
|
|
|
|
ке экрана. |
23793 |
ED |
43 |
0E |
5D |
LD(COORD),0BC |
Запомнили позицию печати |
23797 |
F1 |
|
|
|
POP AF |
Восстановление пары AF. |
23798 |
C1 |
|
|
|
POP BC |
Восстановление счетчиков. |
23799 |
0D |
|
|
|
DEC C |
Уменьшение счетчика |
|
|
|
|
|
|
столбцов. |
23800 |
20 |
E8 |
|
|
JR NZ,23778 |
Если он еще не обнулялся, |
|
|
|
|
|
|
возвращаемся для печати |
|
|
|
|
|
|
соседней точки в ряду. |
23802 |
C5 |
|
|
|
PUSH BC |
Переходя к очередному ря |
|
|
|
|
|
|
ду, запомнили счетчики. |
33803 |
ED |
4B |
0E |
5D |
LD BC,(COORD) |
Возьмем координаты. |
23807 |
05 |
|
|
|
DEC B |
Уменьшим номер ряда. |
23808 |
3E |
F8 |
|
|
LD A,248 |
Уменьшение координаты |
23810 |
80 |
|
|
|
ADD A,B |
y на 8. |
23811 |
47 |
|
|
|
LD B,A |
|
23812 |
ED |
43 |
0E |
5D |
LD (COORD),BC |
Запомнили координату. |
23816 |
C1 |
|
|
|
POP BC |
Восстановили счетчики ря |
|
|
|
|
|
|
дов и столбцов. |
23817 |
E1 |
|
|
|
POP HL |
Восстановление указателя. |
Очередной ряд.
Уменьшаем на единицу сче тчик рядов в B и если он еще не обнулился, то воз вращаемся назад.
Выход
:Координата y=100 ;Координата х=20
Так, например, одной из манипуляций является ротация байта влево командой RLA, которая производит циклическое смещение всех битов байта влево и перенос седьмого бита во флаг CARRY. Если этот бит равен единице, то флаг CARRY включается, следовательно будет включен и соответствующий пиксел на экране, если же бит равен 0, то не включатся ни флаг, ни пиксел. Команда RLA выполняется по 8 раз для каждого из 8 байтов, описывающих шаблон символа.
После каждой команды RLA координаты очередного пиксела изменяются. Начальное значение этих координат соответствует верхнему левому углу матрицы 8*8 битов, хранящей форму символа. Понятно, что при этом начальное значение координаты у должно быть в диапазоне 7...175, а значение координаты x может быть в диапазоне 0...255. Если же это значение окажется больше, чем 255, то это равносильно тому, что значение равно 0, так как если C=255 и выполняется команда INC C, то результат будет C = 0.
Эта программа может быть легко модифицирована для вывода на экран символа, развернутого на 90 градусов против часовой стрелки (см. программу 2. 5).
В этой программе начальные значения координат х,у соответствуют нижнему левому углу матрицы, хранящей форму символа и исследование байта идет не по горизонтали, а по вертикали.
С этого момента мы можем прийти к очень интересным решениям. Дело в том, что одной из первых проблем, встающих перед начинающим пользователем "Спектрума" является печать нестандартными шрифтами.
Обычно каждый спрашивает "а почему компьютер печатает только символами 8X8?" Почему нельзя печатать символами 5X8, 6X8 и даже 3X6, как например в программе "THE LAST WORD 2". Почему нельзя печатать символами 17X18? Может быть для тех, кто пишет программу на японском языке это было бы очень удобно.
Оказывается, если Вы печатаете из БЕЙСИКа или из машинного кода командой RST 16 или процедурой ПЗУ, которая опирается на RST 16, Вы действительно ограничены системным требованием размера 8x8. Так уж заложено в системных процедурах ПЗУ. Но если Вы выполняете графическую печать, то есть как бы не печатаете свои символы, а рисуете их по точкам, то можете обойти эти процедуры и создать свои, которые позволят вам делать все, что захотите.
Листинг 2.6
5 OVER 0: INK 0: PAPER 6: BORDER 3: CLS
10 LET x=24: LET y=79: LET h=5: LET w =2: LET a$="STOP THE TAPE" 15 INK 2: RANDOMIZE USR 32393: PAUSE 100 20 LET w=8: LET y=150: LET x=0: LET a$="PLOT" 30 INK 1: RANDOMIZE USR 32393
35 LET a$ =" ": LET y=140: INK 1: RANDOMIZE USR 32393
250 PRINT AT 8,0:"Высота 1-22? ", "Ширина 1-32? ", "х-коорд. 0-255? ", "Y-коорд. 0195? ", "СООБЩЕНИЕ?", " INK 0-9?", 255 LET z$=" " 257 LET v$=" "
260 PRINT OVER 1; AT 8,0; z$
262 INPUT h: PRINT AT 8,14; h;" ":OVER 1; AT 8,0; v$
265 PRINT OVER 1; AT 9,0; z$: INPUT w; PRINT AT 9,14; w: " "; OVER 1; AT 9,0; v$ 270 PRINT OVER 1; AT 10,0;z$: INPUT x: PRINT AT 10,17;x;" "; OVER 1; AT 10,0;v$ 272 PRINT OVER 1; AT 11,0:z$: INPUT y: PRINT AT 11,17; y:" "; OVER 1: AT 11,0;v$
273 PRINT OVER 1; AT 12,0;z$; AT 13,0; z$: INPUT a$: PRINT AT 13,0: a$; : FOR k=LEN a$ TO
31: PRINT " ";: NEXT k: PRINT OVER 1; AT 12,0; v$; OVER 1: AT 13,0; v$ 275 PRINT OVER 1; AT 14,0; z$: INPUT 1: PRINT AT 14,0; INK 1; OVER 1; v$ 280 PAUSE 50: CLS: INK 1: RANDOMIZE USR 32393: INK 0
300 PRINT #0; AT 0,0 "Q - конец работы"; AT 1,0; "Прочие клавиши - повтор" 310 PAUSE 0: IF INKEY$="Q" OR INKEY$ = "q" ^N STOP 320 CLS: GO TO 250
Освоив способы вывода по точкам произвольного символа, мы можем перейти к нашей первой серьезной программе, позволяющей выводить по точкам на экран в произвольном месте целое сообщение, высота и ширина символов которого может быть выбрана нами.
Эта программа состоит из двух блоков. Первый блок - настроечный, он выполнен на БЕЙСИКе, т.к. от него не требуется выстродействие. второй блок - основной, он выполнен в машинных кодах. Связь между блоками нужна для передачи основных параметров: высота и ширина символов (h,w), начальные координаты сообщения (x,y) и само сообщение (a$). И эта связь осуществляется благодаря тому, что эти параметры в БЕЙСИК-программе являются заданы программными переменными, а в подпрограмму в машинных кодах передается с помощью команд POKE.
Может быть, Вы желаете использовать только программу машинных кодах, тогда Бейсик-программу можно опустить, передавая при этом необходимые параметры прямо в область памяти, зарезервированной для буфера принтера. Это выглядит более профессионально, но наличие БЕЙСИК-программы позволяет упростить работу и дает читателю возможность поэкспериментировать с различными значениями вышеупомянутых параметров.
БЕЙСИК-программа приведена в листинге 2.6.
Блок в машинных кодах приведен в листингах 2.7.1 - 2.7.5 и представляет собой расширенную версию программы 2.4.
Машиннокодовый блок включает в себя следующие процедуры: FIND, SET, START, PLOT и SKIP. для простоты рассмотрим их по отдельности.
Процедура FIND (32335 - 32380) приведена в листинге 2.7.1. Она выполняет поиск значения БЕЙСИК-переменной по ее имени, которое должно быть предварительно задано. У нас имя искомой переменной задано в ячейке памяти 23728.
Это нужно сделать для того, чтобы передать из БЕЙСИКа в машинный код параметры x^,h,w и само текстовое сообщение.
Сама по себе задача передачи параметров из БЕЙСИКа в машинный код - весьма интересная и важная. Для этого существуют много разных способов. Наиболее распространенный прием - через параметры функции пользователя FN () мы рассмотрели и нашей книге "Элементарная графика" ("ИНФОРКОМ", М:, 1992, с. 111).
Здесь С. Николс предлагает прием передачи параметров через БЕЙСИК-переменные. Вот их поиском и занимается процедура FIND. Если Вам покажется сложным то, как она это делает, значит Вы не вполне знакомы с форматом БЕЙСИК-переменных, в котором они хранятся в памяти компьютера. Тогда посмотрите "7Х-РЕВЮ-92" на с. 166.
Процедура SET (32381-32392) приведена в листинге 2.7.2. Она служит для того, чтобы передать параметры x,y,b,w, разысканные процедурой FIND в области БЕЙСИК-переменных в область буфера принтера (весьма удобное место для хранения данных, обычно ничем не занятое в компьютере. Эта область расположена всегда в адресах 23396-23551). Сюда же передается и количество символов в выводимой на экран сообщении, а также коды символов сообщения. Наша основная программа будет работать с параметрами, беря их из области буфера принтера.
Выполнение машинного кода в программе 2.7 начинается с адреса 32393. Это стартовый адрес процедуры START. Она приведена в листинге 2.7.3. Сначала (32393...32418) регистровая пара BC определается в качестве указателя буфера принтера, после чего вызывается процедура SET для записи кода переменной в ячейку 23728. Затем (32419... 32442) определяется количество символов в переменной a$ и переписываются
коды всех символов в буфер принтера. Процедура (32443... 32456) проверяет текущее значение координаты у. Если это значение будет больше 175, то выполняется операция у-176 и новые значения координат x и у записываются в ячейки 23728/9 и 23296/7.
Процедура PLOT (Листинг 2.7.4) представляет собой расширенную версию процедуры вывода символа на экран по точкам с добавлением циклов вывода на зкран точки шириной w пикселов в h строках подряд.
Обратим внимание на то, что здесь неиспользуемые "Спектрумом" ячейки памяти 23728/9 служат для хранения текущих экранных координат позиции печати, а не для того, для чего они использовались в процедуре FIND.
Листинг 2.7.1
АДРЕС
FIND |
|
|
32335 |
2A |
4B |
L2 |
|
|
32338 |
3A |
B0 |
32341 |
BE |
|
32342 |
C8 |
|
32343 |
CB |
6E |
32345 |
20 |
08 |
32347 |
23 |
|
32340 |
5E |
|
32349 |
23 |
|
32350 |
56 |
|
32351 |
19 |
|
32352 |
23 |
|
32353 |
18 |
EF |
L1 |
|
|
32355 |
CB |
76 |
32357 |
20 |
0C |
32359 |
23 |
|
32360 |
7E |
|
32361 |
CB |
7F |
32363 |
28 |
FA |
32365 |
11 |
06 |
32368 |
19 |
|
38369 |
18 |
DF |
L3 |
|
|
32371 |
CB |
7E |
32373 |
28 |
F6 |
32375 |
11 |
13 |
32378 |
19 |
|
52379 |
18 |
D5 |
КОММЕНТАРИЙ
23627 - адрес системной перем. VARS, в которой хранится адрес, с которого начинается размещение переменных БЕЙСИКа.
Код имени БЕЙСИК-переменной. Сравнение и выход, если переменная найдена, в HL остается ее адрес. Если пятый бит в имени переменной равен нулю, то это либо массив, либо символьная строка. Нас они не интересуют, их надо пропустить. Следующие два байта содержат их длину. На нее мы и перескакиваем. Возврат на повтор поиска.
Если шестой бит не равен нулю, то это либо одно символьная переменная, либо параметр цикла. Выясним это, перейдя на L3. Если же это многосимвольная переменная, то пропускаем и ее и возвращаемся для дальнейших проверок на метку L2.
Если седьмой бит равен нулю, это односимвольная переменная (хоть и не наша.) Вернемся на 32365, пропустим 6 байтов и повторим поиск с L2. Седьмой бит равен единице
- это параметр цикла. В этом случае пропускаем 19 байтов и повторяем поиск с L2.
АССЕМБЛЕР ORG 32335
LD HL,(23627)
LD A,(23728) CP (HL) RET Z
BIT 5,(HL) JR NZ,L1 INC HL LD E,(HL) INC HL LD D,HL ADD HL,DE INC HL JR L2
BIT 6,(HL) JR NZ,L3
INC HL LD A,(HL) BIT 7,(HL) JR Z,-6 LD DE,6 ADD HL,DE JR L2
BIT 7,(HL) JR Z,-10
LD DE,19 ADD HL,DE
JR L2
Листинг 2.7.2
КОММЕНТАРИЙ
в стандартном "Спектруме" ячейки 23728/9 не используются
Мы храним в них имя переменной (х, у, h... ) Поиск значения переменной по ее имени. За именем переменной в БЕЙСИКе хранится ее значение в пятибайтной форме. Для целых чисел меньших 255 это форма:
CALL FIND
INC HL INC HL INC HL
|имя|00|Знак^|00|00|
| |
(HL) (HL)+3
Вот для чего нужны три команды INC HL подряд. Переброска параметра в буфер принтера. Выход из процедуры.
Процедура SKIP (Листинг 2.7.5) завершает подпрограмму. Она ведет учет изменения экранной координаты позиции печати. В частности, она осуществляет переход к следующей строке, а когда достигнут низ экрана (y>175), она выполняет переход на первую строку за счет операции "y-175".
Выше мы отметили, что в процедуре ПЗУ PLOT имеется еще одна точка входа по адресу 8924, с помощью которой можно напечатать на экране точку, координаты которой заданы на вершине стека калькулятора. Мы говорили о том, что это может быть удобным при печати результатов расчета калькулятора.
Не углубляясь в подробности работы процедуры CALCULATOR, рассмотрим работу программы, которая использует адрес 8924 для входа в процедуру PLOT.
Листинг 2.7.3
АДРЕС |
МАШ |
и. КОД |
АССЕМБЛЕР КОММЕНТАРИЙ |
START |
|
|
|
|
Точка входа в программу. |
32393 |
01 |
00 |
5B |
LD BC,23296 |
Начало буфера принтера. |
32396 |
3E |
7B |
|
LD A,120 |
120 - код буквы "x". |
32598 |
CD |
7D |
7E |
CALL SET |
в 23296 - коорд. "x". |
32401 |
03 |
|
|
INC BC |
Очередной байт буфера. |
32402 |
3E |
79 |
|
LD A,121 |
121 - код буквы y. |
32405 |
CD |
7D |
7E |
CALL SET |
в 23297 - коорд. "y". |
32407 |
03 |
|
|
INC BC |
Очередной байт буфера. |
32408 |
3E |
68 |
|
LD A,104 |
104 - код буквы h. |
32410 |
CD |
7D |
7E |
CALL SET |
В 23298 - высота "h". |
32413 |
03 |
|
|
INC BC |
Очередной байт буфера. |
32414 |
3E |
77 |
|
LD A,119 |
119 - код буквы w. |
32416 |
CD |
7D |
7E |
CALL SET |
в 23299 - ширина "w". |
32419 |
3E |
41 |
|
LD A,65 |
65 - код буквы A. |
52421 |
32 |
B0 |
5C |
LD (23728),A |
Подготовка и проведение |
32424 |
CD |
4F |
7E |
CALL FIND |
поиска переменной a$. |
32427 |
23 |
|
|
INC HL |
|
32428 |
5E |
|
|
LD E,(HL) |
В DE помещается длина |
32429 |
23 |
|
|
INC HL |
нашего сообщения и пере- |
32430 |
56 |
|
|
LD D,(HL) |
дается в буфер принтера |
32431 |
ED |
53 |
04 5B |
LD(23300),DE |
По адресу 23300. |
32435 |
D5 |
|
|
PUSH DE |
Переброска длины сообще- |
32436 |
C1 |
|
|
POP BC |
ния из DE в BC. |
32437 |
23 |
|
|
INC HL |
Переброска самого текста |
32438 |
11 |
05 |
5B |
LD DE,23301 |
сообщения в буфер принте |
32441 |
ED |
B0 |
|
LDIR |
ра начиная с 23301. |
32443 |
2A |
00 |
5B |
LD HL,(23296) |
В HL - координаты x,y. |
Причем y находится в H.
AF
7C
DE B0 38 04
LD H,A
LD (23296),HL LD (23728),HL
LD HL,23301
PUSH HL LD A,(HL) LD H,0 LD L,A ADD HL,HL ADD HL,HL ADD HL,HL LD DE,15360 ADD HL,DE
LD B,8
PUSH BC LD BC,(23297)
LD A,(HL) PUSH HL PUSH BC LD B,8
PUSH BC RLA
PUSH AF JP C,PLOT
LD HL,(23299)
LD A,(23728) ADD A,L LD (23728),A JP SKIP
67
22 00 5B 22 B0 5C
32459
21 05 5B
LP5 |
|
|
|
32462 |
E5 |
|
|
32463 |
7E |
|
|
32464 |
26 |
00 |
|
32466 |
6F |
|
|
3246т |
29 |
|
|
32468 |
29 |
|
|
32469 |
39 |
|
|
32470 |
11 |
00 |
3C |
32473 |
19 |
|
|
32474 |
06 |
08 |
|
LP4 |
|
|
|
32476 |
C5 |
|
|
52477 |
ED |
4B |
01 |
LP3 |
|
|
|
32481 |
7E |
|
|
32482 |
E5 |
|
|
32483 |
С5 |
|
|
32484 |
06 |
08 |
|
LP2 |
|
|
|
32486 |
С5 |
|
|
32487 |
17 |
|
|
32488 |
F5 |
|
|
32489 |
DA |
F9 |
7E |
32492 |
2A |
03 |
5B |
32495 |
3A |
B0 |
5C |
32498 |
85 |
|
|
32499 |
32 |
B0 |
5C |
32502 |
C3 |
0F |
7F |
Сброс флага переноса (это необходимо перед операцией SBC). Координата y. Проверка на >= 176. Если нет, то переход на адрес 32456 (все O.K.) В противном случае оставляем "y минус 176" и запоминаем копию в 23728/9. Итак, в 23728 - "x"; в 23729 - "y-176". Указание на начало текста
Запомнили начало текста. Взяли символ. Код символа передаем в HL и
умножаем его код на 8.
Нашли адрес, начиная с которого в ПЗУ хранится шаблон этого символа. Счетчик байтов в шаблоне
Запомнили его. в "B" - высота символа; в "C" - его коорд. "y".
Байт шаблона символа. Запомнили его. Запомнили "h" и "у". Счетчик битов в байте шаблона.
Запомнили текущий байт. Ротация байта. Запомнили результат ротации вместе с флагами. Если флаг переноса включился, переходим на 32505 для печати точки. В регистр "L" помещается ширина "w". Координата "x". Нашли "x+w" Запомнили в 23728. Обход.
Программа 2.8 демонстрирует построение по точкам синусоиды, описываемой формулой
88 + 8 0*SIN(A/128*PI)
с применением процедуры CALCULATOR.
С помощью этой процедуры можно выполнить сложные арифметические операции, используя так называемые коды калькулятора. Обычно при вызове этой процедуры выполняется операция над двумя числами, являвшимися верхними на калькулятор ном стеке, но возможны операции и над символьными величинами.
Например, если на вершине стека записаны два числа: 1234 и 2, а после включения процедуры CALCULATOR (RST 40) записан код 4 (DEFB 4), то эти два числа будут извлечены из калькуляторного стека, перемножены между собой, а их произведение будет записано обратно на вершину калькуляторного стека. Теперь на стеке будет записано только одно
число, равное 2468, если до этого там кроме сомножителей ничего не было записано.
Целые положительные числа могут быть занесены на стек следующими способами: STACK VAL A - CALL 11560 STACK VAL BC - CALL 11563,
т.е. число предварительно записывается в регистр а или регистровую пару BC, а затем вызывается соответствующая процедура.
В программе 2.8 используется вызов STACK VAL A для размещения на стеке чисел в следующем порядке:
переменная A 88 80
переменная A 128
Вызов процедуры CALCULATOR осуществляется по команде RST40. Байты, записанные после этой команды, определяют вид выполняемой операции. В программе 2.8 сразу же после команды RST 40 следует байт, равный 5, что означает "Деление". При этом из стека извлекаются два верхних числа, выполняется деление одного числа на другое и результат записывается обратно в калькуляторный стек. Следующий байт - 163 является кодом операции "stack PI/2"- он переписывает на стек значение PI/2, хранимое в ПЗУ. Затем следует код 49 (duplicate top value) - он дублирует верхнее число на стеке, а код 15 "add top two values" производит суммирование двух верхних чисел и записывает результат обратно на стек. Теперь на вершине стека записано число PI. Следующий код 4 (multiply top two values) перемножает два верхних числа, т.е. число PI и A/128, и результат возвращается на стек. Код 31 (SIN of top value) определяет синус числа на вершине калькуляторного стека, а последующие коды 4 и 15 завершает расчет по записанной формуле. Последний код 56 "end calc" выполняет выход из процедуры, т.е. как бы выключает калькулятор и осуществляет возврат в программу в машинных кодах.
Листинг 2. 7.4
КОММЕНТАРИЙ
LD BC, (23298)
PUSH BC LD BC,(23298) PUSH BC CALL 8933
POP BC INC C
PLOT 32505
LP1
32509
32510
32514
32515
32518
32519
32520
32524
32525
ED 4B B0 5C
C5
ED 4B B0 5C C5
CD E5 22
C1
0C
ED 43 B0 5C C1
10 EE
LD (23728),BC POP BC DJNZ LP1
АССЕМБЛЕР
Процедура печати точки. В "B" - ширина "w" В "C" - высота "h".
Запомнили на стеке. Повторили на стеке еще раз.
Вызов процедуры печати точки.
Переход к соседней точке.
Запомнили текущую координату.
Если не все точки напечатаны, возврат на 32509
КОММЕНТАРИЙ
Восстановили текущий байт шаблона.
Восстановили счетчик Уменьшили его и, если он не обнулялся, возврат на LP2 для последующей ротации. Координата "x".
Принимаем "x" в качестве текущей экранной коорди-
LD A,(23296) LD HL,23728 LD (HL),A
наты.
32536 |
23 |
|
|
INC HL |
Указание на "y". |
32539 |
AF |
|
|
XOR A |
Сброс флага переноса, |
32540 |
7E |
|
|
LD A,(HL) |
Координата "y" |
32541 |
DE |
B0 |
|
SBC A,176 |
Проверка на "y>175". |
32543 |
38 |
03 |
|
JR C,+3 |
Если так, переход к 32546 |
32545 |
77 |
|
|
LD (HL),A |
"y" |
32546 |
18 |
08 |
|
JR +8 |
Переход на 32556. |
32548 |
7E |
|
|
LD A,(HL) |
"y-176". |
32349 |
FE |
00 |
|
CP 0 |
Проверка на "ноль". |
32551 |
20 |
02 |
|
JR NZ,+2 |
Если не 0, переход на адрес 32555. |
32553 |
36 |
B0 |
|
LD (HL),176 |
|
32555 |
35 |
|
|
DEC (HL) |
|
32556 |
C1 |
|
|
POP BC |
|
32557 |
E1 |
|
|
POP HL |
|
32558 |
10 |
B1 |
|
DJNZ LP3 |
|
32560 |
23 |
|
|
INC HL |
Переход к следующему байту шаблона. |
32561 |
C1 |
|
|
POP BC |
Восстановили счетчик. |
32562 |
10 |
A8 |
|
DJNZ LP4 |
Если он еще не обнулился, переход на LP 4. |
32564 |
3A |
03 |
5B |
LD A,(E3299) |
Ширина символа "w". |
33567 |
87 |
|
|
ADD A,A |
Умножаем |
32566 |
67 |
|
|
ADD A,A |
ее |
32569 |
67 |
|
|
ADD A,A |
на 8. |
32570 |
6F |
|
|
LD L,A |
Запомнили в L. |
32571 |
3A |
B0 |
5C |
LD A,(25728) |
Новая экранная коорд. "x" |
32574 |
85 |
|
|
ADD A,L |
|
32575 |
32 |
00 |
5B |
LD (23296),A |
Запомнили ее. |
32578 |
32 |
B0 |
5C |
LD (23728),A |
- "" -- "" - |
32581 |
3A |
01 |
5C |
LD A,(23297) |
Новая экранная коорд. "7" |
32584 |
3A |
B1 |
5C |
LD (23729),A |
Запомнили ее. |
32587 |
E1 |
|
|
POP HL |
Восстановили указатель на текущий символ. |
32588 |
23 |
|
|
INC HL |
Переход к новому символу. |
32569 |
3A |
04 |
5B |
LD A,(23300) |
Длина сообщения. |
32592 |
3D |
|
|
DEC A |
Умножаем ее на 1. |
32593 |
C8 |
|
|
RET Z |
Проверка на конец. Если так, то выход. |
32594 |
32 |
04 |
5B |
LD (23300),а |
Запомнили новую длину. |
32597 |
C3 |
CE |
7E |
JP LP5 |
Возврат на печать очеред- |
;ного символа.
Существуют еще много других кодов калькулятора, которые могут быть использованы в Ваших программах для различных целей. С некоторыми из них мы еще познакомимся в следующих главах.
И еще одно важное замечание. Перед возвратом в BASIC-систему после применения процедуры CALCULATOR необходимо полностью очистить калькуляторыый стек от всех записей. В программе 2.8 после последнего кода 56 на стеке все же остаются два числа: переменная A и результат вычисления по формуле 88+80*SIN(A/128*PI). Процедура PLOT(CALL 6924) извлекает эти два значения, тем самым полностью очищая стек и использует эти два числа как координаты для пиксела.
Код программы для построения синусоиды приведен в листинге 2.8.
Листинг 2.8
АДРЕС |
МАШ |
. КОД |
АССЕМБЛЕР |
КОММЕНТАРИЙ |
23760 |
AF |
|
XOR A |
;Обнуление аккумулятора. |
LOOP |
|
|
|
|
23761 |
F5 |
|
PUSH AF |
;Запомнили его на стеке. |
23762 |
CD |
28 2D |
CALL 11560 |
;На кальк. стеке - текущее |
|
|
|
|
;значение аргумента. |
23765 |
3E |
5B |
LD A,88 |
;На кальк. стеке - |
23767 |
CD 28 2D |
CALL 11560 |
88, A. |
23770 |
3E 50 |
LD A,80 |
На кальк. стеке - |
23772 |
CD 28 2D |
CALL 11560 |
80, 88, A. |
23775 |
F1 |
POP AF |
B акк-ре текущее значение аргумента. |
23776 |
F5 |
PUSH AF |
Запомнили его на стеке. |
23777 |
CD 28 2D |
CALL 11560 |
A, 80, 88, A. |
23780 |
3E 80 |
LD A,128 |
|
23782 |
CD 28 2D |
CALL 11560 |
128, A, 80, 88, A. |
23765 |
EF |
RST 40 |
Включение калькулятора. |
23766 |
DEFB 05 |
division |
A/128, 80, 88, A. |
23787 |
DEFB 163 |
stack PI/2 |
PI/2, A/128, 80, 88, A. |
23788 |
DEFB 49 |
duplicate |
PI/2, PI/2, а/128, 80, 88, A |
23789 |
DEFB 15 |
add |
PI, A/128, 80, 88, A. |
23780 |
DEFB 04 |
multiply |
PI*A/128, 80, 88, A. |
23781 |
DEFB 31 |
Sin |
SIN(A/128*PI), 80, 88,A. |
23782 |
DEFB 04 |
multiply |
80*SIN(A/128*PI), 88, A. |
23783 |
DEFB 15 |
add |
88+80*SIN(A/128*PI), A. |
23784 |
DEFB 56 |
endcalc |
Выключение калькулятора. |
23795 |
CD DC 22 |
CALL 8924 |
Вызов процедуры PLOT. |
23798 |
F1 |
POP AF |
Текущее значение аргумента. |
23799 |
3C |
INC A |
Приращение аргумента. |
23800 |
FE 00 |
CP 0 |
Проверка на конец экрана. |
23802 |
20 D5 |
JR NZ,LOOP |
Возврат для расчета и печати следующей точки. |
23804 |
C9 |
RET |
Выход из процедуры. |
DRAW x,y.
Две точки входа в ПЗУ существуют и для реализации команды DRAW х,у. Для входа через первую точку (CALL 9402) необходимо, чтобы в регистре B было записано ABS(y), а в регистре C - ABS (х). Регистровая пара DE служит для хранения знака приращения координат х и у. Регистр D хранит знак приращения по х (SGN dx), а регистр C - знак приращения по у (1, если число положительное и 255, если число отрицательное). Программа 2.9 является модифицированной версией программы 2.8, которая демонстрирует реализацию команды DRAW. Обратите внимание, что перед вызовом процедуры DRAW, начальное значение регистровой пары H'L' (альтернативная) должно быть сохранено. Для этого содержимое этой пары записывается в машинный стек.
Начальное значение регистровой пары H'L' не должно быть потеряно в ходе выполнения программы, написанной пользователем, иначе при возврате в BASIC-систему произойдет сбой в работе компьютера. Начальное значение регистровой пары H'L' восстанавливается после команды DRAW. Команды OVER и INVERSE включаются таким же образом, как и PLOT OVER/INVERSE.
Для реализации команды DRAW х,у через вторую точку входа (CALL 9335) необходимо, чтобы значения х и у были записаны в калькуляторном стеке, причем значение у должно быть верхним в стеке. Чтобы использовать эту точку входа, необходимо прочитать главу 7, для ознакомления со способом записи 5-ти байтных чисел в калькуляторный стек.
Листинг 2.9
АДРЕС |
МАШ |
и. КОД |
АССЕМБЛЕР КОММЕНТАРИЙ |
23760 |
06 |
5F |
LD B,95 |
Координата "y". |
23762 |
0E |
00 |
LD C,00 |
Координата "x". |
23764 |
CD |
E5 22 |
CALL 8933 |
Печать исходной точки. |
23767 |
D9 |
|
EXX |
Включение регистров |
|
|
|
|
альтернативного набора. |
23768 |
E5 |
|
PUSH HL |
Сохранение пара H'L'. |
23769 |
D9 |
|
EXX |
Выключение альтернативного набора |
23770 |
06 |
00 |
LD B,0 |
Приращение по "y"=0. |
23772 |
0E |
FF |
LD C,255 |
Приращение по "x" = 255. |
23774 |
16 |
01 |
LD D,1 |
Знак приращ. по x=+. |
23776 |
1E |
01 |
LD E,1 |
Знак приращ. по y=+. |
23778 |
CD BA 24 |
CALL 9402 |
Рисуем прямую линию. |
23781 |
D9 |
EXX |
Включение регистров |
|
|
|
альтернативного набора. |
23782 |
E1 |
POP HL |
Восстановление пары H'L'. |
23783 |
D9 |
EXX |
Выключение альтернативного набора |
23784 |
AF |
XOR A |
Обнуление аккумулятора. |
LOOP |
|
|
|
23765 |
F5 |
PUSH AF |
Запомнили его на стеке. |
23766 |
CD 28 2D |
CALL 11560 |
На кальк. стеке - текущее |
|
|
|
значение аргумента. |
23789 |
3E 78 |
LD A,120 |
На кальк. стеке - |
23791 |
CD 28 2D |
CALL 11560 |
120,A. |
23794 |
3E 28 |
LD A,40 |
На кальк. стеке - |
23796 |
CD 28 2D |
CALL 11560 |
40, 120, A. |
23799 |
F1 |
POP AF |
в акк-ре текущее значение |
|
|
|
аргумента. |
23600 |
F5 |
PUSH AF |
Запомнили его на стеке. |
23601 |
CD 28 2D |
CALL 11560 |
A, 40, 120, A. |
23804 |
3E 10 |
LD A,16 |
|
23806 |
CD 28 2D |
CALL 11560 |
16, A, 40, 120, A. |
23809 |
EF |
RST 40 |
Включение калькулятора. |
23810 |
DEFB 05 |
division |
A/16, 40, 120, A. |
23811 |
DEFB 163 |
stack PI/2 |
PI/2, A/16, 40, 120, A. |
23813 |
DEFB 49 |
duplicate |
PI/2, PI/2, A/16, 40, 120, A. |
23813 |
DEFB 15 |
add |
PI, A/16, 40, 120, A. |
23814 |
DEFB 04 |
multiply |
PI*A/16, 40, 120, A. |
23815 |
DEFB 31 |
Sin |
SIN(A/16*PI), 10, 120, A. |
23816 |
DEFB 04 |
multiply |
40*SIN(A/16*PI), 120, A. |
25617 |
DEFB 15 |
add |
120+40*SIN (A/16*PI), A. |
23618 |
DEFB 56 |
endcalc |
Выключение калькулятора. |
23819 |
CD DC 22 |
CALL 8924 |
Вызов процедуры PLOT. |
23822 |
D9 |
EXX |
Включение альтерн. набора |
23823 |
E5 |
PUSH HL |
Сохраняя H'L'. |
23824 |
D9 |
EXX |
Выключение альтерн. наб. |
23825 |
06 32 |
LD B,50 |
Приращение по "y". |
23827 |
0E 00 |
LD C,0 |
Приращение по "x". |
23829 |
16 FF |
LD D,255 |
Знак приращения по "y" |
|
|
|
"минус". |
23831 |
1E 01 |
LD E,01 |
Знак приращения по "х" - |
|
|
|
"плюс". |
23833 |
CD BA 24 |
CALL 9402 |
Рисование линии. |
23836 |
D9 |
EXX |
Включение альтерн. набора |
23837 |
E1 |
POP HL |
Восстановление пары H'L'. |
23838 |
D9 |
EXX |
Выключение альтернатив- |
|
|
|
ного набора. |
23839 |
F1 |
POP AF |
Текущее значение аргумента. |
23840 |
3C |
INC A |
Приращение аргумента. |
23841 |
FE 00 |
CP 0 |
Проверка на конец экрана. |
23843 |
20 D5 |
JR NZ, LOOP |
Возврат для расчета и |
|
|
|
печати следующей точки. |
23845 C9 |
|
RET |
Выход из процедуры. |
DRAW x,y,a
Точкой входа в ПЗУ для реализации этой команды служит адрес 9106. В этом случае необходимо, чтобы значения x, y и а были записаны в калькуляторный стек в таком порядке, чтобы значение "a" было верхним на стеке. Опять же, начальное значение регистровой пары H'L' (альтернативной) должно быть сохранено.
CIRCLE x,y,r
Точкой входа в ПЗУ для реализации этой команды служит адрес 9005. При этом необходимо, чтобы значения x,y, и r были записаны в калькуляторном стеке. Начальное значение регистровой пары H'L' должно сохраниться в отдельном месте в течение работы
процедуры CIRCLE.
Программа 2.10 демонстрирует метод рисования концентрических окружностей. Обратите внимание на тот факт, что эта программа в машинных кодах выполняется также медленно, как и на языке BASIC. Это потому, что процедура CIRCLE в ПЗУ достаточно длинная. Если в Вашей программе возникнет необходимость построить окружность, то будет быстрее, если Вы будете строить ее по точкам, используя для постановки пиксела координаты, записанные как последовательность байтов с именем DATA.
Машинный код процедуры представлен в листинге 2.10. В качестве аналога выступает следующая БЕЙСИК-программа: 10 REM CIRCLE x,y,r 20 FOR b= 1 TO 2 30 FOR a= 1 TO 21 STEP 2 40 CIRCLE OVER 1; 128,88,a 50 NEXT а 60 NEXT b
Листинг 2.10
АДРЕС |
МАШ |
и. КОД |
АССЕМБЛЕР КОММЕНТАРИЙ |
23760 |
06 |
02 |
|
LD B,2 |
FOR b=1 TO 2 |
L2 |
|
|
|
|
|
23762 |
C5 |
|
|
PUSH BC |
|
23763 1 1 |
3E |
01 |
|
LD A,1 |
FOR a=1 TO ... |
L1
23765 |
F5 |
|
|
PUSH AF |
|
23766 |
FD |
36 |
57 03 |
LD (IY+87),3 |
OVER 1 |
23770 |
3E |
80 |
|
LD A,128 |
|
23772 |
CD |
28 |
2D |
CALL 11560 |
На кальк. стеке - 128. |
23775 |
3E |
58 |
|
LD A,88 |
|
23777 |
CD |
28 |
2D |
CALL 11560 |
На кальк. стеке - 88,128 |
23780 |
F1 |
|
|
POP AF |
текущее "а". |
23781 |
F5 |
|
|
PUSH AF |
|
23782 |
CD |
28 |
2D |
CALL 11560 |
На к. стеке - а, 88, 128 |
23785 |
D9 |
|
|
EXX |
|
23786 |
E5 |
|
|
PUSH HL |
Сохранение H'L'. |
23787 |
D9 |
|
|
EXX |
|
23788 |
CD |
2D |
23 |
CALL 9005 |
CALL CIRCLE |
23791 |
D9 |
|
|
EXX |
|
23792 |
E1 |
|
|
POP HL |
Восстановление H'L'. |
23793 |
D9 |
|
|
EXX |
|
23794 |
F1 |
|
|
POP AF |
Текущее "a". |
23795 |
3C |
|
|
INC A |
|
23796 |
3C |
|
|
INC A |
STEP 2 |
23797 |
FE |
17 |
|
CP 23 |
a = 23? |
23794 |
20 |
DC |
|
JR NZ, L1 |
Цикл не завершен. |
23801 |
C1 |
|
|
POP BC |
цикл по "A" завершен. |
23802 |
10 |
D6 |
|
DJNZ L2 |
Конец цикла по "b". |
23804 |
FD |
36 |
57 00 |
LD (IY+87),D |
- OVER 0. |
23808 |
C9 |
|
|
RET |
Выход |
3. Счет
Во многих игровых программах необходимо постоянно вести счет, например, счет очков, потерянных "жизней", времени и т.д. Известны, по крайней мере, три способа организации счета в программах и вывода результатов счета на экран.
Первый настолько непригляден и занимает так иного памяти, что мы рассмотрим его кратко, лишь в общих чертах. Способ этот позволяет организовать счет в программе от 0 до очень большого числа. Максимально возможное число определяется только количеством байтов, зарезервированных для представления этого числа. Например, для того, чтобы получить счетчик от 0 до 999 999, необходимо зарезервировать 6 байтов, т. е. для каждого разряда числа нужен 1 байт. Поскольку показание счетчика выводится на экран как значение символьной переменной, то на экране в начальный момент времени будет 6 нулей: 000 000. Затем, с увеличениен значения на 1, изменения произойдут только в разряде единиц. Если же последнее записанное в этом разряде число равно 9, то с добавлением 1 этот разряд обнулится, а единица добавится к числу в следующем разряде. Продедура проверки значения числа выполняется для каждого разряда и, если во всех шести разрядах записано число 9, то дальнейший счет прекращается. Для того, чтобы шаг счетчика был больше, чем 1, необходимо включить в процедуру счета специальные циклы.
Второй способ используется для создания счетчиков, показания которых не выходят за пределы диапазона 0...65535. Реализация этого способа показана в программе 3.1. В этой программе используется одна из процедур ПЗУ, позволяющая выводить на экран значение числа, записанного на вершине калькуляторного стека. Вызов этой процедуры осуществляется командой CALL 11747. При этом текущее значение счетчика может, например, храниться в неиспользуемой системной переменной 23726. Значение счетчика извлекается из этой системной переменной, увеличивается на 1, а затем вновь записывается в эту переменную и на вершину калькуляторного стека. После этого определяются параметры PRINT AT и вызывается процедура 11563, печатающая содержимое стека калькулятора.
LD BC,0
BEGIN
LD (23728),BC CALL 11563
LD A,2 CALL 5633 LD A,22 RST 16 LD A,16 RST 16 CALL 11747 LD BC,(23728) INC BC LD A,B OR C RET Z
BIT 5,(IY+1) JR Z, BEGIN RET
Листинг 3. 1
Инициализация счетчика по адресу 23728. Переброска на стек калькулятора Открыли канал печати на экран.
Аналог ... AT 22
Аналог AT 22,16 Печать счетчика. Вызов счетчика. Приращение. Проверка на обнуление. Выход, если 0. Проверка не была ли нажата клавиша Повтор, если не было нажатия. Выход, если клавиша была нажата.
Если в регистровой паре значение числа становится равным 65535, то осуществляется возврат в BASIC систему. В эту программу включена также процедура EXIT (строки 23795...23800), благодаря которой имеется возможность прервать счет и вернуться в BASIC систему, если нажата какая-нибудь клавиша. Это удобно, так как счет от 0 до 65535 выполняется несколько минут.
Третий способ также основан на использовании калькулятора. Выше, когда мы говорили о калькуляторном стеке, мы не останавливались на детальном рассмотрении способа записи и хранения чисел в стеке. Числа в стеке хранятся в 5-байтной форме, что позволяет калькулятору легко выполнять действия с ними. Более подробно об этом будет сказано в седьмой главе. Здесь же приводим программу, которая выполняет счет от 0 до бесконечно большого числа с шагом 250. При достижении показания счетчика 99 999 999, форма вывода на экран меняется на E-форму (1E+9). Однако счетчик, у которого диапазон представляемых чисел достигает 100 млн. вполне пригоден для большинства программ и на этот факт можно не обращать внимание.
В программе 3.2 есть много важных процедур, которые необходимо объяснить. Начальное значение счетчика, равное нулю, записывается в стек с помощью кода калькулятора 160 (stack_zero). Это число занимает 5 верхних байтов стека, содержимое которых затем копируется в буфер принтера, используемого как "хранилище" для счетчика. Копирование выполняется командой LDIR. Значение, записанное в регистровой паре HL при этом считается равным STACKEND-5.
Значение счетчика выводится на экран с помощью процедуры 11747. Затем в стек записывается число 250 (шаг изменения показания счетчика) и число из "хранилища" в 5-байтной форме. Следующая команда вызова калькулятора RST 40 и код калькулятора 15(add) обеспечивают суммирование двух чисел, записанных на вершине стека, и осуществляется переход на начало цикла для передачи нового значения в "хранилище" и вывода этого значения на экран. В этой программе предусмотрена также процедура преждевременного выхода из цикла счета, если будет нажата любая клавиша. Перед возвратом в BASIC-систему после окончания счета стек обнуляется путем перемещения последнего записанного там значения.
Для создания счетчика, значение которого с каждым шагом уменьшается, необходимо в стек первоначально записать максимальное значение счетчика (запись в стек чисел больше, чем 65535, показана в главе 7), а вместо кода калькулятора для сложения - 15 (add) следует использовать код для вычитания 3 (subtract). Необходимо помнить, что при вычитании верхнее в стеке число вычитается из числа, записанного вторым. После каждой операции вычитания проверяется достигнут ли 0 (либо включен ли 7 бит результата) и если это так, то счет прекращается. Перед выводом очередного значения счетчика необходимо очищать экран, чтобы избежать ошибок, связанных с устареванием экранной информации. Например, если экран не очищать, то при изменении значения счетчика с 1000 на 999 на экране будет изображено не 999, а 9990.
Листинг 3.2
ORG 23760 RST 40 DEFB 160 DEFB 56
LD DE,23296
LD BC,5
LDIR LD A,2 CALL 5633 LD A,22 RST 16 LD A,11 RST 16 CALL 11747
LD A,250 CALL 11560
LD HL,23296
LD DE,(23653)
LD BC,5 LDIR
LD (23653),DE
RST 40 DEFB 15
включили кальк-р. На стек - 0. Выключили кальк-р.
начало буфера принтера. Число перебрасываемых байтов. Переброска. Канал экрана. Открыли канал. ... AT 22 ...
...AT 22,11
Печать показаний счетчика. Шаг счетчика. Поместили его на стек кальк-ра. Начало буфера принтера.
23553 - системная переменная STKEND. Число перебрасываемых байтов. Переброска. Новое значение STKEND.
Включили кальк-р. Код сложения (add)
Выключение к-ра. Проверка не была ли нажата какая -либо клавиша. Если нет, то повтор. (STKEND)
JR Z,L1
LD HL,(23653) DEC HL DEC HL DEC HL DEC HL DEC HL
LD (23553),HL RET
И завершим мы материал этого номера небольшой игрой, в которой объединим то, о чем Вы сегодня прочитали. Программа 3.3 показывает возможность использования счетчика для измерения скорости Вашей реакции.
Программа состоит из двух частей: первая часть написана на БЕЙСИКе и служит в качестве интерфейса между пользователем и компьютером. Она не требует скорости в работе. Машинный код служит для выдачи сообщений и запросов нестандартным шрифтом и для измерения скорости реакции. Печать нестандартным шрифтом выполняется с помощью блока машинного кода, приведенного в листинге 2.7, рассмотренного ранее. Он должен быть подгружен, начиная с адреса 32335. измерение скорости реакции выполнит процедура 3.3.2, приведенная ниже.
Измерение скорости вашей реакции производится следующим образом. Сначала процедура 2.7 нарисует на экране большую букву от A до Z, после чего будет включен режим CAPS LOCK и Вам надо нажать соответствующую клавишу. Время Вашей реакции замерит процедура 3.3.2.
В ней применен один хитрый прием. Дело в том, что рисование с помощью PLOT буквы на экране происходит не очень быстро, и за то время, пока идет этот процесс, Вы можете в принципе догадаться, что это за буква и подготовиться к нажатию соответствующей клавиши. Чтобы этого не было, буква рисуется желтым цветом INK=6 по желтому фону PAPER=6 и ее на экране не будет видно. Когда же дело дойдет до измерения Вашей реакции, процедура 3.3.2 включит на экране цвет пикселов INK=1 (черный). Произойдет это с огромной скоростью и буква появится мгновенно.
БЕЙСИК-программа стартует со строки 9800, в которой записана подгрузка модулей в машинных кодах. Поэтому набрав Бейсик выгрузите его на ленту со строкой автостарта 9800:
SAVE "counter" LINE 9800
Листинг 3.3.1
12 PAPER 6: CLS 15 DATA 87,143,1,2, "COUNT" 20 DATA 95,63,1,2, "SLOW" 30 DATA 71,63,1,2, "AVERAGE" 40 DATA 95,63,1,2, "GOOD" 50 DATA 63,63,1,2, "VERY GOOD" 6O DATA 63,63,1,2, "EXCELLENT" 80 DATA 47,63,1,2, "NOT TRYING" 9O DATA 0,31,1,2, "ANOTHER GO? y/n"
95 DATA 55,125,5,2, "THANK YOU"
96 DATA 103,79,3,2, "for"
97 DATA 71,50,5,2, "PLAYING"
100 DATA 24,79,5,2, "STOP THE TAPE" 105 DATA 15,167,1,2, "REACTION TIMER"
110 DATA 13,164,2,2, "________________________________"
120 DATA 24,15,1,2, "PRESS ANY KEY"
125 RESTORE 100: FOR a=1 TO 4:PAUSE 25: INK 2: GO SUB 9000:NEXT a 180 PAUSE 0: BORDER 5: PAPER 6: INK 0: CLS
190 RESTORE 105: INK 0: GO SUB 9000 290 PRINT AT 20,5; "PRESS ANY KEY TO PLAY" 300 IF INKEY$<>"" THEN GO TO 300 302 IF INKEY$="" THEN GO TO 302
310 CLS: RESTORE 105: INK 1: GO SUB 9000: INK 5: GO SUB 9000 330 RESTORE 15: INK 2: GO SUB 9000
350 INK 0: PLOT 102,114: DRAW 50,0: DRAW 0,-38: DRAW 50,0: DRAW 0,38 380 LET z=INT (RND*26+65)
390 LET x-111: LET y=111: LET h=4: LET w=4: LET a$=CHR$ z
400 INK 6: LET c=USR 33393: FOR a=1 TO RND*200+50: NEXT a
420 INK 0: LET c=USR 30000: LET a=PEEK 23728+256*PEEK 237729
450 IF a>=140 OR a=0 THEN RESTORE 20
460 IF a<140 THEN RESTORE 20
470 IF a<120 THEN RESTORE 30
480 IF a<100 THEN RESTORE 40
490 IF а<85 THEN RESTORE 50
500 IF a<70 THEN RESTORE 60
510 GO SUB 9000
7000 RESTORE 90:INK 1:GO SUB 9000 7010 IF INKEY$<>"" THEN GO TO 7010 7020 IF INKEY$="" THEN GO TO 7020 7030 LET d$=INKEY$ 7040 IF d$ <> "y" THEN GO TO 8000 7050 LET c=USR 30082: GO TO 350
8000 RESTORE 95: POKE 30083,20: LET c=USR 30082: POKE 30083,18 8030 FOR a=1 TO 3: GO SUB 9000: NEXT a: STOP 9000 READ x, y, h, w, a$: LET c=USR 32393: RETURN 9800 CLEAR 29999: LOAD ""CODE: GO TO 12
МАШ. КОД
3E 7A 01 81 5C
CD 7D 7E
FD CB 30 DE
30012 |
21 |
00 |
58 |
LD HL,22528 |
L1 |
|
|
|
|
30015 |
7E |
|
|
LD A,(HL) |
30016 |
FE |
36 |
|
CP 54 |
30018 |
20 |
02 |
|
JR NZ,L2 |
30020 |
36 |
30 |
|
LD (HL),48 |
L2 |
|
|
|
|
30022 |
23 |
|
|
INC HL |
30023 |
7C |
|
|
LD A,H |
30024 |
FE |
5B |
|
CP 91 |
30026 |
20 |
F3 |
|
JR NZ,L1 |
30028 |
01 |
00 |
00 |
LD BC,0 |
L3 |
|
|
|
|
30031 |
C5 |
|
|
PUSH BC |
30032 |
CD |
2B |
2D |
CALL 11563 |
30035 |
3E |
02 |
|
LD A,2 |
30037 |
CD |
01 |
16 |
CALL 5633 |
30040 |
3E |
16 |
|
LD A,22 |
30042 |
D7 |
|
|
RST 16 |
Листинг 3.3.2
КОММЕНТАРИЙ
;Код буквы "z" ;Для хранения параметра "z" ;выбирается неиспользуемая ;в стандартном "Спектруме" ;ячейка 23681. ;Параметр "z" перебрасыва-;ется в 23681 (см. процедуры 2.7.1, 2.7.2.) ;Включение 3-го бита сис-;темной переменной FLAGS2 ;(23656) эквивалентно включению режима CAPS LOCK. ;Первый байт области цветовых атрибутов экрана.
;Проверка этого байта. Переключение атрибутов ;экранной области с ре-;жима PAPER = 6, INK = 6 ;на режим PAPER =6. ;INK = 0.
;Ранее нарисованная бук-;ва мгновенно "проявля-;ется" на экране. ;Инициализация счетчика,
Помещение его на стек калькулятора. Открываем канал печати на экран. Аналог ... AT ...
30043 |
3E |
06 |
|
|
LD A,6 |
Аналог ... AT 6... |
30045 |
D7 |
|
|
|
RST 16 |
|
30046 |
3E |
0E |
|
|
LD A,14 |
|
30046 |
D7 |
|
|
|
RST 16 |
Аналог ... AT 6,14 |
30049 |
CD |
E3 |
2D |
|
CALL 11747 |
Печать показаний счетчика. |
30052 |
C1 |
|
|
|
POP BC |
|
30053 |
03 |
|
|
|
INC BC |
Увеличение счетчика. |
30054 |
78 |
|
|
|
LD A,B |
Проверка счетчика на об |
30055 |
B1 |
|
|
|
OR C |
нуление. |
30056 |
28 |
0F |
|
|
JR Z,END |
Выход, если он успел обну-литься (достиг 256). |
30058 |
FD |
CB |
01 |
6E |
BIT 5,(IY+1) |
Проверка не была ли нажата какая-либо клавиша. |
30062 |
28 |
DF |
|
|
JR Z,L3 |
Если нет, то возврат на повтор. |
30064 |
3A |
08 |
5C |
|
LD A,(23560) |
Код нажатой клавиши. |
30067 |
21 |
81 |
5C |
|
LD HL,23681 |
Код буквы, которая была показана на экране. |
30070 |
BE |
|
|
|
CP (HL) |
Сравнили их между собой. |
30071 |
20 |
06 |
|
|
JR NZ,L3 |
Если они не совпадают, значит нажата не та клавиша. Следует повторить попытку. |
END |
|
|
|
|
|
|
30073 |
ED |
43 |
B0 |
5C |
LD(23728),BC |
Результат испытания передается из BC в адрес 23728 |
30077 |
FD |
CB |
30 |
9E |
RES 3,(IY+48) |
Выключается ранее включенный режим CAPS LOCK. |
30081 |
C9 |
|
|
|
RET |
Выход из программы. |
13082 |
06 |
12 |
|
|
LD B,18 |
Эта вспомогательная проце- |
30084 |
CD |
44 |
0E |
|
CALL 3652 |
дура служит для очистки |
30087 |
C9 |
|
|
|
RET |
k нижних строк экрана, где k-число, установленное в адресе 30083 (исходно 18). |