Когда в первой главе мы рассматривали основные части игровой программы, то в общих чертах показали, как строится многокадровая заставка и какие функции должны выполнять отдельные ее элементы. Теперь настало время всерьез поговорить о том, как реализовать эти элементы в виде законченных подпрограмм, а затем собрать их в единое целое для получения готовой заставки. Конечно, трудно дать ответы на все вопросы, с которыми вы можете столкнуться при решении данной задачи, поэтому мы решили ограничиться только теми проблемами, которые ранее не рассматривались совсем. Среди них - создание простейших звуковых эффектов, преобразование символов стандартного набора таким образом, чтобы надписи не стыдно было поместить на самое видное место экрана, скроллинги окон с графическими изображениями и текстами во всех направлениях, а также включение в программу элемента случайности. После того, как все это будет подробно рассмотрено, станет возможно, наконец, непосредственно заняться многокадровой заставкой.
LD DE,40 LD HL,500 CALL 949 RETНесмотря на некоторую трудность подбора параметров, все же попытаемся создать с помощью приведенной подпрограммы что-либо интересное, причем так, чтобы время звучания и высоту тона можно было свободно регулировать. Поскольку при этом возможны самые разные варианты, то получится множество звуковых эффектов, что, собственно говоря, нам и требуется.
Рассмотрим программу, основанную на приведенном выше примере, генерирующую звуки длительностью порядка 1-3 секунд. Она отличается тем, что высота тона изменяется в цикле, а продолжительность звучания можно регулировать не только изменением DE, но и занося различные значения в регистр B. Мы предполагаем использовать ее в дальнейшем, для чего присвоим ей собственное имя:
SND LD B,10 ;количество циклов LD HL,300 ;начальная частота звучания LD DE,8 ;длительность звука SND1 PUSH BC PUSH DE PUSH HL CALL 949 POP HL POP DE POP BC DEC HL ;или INC HL DJNZ SND1 RETПосле запуска вы услышите звук, увеличивающийся по высоте, но если где-то в игре потребуется, чтобы с течением времени частота уменьшалась, достаточно команду DEC HL заменить на INC HL. Для извлечения более коротких звуков (0.1-0.3 сек.) достаточно задать в SND другие значения регистров:
SND LD B,90 LD DE,2 LD HL,150 SND1 .........Располагая этими программками, попробуйте поэкспериментировать, встроив, например, два резонатора в один цикл, либо, организовав несколько (в том числе и вложенных) циклов с одинаковыми или разными резонаторами. Уверены, что это занятие приведет вас ко многим «открытиям» и доставит немало приятных минут.
а | б |
Приведем теперь текст программы BIGSYM, которая удваивает высоту одного символа, причем его код должен быть предварительно помещен в ячейку памяти по адресу 23296.
ORG 60000 ENT $ LD A,(23296) ;загружаем код печатаемого символа BIGSYM LD L,A LD H,0 ;переписываем этот код в HL ADD HL,HL ;умножаем код на 8 ADD HL,HL ADD HL,HL LD DE,(23606) ;в DE загружаем адрес начала ; текущего фонта ADD HL,DE EX DE,HL LD HL,(23684) ;в HL помещаем адрес в видеобуфере, ; по которому будет выводиться первый ; байт измененного символа LD B,4 PUSH HL ;делаем две копии HL, чтобы PUSH HL ; использовать их во втором цикле BIGS1 LD A,(DE) ;считываем байт из фонта LD (HL),A ;переписываем в видеобуфер INC H LD (HL),A ;еще раз - ниже INC H INC DE ;переходим к следующему байту DJNZ BIGS1 POP HL ;восстанавливаем HL LD BC,32 ;вычисляем адрес первого байта ADD HL,BC ; второго знакоместа LD B,4 BIGS2 LD A,(DE) ;аналогично циклу BIGS1 LD (HL),A INC H LD (HL),A INC H INC DE DJNZ BIGS2 POP HL ;восстанавливаем HL INC HL ;увеличиваем HL на 1 для подготовки ; печати следующего символа LD (23684),HL ;записываем в системную переменную ; адрес следующего знакоместа экрана RETОбращаем ваше внимание на то, что процедура BIGSYM получилась не совсем универсальной. Сделано это с единственной целью упростить и сократить ее исходный текст. Применяя ее, нужно следить, чтобы символы при печати не выходили за пределы экрана ни по вертикали, ни по горизонтали. Кроме того, верхняя и нижняя половинки выводимых знаков не должны попадать в разные трети экрана, то есть не допускается позиционирование курсора на 7-ю и 15-ю строки экрана.
Чтобы посмотреть, как выглядит на экране целая строка удлиненных символов, введите небольшую программку на Бейсике. Само собой разумеется, что перед ее запуском ассемблерная программа должна быть оттранслирована.
10 PRINT AT 5,5; 20 LET a$="Starting program BIGSYM!" 30 FOR n=1 TO LEN a$ 40 POKE 23296,CODE a$(n) 50 RANDOMIZE USR 60000 60 NEXT nНасладившись созерцанием новых букв, возможно, вы захотите воспользоваться процедурой BIGSYM в собственной программе. В последнем разделе этой главы, где приводится программа и описание многокадровой заставки, можно посмотреть, как это лучше сделать. Пока же можем сказать следующее: перед вызовом процедуры из ассемблера необходимо установить позицию печати в нужное место экрана, воспользовавшись командой RST 16, а в аккумулятор занести код выводимого символа. В этом случае надобность в команде LD A,(23296), предваряющей в приведенном примере процедуру BIGSYM, отпадает.
а | б |
Поскольку работа этой процедуры не столь очевидна, как может показаться с первого взгляда, то для лучшего ее понимания мы приводим небольшую схемку (рис. 6.3), отражающую пути перемещений битов при многократном выполнении команд сдвигов. Напоминаем, что эти команды вместе с аналогичными схемами приведены в разделе «Организация циклов в ассемблере» пятой главы.
Рис. 6.3. Перемещения битов
ORG 60000 ENT $ LD A,(23296) ;начало - как в процедуре BIGSYM DBLSYM LD L,A LD H,0 ADD HL,HL ADD HL,HL ADD HL,HL LD DE,(23606) ADD HL,DE EX DE,HL LD HL,(23684) LD B,8 ;количество повторений внешнего цикла PUSH HL ; Во внешнем цикле по очереди берутся 8 байтов исходного символа ; для преобразования во внутреннем цикле DBLS1 PUSH BC ;сохраняем BC для внешнего цикла PUSH DE ;сохраняем текущий адрес ; в символьном наборе LD A,(DE) LD DE,0 ;подготавливаем пару DE для последующего ; формирования в ней расширенного ; байта символа LD B,8 ;количество повторений внутреннего цикла ; (равное числу бит в одном байте) ; Во внутреннем цикле каждый бит исходного символа дважды копируется ; в регистровую пару DE, где в конце концов получаем расширенный ; в два раза байт (рис. 6.3) DBLS2 RLCA PUSH AF RL E RL D POP AF RL E RL D DJNZ DBLS2 LD (HL),D ;вывод преобразованных байтов из INC HL ; пары DE на экран LD (HL),E DEC HL POP DE ;восстанавливаем значение DE INC H INC DE ;переходим к обработке следующего ; байта из набора символов POP BC ;восстанавливаем BC для внешнего цикла DJNZ DBLS1 POP HL INC HL ;переходим к следующему INC HL ; знакоместу экрана LD (23684),HL ;записываем в системную переменную ; адрес следующего символа RETЧтобы напечатать на экране строку с каким-то текстом, можно воспользоваться той же программкой на Бейсике, что и для демонстрации процедуры BIGSYM.
Теперь в вашем распоряжении имеются три программы, в той или иной мере изменяющие форму стандартных символов и, чтобы лучше понять их работу, можно порекомендовать слегка «поиздеваться» над символами, создавая комбинированные программы. Например, часть символа увеличить по высоте, а оставшуюся часть - по ширине, или BIGSYM и DBLSYM соединить с программой BOLD. Надо сказать, что последний вариант дает совсем недурные результаты, в чем вы сможете убедиться, дойдя до последнего раздела этой главы.
Чтобы не утомлять вас долгими рассуждениями о том, каким образом формируется та или иная буква, лучше всего приведем в качестве своеобразного комментария программу на Бейсике с краткими пояснениями к ней, которые предлагаем внимательно изучить, после чего будет совсем нетрудно понять принцип работы соответствующей ей ассемблерной программы.
10 READ y: IF y=-1 THEN GO TO 100 20 READ x,hgt,len,ink,dx 30 LET y=113-y: LET x=x+40 40 INK ink 50 FOR i=1 TO hgt 60 PLOT x,y: DRAW len,0 70 PLOT x,y+1: DRAW len,0 80 LET y=y-4: LET x=x+dx: NEXT i 90 GO TO 10 100 STOP 1000 REM --- Буква «Н» --- 1010 DATA 0,0,12,6,2,0 1020 DATA 20,6,2,14,2,0 1030 DATA 0,20,12,10,2,0 1040 REM --- Буква «А» --- 1050 DATA 0,46,12,4,1,-1 1060 DATA 0,46,12,10,1,1 1070 DATA 24,42,2,16,1,0 1080 REM --- Буква «Р» --- 1090 DATA 0,72,12,10,3,0 1100 DATA 0,82,2,13,3,0 1110 DATA 24,82,2,13,3,0 1120 DATA 4,96,1,2,3,0 1130 DATA 24,96,1,2,3,0 1140 DATA 8,92,4,8,3,0 1150 REM --- Буква «Д» --- 1160 DATA 0,112,10,4,6,-1 1170 DATA 0,112,10,10,6,1 1180 DATA 40,100,2,34,6,0 1190 REM --- Буква «Ы» --- 1200 DATA 0,139,12,10,4,0 1210 DATA 16,150,2,13,4,0 1220 DATA 40,150,2,13,4,0 1230 DATA 20,163,1,2,4,0 1240 DATA 40,163,1,2,4,0 1250 DATA 24,160,4,8,4,0 1260 DATA 0,167,4,8,4,0 1270 DATA 16,169,1,6,4,0 1280 DATA 20,171,6,4,4,0 1290 DATA 44,169,1,6,4,0 1300 DATA -1Теперь скажем несколько слов об этой программе, поскольку все основные идеи, заложенные в ней, затем будут реализованы в программе на ассемблере, но в начале покажем, что вы увидите на экране после ее исполнения (рис. 6.4).
Рис. 6.4. Название для игры НАРДЫ
Вот такая симпатичная надпись, в которой каждая буква составлена из отрезков параллельных двойных линий, нарисованных DRAW'ами. В высоту укладывается двенадцать таких линий плюс промежутки между ними. Точно такие любой из вас сможет легко начертить по линейке на листе бумаги, измерить их длины и координаты, после чего ввести в программу свои вычисления в виде блока данных. Посмотрим, как это делается. Прежде всего разберемся со структурой строки блока данных, поскольку с подобным построением раньше мы еще не встречались, а именно оно все и определяет. Строка списка DATA содержит шесть элементов, каждому из которых в программе соответствует определенная переменная, а именно:
а | б |
в | г |
После того как мы выяснили способ формирования каждой буквы средствами Бейсика, перейдем непосредственно к ассемблерной программе строка за строкой. Но прежде чем приступить к описанию текста, следует заметить, что к этому моменту мы наговорили вам довольно много всякого, поэтому небольшие по размерам программы нет смысла разбивать на отдельные части и подробно распространяться о каждой из них, как мы это делали в четвертой и пятой главах. Думается, что сейчас будет уже вполне достаточно прокомментировать отдельные команды и только в случаях, представляющих особый интерес, рассмотреть более детально некоторые группы команд.
ORG 60000 LD HL,DATA ; Считывание параметров из блока данных CICL1 LD B,(HL) ;Y-координата начала линии INC B RET Z ;выход, если это конец блока данных LD A,112 ;отсчитывать будем не снизу, а сверху. ; Кроме того, сразу добавляем смещение ; от верхнего края в 64 пикселя и ; увеличиваем на 1, поскольку выполнялась ; команда INC B (175-64+1=112) SUB B LD B,A INC HL LD A,(HL) ;X-координата начала линии ADD A,40 ;центрируем надпись LD C,A INC HL LD D,(HL) ;высота рисуемой части буквы INC HL LD E,(HL) ; и ее ширина INC HL LD A,16 ;управляющий код для INK RST 16 LD A,(HL) ;код цвета RST 16 INC HL LD A,(HL) ;наклон рисуемой части INC HL PUSH HL ;сохраняем текущий адрес в блоке данных ; Цикл рисования части буквы, описанной очередной порцией данных CICL2 PUSH AF ;сохраняем в стеке аккумулятор CALL DRAW ;чертим первую линию DEC B ;вторая линия будет на 1 пиксель ниже CALL DRAW ;чертим вторую линию DEC B ;переходим ниже DEC B ;пропускаем еще два ряда пикселей DEC B POP AF ;восстанавливаем в аккумуляторе ; параметр наклона PUSH AF ADD A,C ;смещаем X-координату для LD C,A ; получения наклона POP AF DEC D ;повторяем заданное параметром JR NZ,CICL2 ; «высота» количество раз POP HL ;восстанавливаем адрес в блоке данных JR CICL1 ;переходим на начало ; Подпрограмма рисования одной горизонтальной линии DRAW PUSH BC ;сохраняем нужные регистры PUSH DE ; в машинном стеке CALL 8933 ;ставим точку с координатами ; из регистров B и C POP DE ;регистр E нам понадобится, ; поэтому восстанавливаем PUSH DE ; и снова сохраняем EXX PUSH HL ;сохраняем регистровую пару HL' EXX LD C,E ;длина линии (горизонтальное смещение) LD B,0 ;вертикальное смещение - 0 (линия ; идет горизонтально) LD DE,#101 ;положительное значение ; для обоих смещений CALL 9402 ;рисуем линию EXX POP HL ;восстанавливаем регистры из стека EXX POP DE POP BC RET ; Блоки данных для рисования букв ; буква «Н» DATA DEFB 0,0,12,6,2,0 DEFB 20,6,2,14,2,0 DEFB 0,20,12,10,2,0 ; буква «А» DEFB 0,46,12,4,1,-1 DEFB 0,46,12,10,1,1 DEFB 24,42,2,16,1,0 ; буква «Р» DEFB 0,72,12,10,3,0 DEFB 0,82,2,13,3,0 DEFB 24,82,2,13,3,0 DEFB 4,96,1,2,3,0 DEFB 24,96,1,2,3,0 DEFB 8,92,4,8,3,0 ; буква «Д» DEFB 0,112,10,4,6,-1 DEFB 0,112,10,10,6,1 DEFB 40,100,2,34,6,0 ; буква «Ы» DEFB 0,139,12,10,4,0 DEFB 16,150,2,13,4,0 DEFB 40,150,2,13,4,0 DEFB 20,163,1,2,4,0 DEFB 40,163,1,2,4,0 DEFB 24,160,4,8,4,0 DEFB 0,167,4,8,4,0 DEFB 16,169,1,6,4,0 DEFB 20,171,6,4,4,0 DEFB 44,169,1,6,4,0 DEFB -1 ;указатель конца блока данныхНе решите нечаянно, что таким методом допускается рисовать исключительно буквы. В заставке и, безусловно, в других частях программы ему найдется немало областей применения, например, для создания всевозможных орнаментов, статических рисунков, а если ее слегка дополнить, то возможно даже создавать элементы игрового пространства, скажем, лабиринты.
LD B,50 ;количество сдвигов LOOP PUSH BC ;сохраняем содержимое регистра B .......... ;процедура скроллинга POP BC ;восстанавливаем регистр B DJNZ LOOP RETДо того, как мы приступим к детальному описанию процедуры скроллинга окна вверх, желательно рассмотреть все команды и подпрограммы ПЗУ, которые встречаются здесь впервые. Таких наберется всего две: чрезвычайно полезная команда LDIR, перемещающая блок памяти с инкрементом (т. е. с увеличением содержимого регистров, в которых записаны адреса пересылок) и подпрограмма ПЗУ, расположенная по адресу 8880. Рассмотрим их в том порядке, как они встречаются в программе.
Процедура 8880 вычисляет адрес байта в видеобуфере по координатам точки, заданным в пикселях. Началом отсчета считается левый верхний угол экрана. Таким образом, входными данными к подпрограмме являются:
MET LD A,(HL) LD (DE),A INC HL INC DE DEC BC LD A,B OR C JR NZ,MET RETГлядя на этот фрагмент, можно заметить очевидные достоинства команды LDIR: в цикле изменяется регистр A, а в LDIR он не затрагивается, кроме того, программа занимает 8 строк текста вместо одной и работает примерно в два с половиной раза медленнее.
Если в программу ввести исходные данные, то может получиться эффект, который мы наблюдаем в некоторых играх, например, в SOKOBAN'е. Суть его состоит в том, что из ПЗУ с адреса 0 переписываются 6144 байта в область экранной памяти, начиная с адреса 16384. Если это действие повторять в цикле, то создастся впечатление бегущей по экрану ряби:
LD BC,6144 LD HL,0 LD DE,16384 LD A,255 MET PUSH BC PUSH DE PUSH HL LDIR POP HL POP DE POP BC INC HL DEC A JR NZ,MET RETКроме рассмотренной команды LDIR в ассемблерных программах довольно широко используются и другие команды пересылок байтов:
LDI - пересылка содержимого одной ячейки памяти с инкрементом. Байт из ячейки, адресуемой регистровой парой HL, переносится в ячейку, адресуемую парой DE; содержимое HL и DE увеличивается на 1, а BC уменьшается на 1. Если в результате выполнения команды BC=0, то флаг P/V сбрасывается, в противном случае P/V=1.
LDD - пересылка содержимого одной ячейки памяти с декрементом. Действие аналогично команде LDI, только содержимое регистровых пар HL и DE уменьшается на 1.
SCR_UP LD A,(COL) LD C,A LD A,(HGT) LD B,A LD A,(ROW) ; Значения из переменных ROW, COL и HGT умножаем на 8, ; то есть переводим знакоместа в пиксели SLA A SLA A SLA A SLA B SLA B SLA B DEC B ;потому что один ряд пикселей просто ; заполняется нулями SLA C SLA C SLA C PUSH AF PUSH BC CALL 8880 ;вычисляем адрес верхнего левого угла окна POP BC POP AF SCRUP1 INC A ;следующий ряд пикселей PUSH AF PUSH BC PUSH HL CALL 8880 ;вычисляем адрес POP DE PUSH HL LD A,(LEN) ;пересылаем столько байт, сколько ; умещается по ширине окна LD C,A LD B,0 LDIR POP HL POP BC POP AF DJNZ SCRUP1 LD (HL),0 ;в последний ряд пикселей записываем нули LD D,H LD E,L INC DE LD A,(LEN) ;по ширине окна, DEC A ; минус 1 RET Z ;выходим, если только одно знакоместо LD C,A LD B,0 LDIR ;иначе обнуляем и все остальные байты ряда RETОбратите особое внимание на последние строки процедуры, где в нижний ряд пикселей записываются нулевые байты. Этот прием весьма распространен и применяется для заполнения любой области памяти произвольным значением. Чтобы понять идею, нужно хорошо представлять, как работает команда LDIR. Сначала в первый байт массива, адресуемый парой HL, заносится какой-то определенный байт, в DE переписывается значение из HL и увеличивается на 1, в BC задается уменьшенный на единицу размер заполняемого массива, а дальше с выполнением команды LDIR происходит следующее. Байт из первого адреса (HL) переписывается во второй (DE), затем DE и HL увеличиваются, то есть HL будет указывать на второй байт, а DE - на третий. На следующем круге число из второго адреса пересылается в третий, но после первого выполнения цикла второй байт уже содержит ту же величину, что и первый, поэтому второй и третий байты к этому моменту станут равны первому. И так далее, до заполнения всего массива.
Практическое применение такому методу найти нетрудно. Кроме процедур вертикального скроллинга окон он может использоваться, например, для очистки экрана, инициализации блоков данных и других нужд. Попробуйте сами написать процедуру, аналогичную оператору CLS, в которой участвовала бы команда LDIR.
Вернемся к рассмотрению подпрограмм скроллингов окон. Процедура сдвига окна вниз во многом похожа на предыдущую:
SCR_DN LD A,(COL) LD C,A LD A,(HGT) LD B,A LD A,(ROW) ADD A,B ;начинаем перемещать изображение не ; сверху, как в SCR_UP, а снизу SLA A SLA A SLA A DEC A SLA B SLA B SLA B DEC B SLA C SLA C SLA C PUSH AF PUSH BC CALL 8880 POP BC POP AF SCRDN1 DEC A ;следующий ряд пикселей (идем вверх) PUSH AF PUSH BC PUSH HL CALL 8880 POP DE PUSH HL LD A,(LEN) LD C,A LD B,0 LDIR POP HL POP BC POP AF DJNZ SCRDN1 LD (HL),0 LD D,H LD E,L INC DE LD A,(LEN) DEC A RET Z LD C,A LD B,0 LDIR RETТеперь приведем подпрограмму, выполняющую скроллинг окна влево. Она уже совсем не похожа на предшествующие, но в принципе повторяет известную вам процедуру SCRLIN, описанную в разделе «Бегущая строка» предыдущей главы. Но это и понятно: ведь там мы также скроллировали окно, только оно имело фиксированные размеры в одно знакоместо высотой и 32 - шириной. Здесь же мы приводим универсальную процедуру, но и с ее помощью можно получить тот же эффект.
SCR_LF LD A,(HGT) ;количество повторений такое же, LD B,A ; сколько строк занимает окно LD A,(ROW) ;номер верхней строки SCRLF1 PUSH AF ;дальше все очень похоже на процедуру SCRLIN PUSH BC CALL 3742 LD A,(COL) LD B,A LD A,(LEN) DEC A ADD A,B ADD A,L LD L,A LD B,8 SCRLF2 PUSH HL LD A,(LEN) AND A SCRLF3 RL (HL) DEC HL DEC A JR NZ,SCRLF3 POP HL INC H DJNZ SCRLF2 POP BC POP AF INC A DJNZ SCRLF1 RETИ наконец, для полного комплекта, напишем соответствующую подпрограмму, выполняющую скроллинг окна вправо:
SCR_RT LD A,(HGT) LD B,A LD A,(ROW) SCRRT1 PUSH AF PUSH BC CALL 3742 LD A,(COL) ADD A,L LD L,A LD B,8 SCRRT2 PUSH HL LD A,(LEN) AND A SCRRT3 RR (HL) INC HL DEC A JR NZ,SCRRT3 POP HL INC H DJNZ SCRRT2 POP BC POP AF INC A DJNZ SCRRT1 RETА сейчас рассмотрим программу, в которой демонстрируются возможности вертикального скроллинга вверх. После запуска вы увидите две быстро сменяющие друг друга картинки: сначала весь экран заполняется красивым орнаментом (фактурой), а затем средняя его часть стирается процедурой очистки окна. После этого строка за строкой снизу вверх начнет медленно перемещаться текст с правилами игры (рис. 6.6), который обычно вызывается из меню как один из кадров заставки.
Рис. 6.6. Правила игры
Перед запуском ассемблерной программы следует с адреса 64768 загрузить набор русских букв, однако при желании вы можете использовать и аналогичные латинские, выполнив некоторые изменения в программе. О том, как это сделать, вполне достаточно сказано в четвертой главе.
ORG 60000 ENT $ ; Задание адресов новых фонтов и постоянных атрибутов экрана LATF EQU 64000-256 RUSF EQU LATF+768 LD A,6 LD (23693),A LD A,0 CALL 8859 CALL 3435 LD A,2 CALL 5633 ; Ввод символа UDG для рисования фактуры LD HL,UDG LD (23675),HL ; Заполнение всего экрана фактурой CALL DESK ; Начало основной программы LD HL,RUSF LD (23606),HL NEW LD HL,TEXT CYCLE1 CALL PRINT LD B,10 CYCLE2 PUSH HL PUSH BC CALL SCR_UP CALL PAUSE POP BC POP HL JR Z,EXIT DJNZ CYCLE2 LD A,(HL) AND A JR NZ,CYCLE1 LD B,80 ;расстояние между текстами CYCLE3 PUSH BC CALL SCR_UP CALL PAUSE POP BC JR Z,EXIT DJNZ CYCLE3 JR NEW ; Восстановление стандартного набора символов и выход EXIT LD HL,15360 LD (23606),HL RET ; Для того, чтобы строки выводимого текста можно было успеть прочитать, ; в цикл вставлена подпрограмма задержки, а нажав клавишу Space, ; можно в любой момент завершить работу программы. PAUSE LD BC,6 CALL 7997 LD A,(23560) CP " " RET ; Подпрограмма печати строки текста PRINT PUSH BC LD DE,PAR LD BC,5 CALL 8252 LD B,24 ;число символов в строке PR_CI LD A,(HL) RST 16 INC HL DJNZ PR_CI POP BC RET PAR DEFB 22,15,4,16,0 ; Подпрограмма заполнения экрана фактурой DESK LD BC,704 LD A,#14 RST 16 LD A,1 RST 16 DESK1 LD A,144 RST 16 DEC BC LD A,B OR C JR NZ,DESK1 LD A,#14 RST 16 LD A,0 RST 16 ; Создание в фактуре окна для вывода текста CALL CLSV CALL SETV RET ; Подпрограммы CLSV ......... SETV ......... SCR_UP ......... ; Данные для окна с текстом COL1 DEFB 4 ROW1 DEFB 4 LEN1 DEFB 24 HGT1 DEFB 12 ATTR DEFB %01000010 ; Данные для окна в фактуре COL DEFB 3 ROW DEFB 3 LEN DEFB 26 HGT DEFB 13 ; Данные для выводимого в окно текста TEXT DEFM "····P·I·R·A·M·I·D·A·····" DEFM "························" DEFM "Celx igry sostoit w tom," DEFM "~toby··perwym··postawitx" DEFM "swoi··fi{ki··na··wer{inu" DEFM "piramidy. Dlq |togo nuv-" DEFM "no ispolxzowatx gorizon-" DEFM "talxnye··i··wertikalxnye" DEFM "peredwiveniq fi{ek,krome" DEFM "togo,velatelxno wospolx-" DEFM "zowatxsq··dopolnitelxny-" DEFM "mi prodwiveniqmi,···sutx" DEFM "kotoryh··sostoit··w tom," DEFM "~toby sostawitx fi{ki··w" DEFM "wide treugolxnika, posle" DEFM "~ego sdelatx hod werhnej" ; При желании текст можно продолжить DEFB 0 ; Данные для фактуры вокруг окна UDG DEFB 248,116,34,71,143,7,34,113Два вида горизонтального скроллинга продемонстрируем на примере еще одной программы, где эти процедуры действуют одновременно. Сразу после запуска программа создает на экране фрагмент средневекового замка из красного кирпича с зубчатыми стенами и бойницами, а в средней его части - ажурные ворота. До тех пор, пока никакая клавиша не нажата, изображение неподвижно. После нажатия любой из них створки ворот начинают медленно расходиться в разные стороны (рис. 6.7), создавая совершенно бесподобный эффект.
Рис. 6.7. Раскрытие ворот замка скроллингом окон
А теперь перейдем непосредственно к программе.
ORG 60000 ENT $ ; Подготовка экрана LD A,7 LD (23693),A XOR A CALL 8859 CALL 3435 LD A,2 CALL 5633 ; Назначение нового адреса символов UDG LD HL,UDG LD (23675),HL ; Вывод на экран изображения крепостной стены с воротами CALL SCRN LD BC,0 CALL 7997 ; Основная часть программы, в которой створки ворот ; со стуком разъезжаются в разные стороны LD B,64 MAIN PUSH BC CALL SCR_LF LD A,0 OUT (254),A CALL SCR_RT LD A,16 OUT (254),A LD BC,6 CALL 7997 POP BC DJNZ MAIN RET ; Формирование изображения крепостной стены с воротами: ; Изображение стены SCRN LD DE,D_WALL LD BC,7 CALL 8252 LD BC,384 SCRN1 LD A,147 RST 16 DEC BC LD A,B OR C JR NZ,SCRN1 ; Зеленая трава LD DE,D_GRAS LD BC,5 CALL 8252 LD B,32 SCRN2 LD A," " RST 16 DJNZ SCRN2 ; Зубцы на стене LD BC,#400 ;AT 4,0 CALL PR_AT LD B,16 SCRN3 LD DE,D_BATT PUSH BC LD BC,10 CALL 8252 POP BC DJNZ SCRN3 ; Ворота LD BC,#908 ;AT 9,8 CALL PR_AT LD B,16 SCRN4 LD A," " RST 16 DJNZ SCRN4 ; Бойницы LD H,4 SCRN5 LD A,H ADD A,7 LD B,A LD C,4 CALL PR_AT LD A," " RST 16 LD C,27 CALL PR_AT LD A," " RST 16 DEC H JR NZ,SCRN5 ; Штыри решетки LD A,16 RST 16 LD A,5 RST 16 LD B,10 ;Y LD H,8 SCRN6 LD L,16 LD C,8 ;X SCRN7 CALL PR_AT LD A,145 RST 16 INC C DEC L JR NZ,SCRN7 INC B DEC H JR NZ,SCRN6 ; Пики решетки LD BC,#A08 ;AT 10,8 CALL PR_AT LD B,16 SCRN8 LD A,144 RST 16 DJNZ SCRN8 ; Узор решетки LD L,2 LD B,13 SCRN9 LD C,8 CALL PR_AT LD B,16 SCRN10 LD A,146 RST 16 DJNZ SCRN10 LD B,16 DEC L JR NZ,SCRN9 RET ; Подпрограмма позиционирования вывода спрайтов PR_AT LD A,22 RST 16 LD A,B RST 16 LD A,C RST 16 RET ; Подпрограммы скроллинга окон SCR_LF ........ SCR_RT ........ ; Данные для левого окна COL DEFB 8 ROW DEFB 10 HGT DEFB 8 LEN DEFB 8 ; Данные для правого окна COL1 DEFB 16 ROW1 DEFB 10 LEN1 DEFB 8 HGT1 DEFB 8 ; Данные для рисования крепостной стены и решетки UDG DEFB 34,34,34,119,34,34,34,34 ;пики (144) DEFB 34,34,34,34,34,34,34,34 ;штыри (145) DEFB 54,42,170,255,170,42,54,34 ;узор (146) DEFB 255,2,2,2,255,32,32,32 ;кирпич (147) ; Данные позиционирования печати D_BATT DEFB 17,2,16,7,147,147,17,0,32,32 D_WALL DEFB 22,6,0,17,2,16,7 D_GRAS DEFB 22,18,0,17,4
В качестве «случайных» чисел довольно часто используют последовательность кодов ПЗУ. Такой метод крайне прост и дает неплохую степень случайности в циклах. Если вы не забыли, именно такой метод мы применили в программе «растворения» символов, описанной в предыдущей главе. Сейчас же мы расскажем и о некоторых других способах получения псевдослучайных чисел.
Иногда «случайные» числа извлекают из системного регистра регенерации R. Поскольку его значение постоянно увеличивается после выполнения каждой команды микропроцессора, предугадать, что же он содержит в какой-то момент времени практически невозможно. Таким образом, простейший генератор случайных чисел может выглядеть так:
LD A,RНо помните, что это справедливо только для достаточно разветвленных программ, особенно если их работа зависит от внешних воздействий (например, при управлении с помощью клавиатуры или джойстика). В коротких же циклах ни о какой непредсказуемости говорить не приходится.
Кроме того, есть и еще один недостаток использования регистра регенерации. Значение его никогда не превышает 127. Иными словами, седьмой бит этого регистра обычно «сброшен» в 0, и, дойдя до значения 127, он вновь обнуляется.
Однако, справедливости ради, стоит заметить, что это относится лишь к тем программам, в которых регистр R не изменяется принудительным образом. При желании вы можете установить его 7-й бит и тогда постоянно будете получать из него значения от 128 до 255. Правда, делается это обычно в целях защиты программ (например, такой метод применен в игре NIGHT SHADE), но это уже совсем другая тема.
Когда требуется получить наибольшую степень случайности, прибегают к математическим расчетам. Разберем одну из таких «математических» подпрограмм. Несмотря на ее простоту, она вырабатывает все же достаточно длинную последовательность неповторяющихся значений, чтобы их можно было рассматривать в качестве случайных.
RND255 PUSH BC PUSH DE PUSH HL ; Регистровая пара HL загружается значением из счетчика «случайных» чисел ; (это может быть, например, системная переменная 23670/23671, ; которая используется Бейсиком для тех же целей) LD HL,(ADDR) LD DE,7 ;дальше следует расчет очередного ; значения счетчика ADD HL,DE LD E,L LD D,H ADD HL,HL ADD HL,HL LD C,L LD B,H ADD HL,HL ADD HL,BC ADD HL,DE LD (ADDR),HL ;сохранение значения счетчика «случайных» ; чисел для последующих расчетов LD A,H ;регистр A загружается значением ; старшего байта счетчика POP HL POP DE POP BC RET ADDR DEFW 0Эта процедура возвращает в аккумуляторе «случайные» числа от 0 до 255. Однако в подавляющем большинстве случаев нужно иметь возможность получать значения из произвольного диапазона. С этой целью дополним подпрограмму RND255 расчетами по ограничению максимального значения и назовем новую процедуру просто RND. Перед обращением к ней в регистре E задается верхняя граница вырабатываемых «случайных» чисел. Например, для получения в аккумуляторе числа от 0 до 50 в регистр E нужно загрузить значение 51:
RND CALL RND255 LD L,A LD H,0 LD D,H CALL 12457 LD A,H RET RND255 .........Здесь вновь появилась еще одна подпрограмма ПЗУ, расположенная по адресу 12457. Она выполняет целочисленное умножение двух чисел, записанных в регистровых парах DE и HL. Произведение возвращается в HL. Если в результате умножения получится число, превышающее 65535, то будет установлен флаг CY, иначе выполняется условие NC. Проверка переполнения может оказаться полезной, когда перемножаются не известные заранее величины. В подпрограмме RND это условие проверять не нужно, так как оба сомножителя не превышают величины 255 (H и D предварительно обнуляются).
После того, как мы получили в свое распоряжение подпрограмму генерации случайных чисел, рассмотрим один занимательный пример ее применения. Представьте себе некое подобие мишени, состоящей, как и положено, из окружностей и ряда цифр, характеризующих заработанные вами очки при попадании в ту или иную ее часть. Вы нажимаете любую клавишу компьютера и в ту же секунду раздается звук, очень похожий на пролетающую мимо вашего уха пулю, а в мишени появляется отверстие с рваными краями. Нажимаете еще раз - снова попадание, но уже совсем в другом месте (рис. 6.8) и изображение отверстия тоже стало каким-то другим. Повторив эту процедуру много раз, вы легко можете убедиться в том, что «пули», как и при настоящей стрельбе, ложатся на мишень совершенно случайно. То же самое можно сказать и о характере отверстий. Отсюда ясно, что программа, реализующая эту игрушку, должна вырабатывать для каждого выстрела три случайных числа: координаты X и Y места попадания и номер изображения для пулевого отверстия. Теперь можно обратиться к самой программе и кратко ее прокомментировать.
Рис. 6.8. Программа МИШЕНЬ
ORG 60000 ENT $ ; Задание постоянных атрибутов экрана LD A,7 LD (23693),A XOR A CALL 8859 CALL 3435 LD A,2 CALL 5633 ; Ввод символов UDG - три «пулевые отверстия» LD HL,UDG LD (23675),HL ; Основная часть программы CALL MISH ;рисование мишени MAIN CALL WAIT ;ожидание нажатия любой клавиши CP " " RET Z LD A,22 RST 16 LD E,20 ;задание диапазона для координаты Y CALL RND RST 16 LD E,30 ;задание диапазона для координаты X CALL RND RST 16 LD A,16 RST 16 LD A,6 RST 16 LD E,3 ;задание номера «пулевого отверстия» CALL RND ADD A,144 ;вычисление кода спрайта RST 16 CALL SND ;звуковой сигнал JR MAIN ; Подпрограмма вывода на экран мишени MISH LD C,20 CALL CIRC LD C,40 CALL CIRC LD C,60 CALL CIRC LD C,80 CALL CIRC LD DE,TEXT LD BC,LENTXT JP 8252 ; Подпрограмма рисования окружностей CIRC EXX PUSH HL EXX PUSH BC LD A,120 CALL 11560 LD A,90 CALL 11560 POP BC LD B,0 CALL 11563 CALL 9005 EXX POP HL EXX RET ; Подпрограмма остановки счета WAIT XOR A LD (23560),A WAIT1 LD A,(23560) AND A JR Z,WAIT1 RET ; Подпрограммы RND ......... SND LD B,80 LD HL,150 LD DE,1 SND1 ......... ; Данные для мишени TEXT DEFB 22,10,14 DEFM "10" DEFB 22,10,18 DEFM "8" DEFB 22,10,21 DEFM "6" DEFB 22,10,23 DEFM "4" DEFB 22,10,11 DEFM "8" DEFB 22,10,8 DEFM "6" DEFB 22,10,6 DEFM "4" LENTXT EQU $-TEXT ; Данные для «пулевых отверстий» UDG DEFB 4,20,62,60,127,60,40,8 DEFB 9,95,252,63,126,44,8,8 DEFB 16,48,244,63,28,56,28,8
Что касается окон, то в принципе этот вопрос нами уже решен и можно было бы к нему вновь не возвращаться. Однако использование подпрограмм, работающих с окнами в том виде, в котором мы их предложили ранее, не всегда удобно. Если все параметры окон фиксированы, то нелепо каждый раз переопределять переменные ROW, COL и иже с ними. Лучше составить блоки данных, содержащие сведения о размерах, местоположении и всех прочих характеристиках, а затем выполнять любые преобразования окон, передавая подпрограммам единственное значение - адрес таблицы параметров (то бишь блока данных).
В начале книги мы говорили о существовании такой группы регистров, как индексные. Группа эта немногочисленна и включает в себя лишь два регистра: IX и IY. Как мы уже сообщали, оба они состоят из 16 бит и разделить их на половинки «законными» методами невозможно, поэтому они обычно рассматриваются не как регистровые пары, а как отдельные регистры.
Но для каких целей они существуют, в каких ситуациях их удобно применять и в каких группах команд они могут встречаться? Обычно эти регистры используются при обработке массивов, блоков данных или таблиц, а употребляются они практически во всех типах команд, в которых может принимать участие регистровая пара HL. За более подробной информацией на этот счет можете обратиться к Приложению I, где в алфавитном порядке приведены все команды микропроцессора.
Известно, что в Бейсике к любому элементу массива можно обратиться по индексу, например, оператор LET b=a(8) присвоит переменной b значение 8-го элемента массива a(). В ассемблере подобная запись может выглядеть так:
LD B,(IX+7)Обратите внимание, что первый элемент массива имеет индекс 0, а не 1. В отличие от массивов Бейсика, максимальный индекс у регистров IX и IY не может превышать 127, но зато допускаются отрицательные значения номера элемента массива. Таким образом, общий размер адресуемой области составляет 256 байтовых элементов, а индексный регистр указывает на его «середину».
Реальным примером большой структуры данных могут служить системные переменные Бейсика. Обычно они адресуются регистром IY, который указывает на переменную ERR_NR, находящуюся по адресу 23610. Кстати, именно в связи с этим регистр IY лучше оставить в покое и никак не изменять его в своих программах, по крайней мере, до тех пор, пока вы так или иначе используете операционную систему компьютера. Что касается регистра IX, то им вы можете смело пользоваться при любых обстоятельствах.
Наверное, лучше всего объяснить идею применения индексных регистров на конкретном примере. В качестве такого примера приведем уже знакомые вам процедуры очистки окон и установки в них постоянных атрибутов (тем более, что они понадобятся при составлении программы многокадровой заставки), но графические переменные заменим единой структурой, первый элемент которой адресуем через IX. Саму же структуру оставим без изменения, то есть на первом месте (по смещению 0, задаваемом как IX+0 или просто IX) по-прежнему будет находиться параметр COL, на втором (задаваемом как IX+1) - ROW, дальше LEN, HGT и ATTR. А чтобы новые процедуры отличить от описанных выше, изменим их имена на SETW и CLSW:
SETW LD DE,#5800 LD B,(IX+3) ;HGT LD C,(IX+2) ;LEN LD A,(IX+1) ;ROW LD L,A LD H,0 ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,DE LD A,L ADD A,(IX) ;COL LD L,A LD A,(IX+4) ;ATTR SETW1 PUSH BC PUSH HL SETW2 LD (HL),A INC HL DEC C JR NZ,SETW2 POP HL POP BC LD DE,32 ADD HL,DE DJNZ SETW1 RET ; ----------------- CLSW LD B,(IX+3) ;HGT LD C,(IX+2) ;LEN LD A,(IX+1) ;ROW CLSW1 PUSH AF PUSH BC PUSH DE CALL 3742 POP DE LD A,L ADD A,(IX) ;COL LD L,A LD B,8 CLSW2 PUSH HL PUSH BC LD B,C CLSW3 LD (HL),0 INC HL DJNZ CLSW3 POP BC POP HL INC H DJNZ CLSW2 POP BC POP AF INC A DJNZ CLSW1 RETСравнив эти подпрограммы с SETV и CLSV, вы найдете много общего. Собственно, отличаются они друг от друга только способом передачи параметров. И это лишний раз доказывает, насколько ассемблер гибче всех прочих языков программирования - всякий раз вы можете использовать совершенно новые или видоизмененные процедуры, наиболее пригодные для применения именно в данном конкретном случае.
Составим блоки данных, включающих в себя по пять известных вам параметров для пяти различных окон, которые будут появляться на экране:
WIN0 DEFB 5,0,23,3,%01001110 WIN1 DEFB 8,6,17,13,%01010111 WIN2 DEFB 13,5,13,15,%01011110 WIN3 DEFB 13,9,13,11,%01101000 WIN4 DEFB 6,5,12,15,%01110101Теперь перед обращением к процедурам SETW и CLSW достаточно в регистр IX записать адрес соответствующего блока и окно появится на экране. Напишем подпрограмму формирования окна заданных размеров и черной «тени» под ним:
WINDOW INC (IX) ;смещаем окно вправо (COL) INC (IX+1) ; и вниз (ROW) LD A,(IX+4) ;ATTR PUSH AF LD (IX+4),1 ;байт атрибутов для «тени» CALL SETW ;изменяем только атрибуты DEC (IX) ;возвращаем окно на прежнее место DEC (IX+1) POP AF LD (IX+4),A ;восстанавливаем байт атрибутов CALL CLSW ;очищаем окно CALL SETW ;окрашиваем заданным цветом CALL BOX ;рисуем рамку вокруг окна RET ; Подпрограмма, рисующая рамку вокруг окна BOX LD A,(IX+1) ;ROW PUSH AF CALL 3742 ;вычисляем адрес экрана LD A,L ADD A,(IX) ;COL LD L,A LD B,(IX+2) ;LEN BOX1 LD (HL),255 ;проводим верхнюю линию INC HL DJNZ BOX1 LD B,(IX+3) ;HGT POP AF BOX2 PUSH AF ;по точкам рисуем боковые линии ; сверху вниз PUSH BC CALL 3742 LD A,L ADD A,(IX) LD L,A LD B,8 BOX3 PUSH HL LD A,(HL) OR 128 ;левая точка LD (HL),A LD A,(IX+2) ADD A,L DEC A LD L,A LD A,(HL) OR 1 ;правая точка LD (HL),A POP HL INC H DJNZ BOX3 POP BC POP AF INC A DJNZ BOX2 DEC H LD B,(IX+2) ;LEN BOX4 LD (HL),255 ;проводим нижнюю линию INC HL DJNZ BOX4 RETНачало программы традиционное - задание постоянных атрибутов экрана и окрашивание бордюра. Ну, а чтобы тексты выглядели более привлекательно, обратимся к уже известной процедуре утолщения символов - BOLD.
ORG 60000 ENT $ LD A,38 LD (23693),A LD A,4 CALL 8859 CALL BOLDЗатем сформируем окно, в которое должно быть помещено название игры (рис. 6.9), а в нижней части заставки напечатаем принятый в таких случаях текст «Select options 0 to 4» («Выберите опции от 0 до 4»). Поскольку к метке MENU программа будет неоднократно возвращаться из кадров, то чтобы избежать хаотического наложения окон, целесообразно каждый раз очищать экран.
Рис. 6.9. Многокадровая заставка
MENU CALL 3435 LD A,2 CALL 5633 LD DE,TEXT5 LD BC,ENDTXT-TEXT5 CALL 8252 ;«Select options 0 to 4» LD IX,WIN0 ;окно для названия игры CALL CLSW CALL SETW CALL BOXВ сформированном окне напечатаем название игры FOOTBALL, для чего используем расширенные подпрограммой DBLSYM буквы.
LD DE,COORD ;позиционирование курсора LD BC,TEXT-COORD CALL 8252 LD HL,TEXT ;вывод названия игры LD B,8 MET LD A,(HL) PUSH HL PUSH BC CALL DBLSYM POP BC POP HL INC HL DJNZ METСделаем для меню окно с черной тенью и поместим в него текст, в соответствии с которым вы можете обратиться к одному из четырех кадров, нажав клавиши 1-4, или начать игру, выбрав клавишу 0 (для упрощения программы нажатие 0 или 4 возвращает вас в редактор GENS4 или в Бейсик).
LD IX,WIN1 ;окно основного меню CALL WINDOW LD DE,TEXT1 ;текст меню LD BC,TEXT2-TEXT1 CALL 8252Теперь можно написать блок управления, который, как ни странно, выглядит довольно простым. Программка «крутится» в цикле, пока не нажата одна из указанных в кавычках клавиш. При нажатии же на клавишу, команда сравнения CP изменит биты флагового регистра (в частности флаг Z установится в ноль), после чего следующая команда JR Z,KADR? осуществит переход на выбранный вами кадр.
XOR A LD (23560),A CYCLE LD A,(23560) CP "1" JR Z,KADR1 CP "2" JR Z,KADR2 CP "3" JR Z,KADR3 CP "4" JR Z,EXIT CP "0" JR NZ,CYCLE ; При нажатии на клавиши 0 или 4 восстанавливаются атрибуты экрана и стандартный ; набор символов, после чего происходит выход в редактор GENS или в Бейсик. EXIT LD A,7 CALL 8859 ;белый бордюр LD A,%00111000 ;стандартные атрибуты LD (23693),A CALL 3435 LD A,2 CALL 5633 LD HL,15360 LD (23606),HL RETЧасти программы, формирующие окна кадров и печатающие в них тексты, исключительно похожи друг на друга. Тем не менее, имеются и некоторые отличия, которые определяются конкретными данными для окон и текстов. Каждый из кадров начинается со звукового сигнала SND, о котором мы говорили в начале этой главы и заканчивается процедурой WAIT, которая фиксирует кадр на экране, позволяя увидеть его содержание и выбрать дальнейшие действия (в этой программе чисто умозрительно, за исключением клавиши Е, которая действительно возвращает вас в меню).
KADR1 CALL SND ;звуковой сигнал LD IX,WIN2 CALL WINDOW ;вывод окна LD DE,TEXT2 ;текст в окне LD BC,TEXT3-TEXT2 CALL 8252 CALL WAIT ;ожидание нажатия клавиши E JP MENU ;возврат в меню ; Формирование окна Кадра 2 и печать в нем текста KADR2 CALL SND LD IX,WIN3 CALL WINDOW LD DE,TEXT3 LD BC,TEXT4-TEXT3 CALL 8252 CALL WAIT JP MENU ; Формирование окна Кадра 3 и печать в нем текста KADR3 CALL SND LD IX,WIN4 CALL WINDOW LD DE,TEXT4 LD BC,TEXT5-TEXT4 CALL 8252 CALL WAIT JP MENU ; Подпрограмма ожидания нажатия клавиши E WAIT XOR A LD (23560),A WAIT1 LD A,(23560) CP "E" RET Z CP "e" JR NZ,WAIT1 RET SND LD B,10 LD HL,350 LD DE,4 SND1 ......... DBLSYM ......... BOLD ......... SETW ......... CLSW ......... WINDOW ......... BOX ......... ; Данные для формирования окна с названием игры WIN0 ......... ; Данные для формирования всех окон с тенями WIN1 ......... WIN2 ......... WIN3 ......... WIN4 ......... ; Данные для печати названия игры COORD DEFB 22,1,8,16,6,19,1 TEXT DEFM "FOOTBALL" ; Данные для печати текста Меню TEXT1 DEFB 22,7,13,16,7,17,2,19,1 DEFM "M E N U" DEFB 22,9,9 DEFM "0. START··GAME" DEFB 22,11,9 DEFM "1. KEYBOARD" DEFB 22,13,9 DEFM "2. JOYSTICK" DEFB 22,15,9 DEFM "3. HI··SCORE" DEFB 22,17,9 DEFM "4. DEMO MODE" ; Данные для печати текста Кадра 1 TEXT2 DEFB 22,6,14,17,3,16,1 DEFM "Define keys" DEFB 22,8,15 DEFM "LEFT....O" DEFB 22,10,15 DEFM "RIGHT...P" DEFB 22,12,15 DEFM "UP......Q" DEFB 22,14,15 DEFM "DOWN....A" DEFB 22,16,15 DEFM "FIRE....M" DEFB 22,18,16 DEFM "MENU--E" ; Данные для печати текста Кадра 2 TEXT3 DEFB 22,10,16,17,5,16,0 DEFM "Joystick" DEFB 22,12,14 DEFM "1. KEMPSTON" DEFB 22,14,14 DEFM "2. SINCLAIR" DEFB 22,16,14 DEFM "3. CURSOR" DEFB 22,18,16 DEFM "MENU--E" ; Данные для печати текста Кадра 3 TEXT4 DEFB 22,7,8,17,6,16,1 DEFM "Hi score" DEFB 22,8,7 DEFM "PETR...726" DEFB 22,10,7 DEFM "IGOR...694" DEFB 22,12,7 DEFM "ALEX...605" DEFB 22,14,7 DEFM "SERG...523" DEFB 22,16,7 DEFM "WLAD...419" DEFB 22,18,8 DEFM "MENU--E" ; Данные для текста под заставкой TEXT5 DEFB 22,21,6,16,7,17,4,19,0 DEFM "Select options 0 to 4" ENDTXT
Глава 5 | Глава 7