; Адрес 32766=127ґ256+254, в B заносится адрес полуряда, ; а в C - адрес порта (254). KEY LD BC,32766 IN A,(C) ; Один из способов проверки данного бита - у отпущенной клавиши ; бит установлен (1), у нажатой сбрасывается в 0 BIT 2,A JR NZ,KEY RETВторой способ принципиально не отличается от первого. Перед чтением в аккумулятор помещается старший байт адреса соответствующего порта, а младший байт задается в явном виде в команде IN: KEY LD A,#7E ;в аккумулятор заносится старший байт ; адреса порта #7EFE IN A,(254) ;считывание из порта (254 или #FE - ; младший байт адреса) BIT 2,A ;проверка нажатия третьей от края ; клавиши (M) JR NZ,KEY RETРассмотрим программу, в которой при нажатии клавиш Q, A, O и P изменяются координаты точки на экране. Сами точки будем ставить в бейсик-программе, которую напишем позже, но подразумевая использование процедуры из Бейсика, воспользуемся для передачи координат точки, как и раньше, областью буфера принтера, определив адрес передаваемых параметров константой XY. ORG 60000 XY EQU 23296 KEY LD HL,(XY) ;запись координат точки в HL ; В регистр A заносится старший байт полуряда, ; в котором располагается клавиша Q LD A,251 IN A,(254) ;читаем из порта значения для полуряда ; Проверка бита 0 (команду RRCA вместо BIT здесь удобнее применять ; потому, что клавиша Q в полуряду занимает крайнее положение) RRCA ; Если клавиша не нажата (на что указывает установленный бит), ; то следующую команду пропускаем JR C,KEY1 ; Увеличиваем значение вертикальной координаты, которое находится в регистре H INC H KEY1 LD A,253 IN A,(254) RRCA ;клавиша A JR C,KEY2 DEC H ;уменьшаем вертикальную координату KEY2 LD A,223 IN A,(254) RRCA ;клавиша P JR C,KEY3 INC L ;увеличиваем горизонтальную координату ; Так как клавиши P и O находятся в одном полуряду, ; то выполнять команду IN дважды нет необходимости KEY3 RRCA ;клавиша O JR C,KEY4 DEC L ;уменьшаем горизонтальную координату KEY4 LD (XY),HL LD A,127 IN A,(254) BIT 2,A RET NZ ;выход, если клавиша M не нажата JP 3435 ; иначе очищаем экранЧтобы увидеть эту процедуру в действии, необходимо дополнить ее небольшой бейсик-программкой, задача которой состоит только в том, чтобы ставить на экране точку в соответствии с координатами (XY), полученными в ассемблерной программе. 100 POKE 23296,100: POKE 23297,100 110 PLOT PEEK 23296, PEEK 23297 120 RANDOMIZE USR 60000: GO TO 110Попробуйте ее ввести и исполнить, а затем понажимайте клавиши Q, A, O и P - по экрану в разных направлениях потянутся четкие прямые линии подобно использованию функции PEN в графическом редакторе. Нажав клавишу M, в любой момент можно очистить экран и начать рисовать новую «картину». В заключение этого раздела приведем еще один пример управления с помощью клавиатуры, с которым мы иногда встречаемся, загружая те или иные игровые программы. Он полезен еще и тем, что дает вариант решения некоторых побочных проблем, таких, например, как учет ограничений на перемещение курсора (или спрайта) по экрану, введение дополнительных функций управления и некоторые другие.
Рис. 8.1. Ввод имени играющего Представим себе, что в конце игры необходимо набрать имя играющего, чтобы затем записать его в раздел меню HI SCORE. Для этого, при достижении определенных результатов, вызывается кадр, в котором вы видите примерно такую таблицу, какая изображена на рис. 8.1. Далее, управляя курсором с помощью клавиш Q, A, O и P, требуется выбрать из таблицы буквы вашего имени, нажимая после каждой клавишу выбора M. При этом набранные буквы из таблицы будут переноситься в строку, расположенную ниже. Если какой-то символ набран неверно, его можно стереть, «нажав» в таблице букву d (delete), для печати пробела используется буква s (space), а для ввода имени и завершения этой части программы - буква e (enter). Надо сказать, что такой способ ввода имени не самый удобный, однако он имеет право на существование в случаях, когда играющий еще плохо знаком с клавиатурой ZX Spectrum, но имеет некоторое представление о латинском алфавите. ORG 60000 ENT $ XOR A CALL 8859 LD A,68 LD (23693),A CALL 3435 LD A,2 CALL 5633 ; Очистка строки для ввода имени LD HL,NAME LD DE,NAME+1 LD BC,19 LD (HL)," " LDIR ; Вывод таблицы символов в рамке CALL TABL CALL LINES LD A,68 LD (23693),A LD BC,#506 ;начальные координаты курсора в таблице LD E,0 ;номер символа в строке ввода SET 3,(IY+48) ;режим ввода прописных букв ; Управление курсором и печать выбранного символа в строку KEYS CALL SETCUR ;вывод курсора XOR A LD (23560),A WAIT LD A,(23560) ;ожидание нажатия клавиши AND A JR Z,WAIT CP "P" ;перемещение курсора на JR Z,RIGHT ; один шаг вправо CP "O" ;перемещение курсора JR Z,LEFT ; на один шаг влево CP "Q" ;перемещение курсора JR Z,UP ; на один шаг вверх CP "A" ;перемещение курсора JR Z,DOWN ; на один шаг вниз CP "M" ;печать выбранного символа JR Z,SELECT ; в строке ввода JR KEYS ; Перемещение курсора вправо RIGHT LD A,C ;проверка достижения курсором CP 24 ; правой границы таблицы JR NC,KEYS CALL RESCUR ;удаление курсора на прежнем месте INC C ;изменение положения курсора INC C CALL SETCUR ;установка курсора на букву таблицы JR KEYS ; Перемещение курсора влево LEFT LD A,C ;проверка достижения курсором CP 7 ; левой границы таблицы JR C,KEYS CALL RESCUR DEC C DEC C CALL SETCUR JR KEYS ; Перемещение курсора вверх UP LD A,B ;проверка достижения курсором CP 6 ; верхней границы таблицы JR C,KEYS CALL RESCUR DEC B DEC B CALL SETCUR JR KEYS ; Перемещение курсора вниз DOWN LD A,B ;проверка достижения курсором CP 11 ; нижней границы таблицы JR NC,KEYS CALL RESCUR INC B INC B CALL SETCUR JR KEYS ; Выбор символа, который затем будет напечатан в строке или выбор ; функции для редактирования этой строки SELECT PUSH BC PUSH DE CALL SND ;звуковой сигнал, издаваемый при ; перемещении символа из таблицы в ; набираемую строку POP DE POP BC LD A,B CP 11 JR NZ,MOVE ;печать символа LD A,C CP 20 JR Z,DELETE ;удаление символа в строке CP 22 JR Z,SPACE ;печать пробела в строке CP 24 RET Z ;выход из программы ; Перемещаем символ из таблицы в набираемую строку и смещаем курсор ; на позицию вправо, при этом делаем проверку того, чтобы символ ; не вышел за заданные границы строки (слева и справа). MOVE LD A,E CP 20 JP NC,KEYS LD D,0 PUSH BC PUSH DE LD A,B ;по вертикальной координате курсора ; определяем адрес данных строки ; таблицы (STR1, STR2, STR3 или STR4) SUB 5 LD HL,D_STR LD E,A ADD HL,DE LD E,(HL) INC HL LD D,(HL) EX DE,HL LD A,C ;по горизонтальной координате находим ; код символа в блоке данных SUB 6 LD C,A LD B,0 ADD HL,BC POP DE POP BC LD A,(HL) ;помещаем код символа в A LD HL,NAME ;определяем адрес в строке NAME ADD HL,DE ; для ввода символа LD (HL),A ;помещаем символ в строку ввода CALL PR_STR ;выводим строку ввода на экран INC E ;смещаем позицию ввода вперед JP KEYS ; Удаление неправильно набранного символа DELETE LD A,E ;проверка достижения начала строки ввода AND A JP Z,KEYS DEC E ;уменьшаем позицию ввода LD D,0 LD HL,NAME ADD HL,DE LD (HL)," " ;заменяем удаляемый символ пробелом CALL PR_STR JP KEYS ; Ввод пробела SPACE LD A,E ;проверка достижения конца строки ввода CP 20 JP NC,KEYS LD D,0 LD HL,NAME ADD HL,DE LD (HL)," " CALL PR_STR INC E ;увеличиваем позицию ввода JP KEYS ; Вывод курсора изменением байта атрибутов RESCUR LD A,68 ;PAPER 0, INK 4, BRIGHT 1 JR PRATTR ; Удаление курсора восстановлением байта атрибутов SETCUR LD A,79 ;PAPER 1, INK 7, BRIGHT 1 ; Вычисляем адрес атрибутов знакоместа и заносим ; по этому адресу байт из аккумулятора PRATTR LD L,B LD H,0 ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL PUSH AF LD A,H ADD A,#58 LD H,A LD A,L ADD A,C LD L,A POP AF LD (HL),A RET ; Подпрограмма печати таблицы символов TABL LD DE,STR LD BC,LENSTR JP 8252 ; Подпрограмма печати введенной строки PR_STR PUSH BC PUSH DE LD DE,STR5 LD BC,LENLIN CALL 8252 POP DE POP BC RET ; Подпрограмма рисования рамки LINES EXX PUSH HL LD A,66 LD (23695),A LD BC,#8A2C ;B = 138, C = 44 CALL 8933 LD DE,#101 LD BC,160 ;B = 0, C = 160 CALL 9402 LD DE,#FF01 LD BC,#3D00 ;B = 61, C = 0 CALL 9402 LD DE,#1FF LD BC,160 CALL 9402 LD DE,#101 LD BC,#3D00 CALL 9402 POP HL EXX RET ; Короткий звуковой сигнал SND LD B,30 LD HL,350 LD DE,2 SND1 PUSH BC PUSH DE PUSH HL CALL 949 POP HL POP DE POP BC SBC HL,DE DJNZ SND1 RET ; Данные таблицы символов STR DEFB 22,5,6 STR1 DEFM "1 2 3 4 5 6 7 8 9 0" ;символы через один пробел DEFB 22,7,6 STR2 DEFM "A B C D E F G H I J" DEFB 22,9,6 STR3 DEFM "K L M N O P Q R S T" DEFB 22,11,6 STR4 DEFM "U V W X Y Z . d s e" STR5 DEFB 22,19,5,16,5,">",16,2 NAME DEFM "····················" DEFB 16,5,"<" LENSTR EQU $-STR ;длина строки для печати таблицы LENLIN EQU $-STR5 ;длина строки ввода имени ; Адреса данных символов в таблице D_STR DEFW STR1,STR2,STR3,STR4Эту программу можно рассматривать как вполне независимый кадр заставки. Если вы решите использовать ее в своей собственной игре, единственное, что вам потребуется, это перенести после выхода введенное имя из строки NAME в таблицу «рекордов». И, конечно же, вам нужно будет проследить, чтобы имена меток, задействованные в приведенной программе не повторялись в вашей игре. Естественно, что при необходимости наименования меток можно и заменить.
УПРАВЛЕНИЕ ДЖОЙСТИКОМВ динамичных играх управление спрайтами с помощью Kempston-джойстика часто оказывается более удобным, чем от клавиатуры - нужно помнить всего лишь о четырех сторонах света, да в пылу борьбы не забыть, что есть еще кнопка «огонь». Наверное, вам известно, что в Бейсике определить положение ручки джойстика можно с помощью функции IN 31. В ассемблере для этих же целей удобнее всего применять команду IN A,(31), после выполнения которой в аккумуляторе появится некоторое число, отдельные биты которого и определяют «статус» джойстика. Значения имеют не все биты, а только 5 младших, причем, в отличие от клавиатуры, в нейтральном положении все биты сброшены в 0 (конечно, если порт джойстика вообще подключен), а установка какого-то бита в 1 означает поворот ручки или нажатие кнопки «огонь». При диагональном наклоне ручки будут установлены сразу два бита. В табл. 8.2 показано соответствие пяти младших битов, получаемых в аккумуляторе после выполнения команды IN A,(31), направлениям наклона ручки джойстика и нажатию кнопки «огонь». Три старших бита не определены, поэтому в таблице они заменены знаками вопроса.
Давайте сначала на примере простой программки перемещения точки, аналогичной той, которую мы описали в начале первого раздела данной главы, рассмотрим принцип использования джойстика. Причем, как и в случае с клавиатурой, точку на экран будем ставить с помощью бейсик-программы. ORG 60000 XY EQU 23296 JOY LD HL,(XY) ;в регистре H вертикальная координата, ; а в L - горизонтальная IN A,(31) ;читаем из порта джойстика RRCA ;проверяем бит 0 JR NC,JOY1 ;если в 0, переходим к проверке ; следующего бита INC L ;увеличиваем горизонтальную координату JOY1 RRCA ;аналогично проверяем остальные биты JR NC,JOY2 DEC L ;уменьшаем горизонтальную координату JOY2 RRCA JR NC,JOY3 DEC H ;увеличиваем вертикальную координату JOY3 RRCA JR NC,JOY4 INC H ;уменьшаем вертикальную координату JOY4 LD (XY),HL ;новые координаты передаем ; бейсик-программе RRCA RET NC JP 3435 ;при нажатии кнопки «огонь» ; экран очищаетсяЧтобы точка, перемещаясь по экрану, не ушла за его пределы, необходимо ввести ограничения. Как это осуществить, покажем на конкретном примере, в котором мы используем приведенный выше принцип для воспроизведения реальной игровой ситуации, например, для управления самолетом. Прежде всего создадим три спрайта (блоки SAM1, SAM2 и SAM3), первый из которых будет соответствовать полету самолета прямо, второй повороту вправо (самолет при этом должен слегка наклониться) и, наконец, третий - его повороту влево. Таким образом, наклоняя ручку джойстика в ту или иную сторону, вы можете в приведенной ниже программе легко изменять положение спрайта на экране. ORG 60000 ENT $ LD A,5 LD (23693),A XOR A CALL 8859 CALL 3435 ; Основная часть программы LD BC,#505 ;в регистре B вертикальная координата Y, ; а в C горизонтальная JOY IN A,(31) ;читаем данные из порта джойстика LD E,A ;освобождаем аккумулятор ; для проверки границ LD HL,SAM1 ;задаем адрес первого спрайта RRC E ;сдвигаем E вправо на один бит JR NC,JOY1 LD A,C CP 30 ;задаем границу перемещения вправо JR NC,JOY1 ;если правая граница достигнута, ; то увеличивать X уже нельзя - ; переходим на метку JOY1 INC C ;увеличиваем координату X, что соответствует ; перемещению самолета вправо LD HL,SAM2 ;задаем адрес второго спрайта JOY1 RRC E JR NC,JOY2 LD A,C CP 1 JP M,JOY2 DEC C ;уменьшаем координату X LD HL,SAM3 ;задаем адрес третьего спрайта JOY2 RRC E JR NC,JOY3 LD A,B CP 22 JR NC,JOY3 INC B ;увеличиваем координату Y JOY3 RRC E JR NC,JOY4 LD A,B CP 1 JP M,JOY4 DEC B ;уменьшаем координату Y JOY4 RRC E RET C ;если кнопка «огонь» нажата - ; выходим из программы ; Вывод на экран по принципу XOR одного из трех спрайтов самолета PUSH BC PUSH HL LD A,SPRXOR ;устанавливаем режим вывода XOR CALL PTBL ;печатаем один из самолетов LD BC,10 ;вводим задержку CALL 7997 POP HL POP BC PUSH BC LD A,SPRXOR ;устанавливаем режим вывода XOR CALL PTBL ;стираем изображение самолета POP BC JR JOY ;переходим в начало программы ; для изменения координат PTBL ......... ; Заголовок данных спрайта первого самолета, соответствующего полету вперед и назад SAM1 DEFB 4 DEFB 0,0,5,0,1,5,1,0,5,1,1,5 ; Данные для первого самолета DEFB 0,0,5,0,95,254,56,66 DEFB 0,0,160,0,250,127,28,66 DEFB 61,1,1,5,13,0,0,0 DEFB 188,128,128,160,176,0,0,0 ; Заголовок данных спрайта второго самолета, соответствующего повороту вправо SAM2 DEFB 4 DEFB 0,0,5,0,1,5,1,0,5,1,1,5 ; Данные для второго самолета DEFB 0,0,5,0,11,95,78,35 DEFB 0,0,160,0,244,62,28,8 DEFB 29,1,1,2,2,0,0,0 DEFB 176,128,128,192,224,192,0,0 ; Заголовок данных спрайта третьего самолета, соответствующего повороту влево SAM3 DEFB 4 DEFB 0,0,5,0,1,5,1,0,5,1,1,5 ; Данные для третьего самолета DEFB 0,0,5,0,47,124,56,16 DEFB 0,0,160,0,208,250,114,196 DEFB 13,1,1,3,7,3,0,0 DEFB 184,128,128,64,64,0,0,0
СОВМЕСТНОЕ УПРАВЛЕНИЕ КЛАВИАТУРОЙ И ДЖОЙСТИКОМПрочитав название параграфа, многие наверняка подумали - а в чем тут собственно проблема, достаточно объединить вместе блоки управления клавиатурой и джойстиком и вроде бы все, можно пользоваться как тем так и другим. Не будем вас разочаровывать, так оно и есть, и в принципе подобную схему вполне можно использовать для совместного управления. Но при этом нужно помнить о том, что если потребуется изменить управляющие клавиши, вам придется вносить в текст программы довольно много изменений. То же самое можно сказать и о смене типа джойстика. Поэтому хотелось бы иметь универсальную процедуру опроса клавиатуры и джойстика, в которой перечисленные изменения можно было выполнить с наименьшей затратой сил. Ниже приводится программа, использующая подобную процедуру, обозначенную меткой KBDJOY. В ней на экран выводится вертолет с вращающимся винтом (рис. 8.2 а,б) и стрекочущим двигателем. Нажимая клавиши Q, A, O, P или наклоняя ручку джойстика, вы сможете легко убедиться в том, что программа работает как положено. А для усиления эффекта можно попробовать нажать какую-нибудь из клавиш и одновременно повернуть джойстик - вертолет послушно полетит по диагонали.
ORG 60000 ENT $ LD A,5 LD (23693),A XOR A CALL 8859 CALL 3435 ; Основная часть программы LD BC,#505 ;задаем исходное положение вертолета KEY PUSH BC CALL KBDJOY ;читаем данные из портов POP BC RRCA ;поворачиваем ручку джойстика вправо ; или нажимаем клавишу P - ; полет вертолета вправо JR NC,KEY1 INC C KEY1 RRCA ;поворачиваем ручку джойстика влево ; или нажимаем клавишу O - ; полет вертолета влево JR NC,KEY2 DEC C KEY2 RRCA ;поворачиваем ручку джойстика вниз ; или нажимаем клавишу A - ; полет вертолета вниз JR NC,KEY3 INC B KEY3 RRCA ;поворачиваем ручку джойстика вверх ; или нажимаем клавишу Q - ; полет вертолета вверх JR NC,KEY4 DEC B KEY4 RRCA ;при нажатии кнопки «огонь» джойстика ; или клавиши M - выход RET C ; Подпрограмма вывода на экран изображения вертолета в двух фазах, ; каждая из которых соответствует одному из положений винта XOR A ;формируем звуковой сигнал, OUT (254),A ; имитирующий работу двигателя CALL CHECK ;проверка достижения границ экрана LD A,16 OUT (254),A LD A,SPRXOR ;задаем режим вывода спрайта LD HL,WERT1 ;устанавливаем адрес спрайта PUSH BC PUSH HL CALL PTBL ;выводим вертолет в первой фазе LD BC,5 ;задаем задержку между фазами CALL 7997 ; вращения винта POP HL POP BC LD A,SPRXOR ;режим вывода спрайта PUSH BC PUSH HL CALL PTBL ;стираем вертолет в первой фазе POP HL POP BC XOR A OUT (254),A ; Вывод вертолета во второй фазе LD A,16 ;звуковой сигнал OUT (254),A LD A,SPRXOR ;режим вывода спрайта LD HL,WERT2 ;устанавливаем адрес спрайта ; с другим расположением винта PUSH BC PUSH HL CALL PTBL ;выводим спрайт во второй фазе LD BC,5 CALL 7997 POP HL POP BC LD A,SPRXOR ;режим вывода спрайта PUSH BC PUSH HL CALL PTBL ;стираем с экрана спрайт во второй фазе POP HL POP BC JR KEY ; Подпрограмма проверки границ экрана CHECK LD A,C AND A ;сравниваем координату X вертолета ; с заданной левой границей экрана JR NZ,CONT1 INC C CONT1 CP 29 ;сравниваем координату X вертолета ; с заданной правой границей экрана JR NZ,CONT2 DEC C CONT2 LD A,B ;задаем верхнюю границу экрана AND A ;сравниваем координату Y вертолета ; с заданной верхней границей экрана JR NZ,CONT3 INC B CONT3 CP 21 ;сравниваем координату Y вертолета ; с заданной нижней границей экрана RET NZ DEC B RET ; Подпрограмма чтения данных из портов клавиатуры и джойстика KBDJOY IN A,(31) ;опрашиваем порт джойстика LD E,A ;запоминаем полученные биты ; Проверяем, подключен ли порт джойстика (ручку невозможно ; повернуть сразу и вправо и влево - если оба бита установлены, ; порт не подключен) AND 3 CP 3 JR NZ,KBDJ1 ;если да, переходим к опросу клавиатуры LD E,0 ; иначе очищаем коллектор битов KBDJ1 LD HL,DKEY ;адрес блока данных клавиатуры KBDJ2 LD C,(HL) ;младший байт адреса порта INC C ;проверка на 0 (конец блока данных) DEC C LD A,E ;значение коллектора в аккумулятор RET Z ;выход, если конец данных INC HL LD B,(HL) ;старший байт адреса порта INC HL IN A,(C) ;читаем из порта CPL ;инвертируем биты AND (HL) ;проверяем конкретный бит INC HL JR Z,KBDJ3 LD A,(HL) ;если бит установлен, читаем код направления OR E ; и объединяем с коллектором LD E,A KBDJ3 INC HL JR KBDJ2 ;продолжаем чтение ; Данные управляющих клавиш: ; первое число - младший байт порта ; второе число - старший байт порта ; третье число - маска бита ; четвертое - код направления (аналогично кодам джойстика) DKEY DEFB #FE,#FB,1,8 ;Q - вверх DEFB #FE,#FD,1,4 ;A - вниз DEFB #FE,#DF,2,2 ;O - влево DEFB #FE,#DF,1,1 ;P - вправо DEFB #FE,#7F,4,16 ;M - «огонь» DEFB 0 ;метка конца блока данных PTBL ......... ; Заголовок первой фазы спрайта «вертолет» WERT1 DEFB 7 DEFB 0,1,6,1,0,6,1,1,6,1,2,6 DEFB 2,0,6,2,1,6,2,2,6 ; Данные первой фазы спрайта «вертолет» DEFB 0,0,4,28,56,32,24,24 DEFB 0,0,0,1,2,2,4,4 DEFB 60,60,255,129,66,36,36,24 DEFB 0,0,0,128,64,64,32,32 DEFB 7,2,1,0,1,2,4,14 DEFB 255,36,36,255,0,0,0,0 DEFB 224,64,128,0,128,64,32,112 ; Заголовок второй фазы спрайта «вертолет» WERT2 DEFB 9 DEFB 0,0,6,0,1,6,0,2,6 DEFB 1,0,6,1,1,6,1,2,6 DEFB 2,0,6,2,1,6,2,2,6 ; Данные второй фазы спрайта «вертолет» DEFB 0,0,30,127,255,127,14,0 DEFB 0,0,0,129,231,129,24,24 DEFB 0,0,120,254,255,254,112,0 DEFB 0,0,0,1,2,2,4,4 DEFB 60,60,255,129,66,36,36,24 DEFB 0,0,0,128,64,64,32,32 DEFB 7,2,1,0,1,2,4,14 DEFB 255,36,36,255,0,0,0,0 DEFB 224,64,128,0,128,64,32,112 СОДЕРЖАНИЕ:
|