Главная » Ассемблер, Железо » Анимация двухмерных изображений

0

Анимация в двухмерном режиме, то есть создание на экране иллюзии движения плоских изображений (спрайтов), также реализуется при помощи масок [1]. Однако если для неподвижного объекта (например, буквы шрифта) достаточно одной маски, то движущийся объект требует отдельную маску для каждой фазы движения в каждом из возможных направлений. Чтобы получить приличную иллюзию движения, необходимо отобразить от 8 до 12 фаз [20]. Человеческий глаз способен различать углы менее одного градуса, поэтому, чтобы создать иллюзию вращения крупного объекта или движения его по произвольным направлениям, нужно иметь по маске на каждый из возможных трехсот шестидесяти градусов поворота. Сложные объекты способны совершать разнообразные виды движений, для каждого из которых нужен собственный комплект масок. Объект типа «человек», например, может идти, ползти, прыгать, приседать, взбираться по лестнице, плыть и т. п. Механические объекты обычно проще, чем живые, — требуют меньшего количества фаз и разновидностей движения (поэтому их так любят использовать разработчики игр).

Современные компьютерные игры (например, стратегические) предусматривают одновременный вывод на экран нескольких сотен или даже тысяч объектов (подвижных и неподвижных). Можно сформировать группы, считая однотипными те объекты, для отображения которых пригодны одинаковые комплекты масок, однако количество групп все равно измеряется десятками. Одновременно на экран выводится до десяти типов танков, несколько типов самолетов, несколько типов солдат и т. д. В результате общее количество масок, которое необходимо хранить в памяти компьютера, является произведением количества групп объектов, умноженного на количество возможных видов движений объекта, на количество фаз движений и на количество направлений движения. Рассмотрим следующий пример: пусть имеется 10 типов объектов, способных совершать по два вида движений; при отображении объектов используется 10 фаз, объекты могут двигаться по 50 направлениям. Тогда в общей сложности нужно десять тысяч масок! Каждую

маску необходимо предварительно нарисовать, для чего применяются специальные анимационные программные пакеты (проводить такую работу ручным способом слишком дорого и долго). Кроме того, одна маска сравнительно небольшого объекта размером 32×32 пиксела занимает от 1 до 4 Кбайт памяти, а десять тысяч масок требуют соответственно 10-40 Мбайт!

Как следует из приведенного примера, при разработке компьютерных игр и других программ, использующих анимацию, приходится искусственно ограничивать количество типов объектов, видов и направлений движения.

В общем случае цикл вывода движущегося объекта включает следующую последовательность операций.

1.   Вычисление координат области отображения объекта.

2.    Сохранение фона области отображения.

3.    Рисование текущей фазы движения объекта.

4.    Ожидание начала обратного хода луча по кадру.

5.    Стирание изображения объекта путем восстановления фона. Для сохранения фона нужно выделить область памяти такого же размера, что и для маски отображаемой фазы движения. Если маска объекта имеет размер MxN пикселов, а цвет пиксела кодируется К байтами, то область сохранения фона занимает MxNxK байт. При работе с группой движущихся объектов необходимо запоминать последовательность их рисования на экране, поскольку стирание должно проводиться в порядке, обратном порядку вывода. Вывод изображения на экран обязательно должен быть синхронизирован с началом хода луча по кадру — в противном случае изображение движущегося объекта будет искажаться всякий раз, когда совпадают момент перерисовки и вывода объекта на экран. Если в режиме анимации используется только одна видеостраница, то обязательно необходимо учитывать скорость вывода изображения в видеопамять — даже при наличии синхронизации с ходом луча по кадру и высокой скорости вывода данных в верхней части картинки образуется зона искажений. В зависимости от алгоритма вывода изображений возможны два основных типа искажений — разрезание картинки или непрорисовка некоторых ее участков (рис. 4.15). Ширина зоны искажений определяется количеством и размером динамических (движущихся или изменяющихся) объектов, их размерами и скоростью вывода изображения на экран. Зона искажений заканчивается в том месте экрана, где при любом возможном количестве и порядке вывода объектов центральный процессор заведомо опережает луч электронно-лучевой трубки (ЭЛТ). Обычно изготовители компьютерных игр идут на маленькую хитрость — занимают верхнюю часть экрана широким статическим изображением (например, графическим меню).

Рис. 4.15. Основные виды искажений при выводе движущихся объектов

При использовании одной видеостраницы движение объектов по экрану будет выглядеть плавным только в том случае, если процессор успевает полностью перерисовывать изображение в течение одного кадра ЭЛТ. Поскольку частота кадров у современных мониторов обычно составляет 65-100 Гц, процессор должен успеть перерисовать кадр за 10-15 миллисекунд, причем для каждого объекта нужно (по изложенному выше алгоритму) успеть:

•     восстановить фон в предыдущей позиции;

•     сохранить фон в новой позиции;

•     нарисовать изображение.

Кроме того, допустимая высота зоны искажений обычно составляет не более 1/5 высоты экрана: пока луч находится в этой зоне, процессор должен успеть перерисовать все подвижные объекты в остальной части экрана.

Проведем оценочные расчеты при условии, что скорость вывода данных составляет 10 млн пикселов в секунду. За время одного кадра (15 мс) процессор успеет перерисовать 150 тыс пикселов, но это число нужно поделить на 3 (для каждого объекта — 3 операции) и на 5 (под зону искажений выделена 1/5 часть экрана). В результате получается, что сумма размеров (в байтах) всех массивов-масок не должна превышать 10 Кбайт. Например, если размер маски равен 1 Кбайт (небольшой объект 32×32 в 256-цветном режиме), то можно вывести 10 движущихся объектов, а при размере маски 4 Кбайт (32×32, режим TrueColor) — всего 2 объекта!

Чтобы ускорить вывод данных, разработчикам программ приходится применять множество разнообразных хитростей. Первая хитрость основана на том, что современные компьютеры имеют большой объем оперативной памяти, и фон можно в ней хранить целиком, а не кусочками. Ускорение работы при этом получается даже больше, чем на треть, поскольку операция чтения из видеопамяти выполняется медленнее, чем запись в нее (видеоконтроллеры всегда оптимизированы по записи).

Второй трюк заключается в том, чтобы избавиться от операций контроля пересечения границ экрана при выводе маски объекта. Если видеопамять организована обычным образом, а контроля пересечения границ нет, то возможно появление ложных изображений вдоль левой и правой границ экрана (рис. 4.16, сверху) или зависание программы при выходе за пределы видеопамяти. Проблема легко решается, если объем видеопамяти позволяет установить логическую длину строки больше физической — при выходе за боковые границы маска попадает в неотображаемую область памяти (рис. 4.16, снизу). Кроме того, выравнивание логической длины строки на 2N‘ пикселов позволяет (хотя и весьма незначительно) упростить расчет координат точек изображения.

Рис. 4.16. Использование невидимого участка видеопамяти для подавления ложных изображений

Рисунок 4.16 создает впечатление, что ложное изображение подавляется только при выходе за правую границу. На самом деле подавление происходит и в случае выхода за левый край экрана: хорошей моделью развертки линейной памяти контроллера на экран является спираль (пружина), показанная на рис. 4.17. При выходе за любую боковую границу маска попадает на невидимый участок.

Рис. 4.17. Модепь развертки видеопамяти на экран монитора

Можно избавиться от необходимости контролировать пересечение не только боковых, но также верхней и нижней границ. С этой целью создаются горизонтальные неотображаемые области, точнее — горизонтальная область над страницей, поскольку невидимая область под страницей присутствует изначально (см. рис. 4.1 и 4.18). Ширина вертикальной невидимой области должна быть больше или равна максимальной возможной ширине маски Lmax. Высота горизонтальной области должна быть равна максимальной возможной высоте маски Нтах.

К сожалению, трюки с защитными полосами применимы не всегда — очень часто по крайней мере одну из сторон изображения занимает полоса меню управления. Проще всего контролировать нижнюю границу спрайта, поэтому (если имеется возможность выбора) лучше всего размещать полосу меню снизу.

Самый эффективный способ устранения искажений анимирован- ного изображения состоит в использовании упомянутого выше механизма переключения страниц: пока видеоконтроллер отображает на экран одну страницу, процессор перерисовывает другую. Однако при этом расход видеопамяти увеличивается вдвое — на старых видеоконтроллерах ее может оказаться недостаточно для создания второй страницы. Кроме того, невидимые области также съедают заметный объем памяти. Организация видеопамяти в режиме переключения страниц при наличии невидимых областей показана на рис. 4.18.

Рис. 4.18. Организация видеопамяти в режиме переключения страниц при наличии защитных неотображаемых полос

При переключении страниц требования к скорости перерисовки кадра значительно ниже, поскольку процессор может заниматься выводом данных в течение всего времени хода луча по кадру. Кроме того, уже не обязательно перерисовывать кадры со скоростью их вывода на экран монитора, то есть переключение страниц можно выполнять не в каждом кадре, а через один или через два (но обязательно — через одинаковое число кадров). Для создания приемлемого изображения достаточно генерировать 25-30 кадров в секунду, то есть на перерисовку каждого кадра можно тратить 30-40 миллисекунд. В 256-цветном режиме с разрешением 640×480 точек за это время можно полностью перерисовать весь экран! Программа, реализующая анимацию в одностраничном режиме, показана в листингах 4.14-4.20. Листинг 4.14 — это усовершенствованная версия программы из листинга 2.1, реализующая доступ к видеопамяти и дополнительной оперативной памяти через раздельные сегменты большого размера с контролем границ. Пределы сегментов установлены в расчете на минимальную конфигурацию современных ПК — 4 Мбайт ОЗУ и 2 Мбайт видеопамяти, но в примере используется только часть (меньше половины) выделенного пространства. В принципе, размеры указанных сегментов можно задавать динамически (в соответствии с конфигурацией конкретного компьютера), однако недавно возникла проблема определения объема ОЗУ, если он больше 64 Мбайт (предел, на который были когда-то рассчитаны стандартные функции BIOS [85]). Процедура GInitiali- zati on использует для загрузки теневых регистров вспомогательную подпрограмму SetSegAddrModeForFSGS, а затем разблокирует линию А20 при помощи подпрограмм ЕпаЫ е_А20 и устанавливает обработчик прерывания по нарушению границ сегментов MemoryProtectionlnter- rupt при помощи процедуры SetProtectionlnterrupt.

Листинг 4.14. Подпрограмма, выполняющая настройку регистра FS на видеопамять, регистра GS — на дополнительную память

; Порт, управляющий запретом немаскируемых прерываний

CMOS ADDR equ 0070h

CM0S_DATA equ 0071h

; Селекторы сегментов

SYS_PROT_CS equ 0008h

SYSREALSEG equ OOlOh

SYSVIDEOSEG equ OOlSh

SYS_LINEAR_SEG equ 0020h

DATASEG

; Текстовые сообщения ProtErr

DB LIGHTRED.12,0

DB "Ошибка: нарушение границ сегмента памяти",О

DB YELLOW.24,29,"Нажмите любую клавишу",О

; Область сохранения старого вектора прерывания

; по IRQ5 и защите памяти 01dProtlnterruptOffset DW ? OldProtlnterruptSegment DW ? ENDS

CODESEG

;* Подготовка системы к работе *

• A ‘frit к trick ‘A’^rtWrink Aк к A к

PROC GInitialization NEAR pushad

: Занести линейный адрес видеопамяти (для FS)

mov EAX.[LinearVideoBuffer]

mov [word ptr CS:GDT+26],AX ror EAX,16

mov [byte ptr CS:GDT+28],AL

mov [byte ptr CS:GDT+31],AH

; Установить предел видеопамяти 2 Мб

mov EAX,2*100000h

shr EAX.12 :поделить на гранулярность (4 Кб) dec ЕАХ ;уменьшить на 1

mov [word ptr CS:GDT+24],AX

Листинг 4.14(продолжение) гог ЕАХ.16

or [byte ptr CS:GDT+30].AL : Занести линейный адрес расширенной памяти (для GS)

mov ЕАХ,llOOOOh;1 Мб + 64 Кб

mov [word ptr CS:GDT+34].AX ror EAX.16

mov [byte ptr CS:GDT+36] ,AL

mov [byte ptr CS:GDT+39],AH : Установить предел расширенной памяти

; (4 Мб минус 1,064 Мб)

mov EAX,4*100000h-110000h

shr ЕАХ,12 ;поделить на гранулярность (4 Кб)

dec ЕАХ уменьшить на 1

mov [word ptr CS:GDT+32],AX

ror EAX,16

or [byte ptr CS:GDT+38],AL

; Сохранить значения сегментных регистров в

; реальном режиме (кроме GS)

mov [CS:Save_SP],SP

mov АХ,SS

mov [CS:Save_SS],AX

mov AX.DS

mov [CS:Save_DS],AX

; (работаем теперь только с кодовым сегментом)

mov AX.CS

mov [word ptr CS:Self_Mod_CS],AX

mov DS.AX

cli

mov SS.AX

mov SP.offset Local_Stk_Top sti

; Установить режин линейной адресации call SetSegAddrModeForFSGS

: Восстановить значения сегментных регистров cli

mov SP,[CS:Save_SP]

mov AX,[CS:Save_SS]

mov SS.AX

mov AX.[CS:Save_DS]

mov DS.AX

sti

; Разрешить работу линии A20

call Enable_A20 : Замаскировать прерывание IRQ5 и установить : обработчик прерывания по нарушению границ сегментов call SetProtectionlnterrupt popad ret

ENDP Glnitialization

; Область сохранения значений сегментных регистров

SaveSP DW ?

Save_SS DW ?

SaveDS DW ?

: Указатель на GDT

GDTPtr DQ ?

: Таблица дескрипторов сегментов для : входа в защищенный режим

GDT DW OOOOOh,OOOOOh.OOOOOh,OOOOOh ;не используется DW OFFFFh,OOOOOh,09A00h,OOOOOh ;сегмент кода CS DW OFFFFh.OOOOOh,09200h,OOOOOh ;сегнент данных DS DW OOOOOh.OOOOOh.09200h.000F0h ;сегнент FS DW OOOOOh,OOOOOh.09200h,000F0h ;сегнент GS

; Локальный стек для защищенного режима

; (организован внутри кодового сегмента) label GDTEnd word

DB 255 DUP(OFFh) Local_Stk_Top

DB (OFFh)

;* Процедура, изменяющая содержимое теневых * ;* регистров FS и GS*

• Д-Jr А1 ** А1 ** к к к кк к ‘A1‘A1 A1 кк кк кккк кк A* A"Jr ккк к rff’А? кк1 A ‘A A1 A’

PROC SetSegAddrModeForFSGS near

; Вычислить линейный адрес кодового сегиента

mov AX.CS movzx ЕАХ,АХ

shl ЕАХ,4 ;уиножить номер параграфа на 16

mov ЕВХ,ЕАХ сохранить линейный адрес в ЕВХ

; Занести иладшее слово линейного адреса в дескрипторы

; сегментов кода и данных

mov [word ptr CS:GDT+10],АХ

mov [word ptr CS:GDT+18].AX

; Переставить местами старшее и младшее слова гог ЕАХ.16

; Занести биты 16-23 линейного адреса в дескрипторы

; сегментов кода и данных

mov [byte ptr CS:GDT+12],AL

mov [byte ptr CS:GDT+20],AL

; Установить предел (Limit) и базу (Base) для GDTR

add ЕВХ, offset GDT

mov [word ptr CS:GDTPtr],(offset GDTEnd-GDT-1)

mov [dword ptr CS:GDTPtr+2],EBX

; Сохранить регистр флагов pushf

; Запретить прерывания, так как таблица прерываний IDT

; не сформирована для защищенного режима

Листинг 4.14(продолжение) cli

: Запретить немаскируемые прерывания NMI in AL.CMOSADDR

mov AH.AL

or AL,080h установить старший разряд out CMOS_ADDR,AL ;не затрагивая остальные and AH,080h

: Запомнить старое состояние маски NMI

mov СН.АН : Перейти в защищенный режим

lgdt [fword ptr CS:GDTPtr]

mov BX,CS запомнить сегмент кода

mov EAX.CRO

or AL.Olb установить бит РЕ

mov CRO.EAX защита разрешена

; Безусловный дальний переход на метку SetPMode

; (очистить очередь коианд и перезагрузить CS)

DB OEAh

DW (offset SetPMode) DW SYS_PROT_CS

SetPMode:

; Подготовить границы сегментов

mov AX,SYS_REAL_SEG

mov SS.AX

mov DS.AX

mov ES,AX

: Настроить сегмент FS на видеопамять

mov AX,SYS_VIDEO_SEG

mov FS.AX

; Настроить сегмент GS на расширенную память

mov АХ.SYS_LINEAR_SEG

mov GS.AX

; Вернуться в реальный режим

mov EAX.CRO

and AL,11111110b ;сбросить бит РЕ

mov CRO.EAX защита отключена

; Безусловный дальний переход на метку SetRMode

; (очистить очередь команд и перезагрузить CS)

DB OEAh

DW (offset SetRMode) Self_Mod_CS DW ?

SetRMode:

; Регистры стека и данных

; настроить на сегмент кода

mov SS.BX

mov DS.BX : Обнулить ES

хог АХ.АХ

mov ES.AX

; Возврат в реальный режин,

; прерывания снова разрешены

in AL.CMOS_ADDR

and AL.07Fh

or AL.CH

out CMOS_ADDR,AL

popf

ret

ENDP SetSegAddrModeForFSGS

;* Разрешить работу с панятью выше 1 Мб *

• А А А А А А А; А А А; АЛЛА1 А А Л Л Л Л А Л; А А Л * Л А А А А Л; А А А Л * А А А;

PROC Enable_A20 near

call Wait8042BufferEmpty

mov AL.ODlh ;конанда управления линий A20

out 64h,AL

call’ Wait8042BuffeгEшpty

mov AL.ODFh разрешить работу линии

out 60h,AL

call Wait8042BufferEmpty

ret

ENDP Enable_A20

;* ОЖИДАНИЕ ОЧИСТКИ ВХОДНОГО БУФЕРА IB042 * ;* При выходе из процедуры:*

;* флаг ZF установлен • норнальное завершение, * ;* флаг ZF сброшен • ошибка тайн-аута.*

ргос Wait8042BufferEmpty near push СХ

mov СХ.OFFFFh ;задать число циклов

@@kb: in AL,64h ;получить статус

test AL.lOb ;буфер i8042 свободен? loopnz

@@kb ;если нет. то цикл pop СХ

; (если при выходе сброшен флаг ZF – ошибка) ret

endp Wait8042BufferEmpty

;* УСТАНОВИТЬ ВЕКТОР ПРЕРЫВАНИЯ ПО * ;* СРАБАТЫВАНИЮ ЗАЩИТЫ ПАМЯТИ *

PROC SetProtectionlnterrupt NEAR pusha

push ES

Листинг 4.14 (продолжение)

mov АХ,[CS:Mai nDataSeg]

mov DS.AX : Замаскировать прерывание IRQ5 cli

in AL,21h or AL.100000b out 21h.AL sti

; Установить вектор прерывания

; Настроить ES на область векторов

mov АХ,0

mov ES.AX

; Сохранить старый вектор

mov АХ,[ES:13*4]

mov [DldProtlnterruptDffset].АХ

mov АХ,[ES:13*4+2]

mov [D1dProtInterruptSegment],AX

: Установить новый вектор

cli

mov AX,offset MemoryProtectionlnterrupt

mov [ES:13*4],AX

mov AX.СS

mov [ES:13*4+2],AX

sti

pop ES

popa

ret

ENDP SetProtectionlnterrupt

;* ВОССТАНОВИТЬ СТАРЫЙ BEKTDP ЗАЩИТЫ *

PROC RestoreOldProtectionlnterrupt NEAR pusha

push ES

; Настроить ES на область векторов

mov AX.О

mov ES.AX

: Восстановить старый вектор cli

mov AX.[OldProtlnterruptDffset]

mov [ES:13*4],AX

mov AX,[OldProtlnterruptSegment]

mov [ES:13*4+2],AX

sti

pop ES

рора ret

ENDP RestoreOI dProtectioril interrupt

;* НОВЫЙ ОБРАБОТЧИК ПРЕРЫВАНИЯ ПО * ;* НАРУШЕНИЮ ГРАНИЦ СЕГМЕНТОВ ПАМЯТИ *

PROC MemoryProtectionlnterrupt NEAR

mov AX,[CS:MainDataSeg]

mov DS, AX

; Установить текстовый режин и очистить экран

mov АХ.З int 10h

; Скрыть курсор – убрать за нижнюю границу экрана

mov [ScreenString],25

mov [ScreenColumn],0 call SetCursorPosition

; Вывести текстовые сообщения на экран

MShowColorText 2,ProtErr

; Ожидать нажатия клавиши

cal1 GetChar : Восстановить старый обработчик прерывания

cal1 RestoreOIdProtecti onlnterrupt

; Выход в DOS

mov AH,4Ch int 21h ENDP MemoryProtectionlnterrupt ENDS

После выполнения процедуры GInitial i zation к видеопамяти можно обращаться через сегментный регистр FS, а к дополнительной оперативной памяти — через 6S. В случае нарушения установленных границ сегментов происходит прерывание общей защиты памяти, и процедура MemoryProtectionlnterrupt выдает сообщение об ошибке, а затем осуществляет аварийное завершение работы программы с немедленным выходом в DOS.

ВНИМАНИЕ

Система защиты памяти и параллельный порт LPT2 настроены на один и тот же вектор, поэтому сигнал от LPT2 обязательно нужно маскировать при установке обработчика сигнала защиты памяти.

Листинг 4.15 является видоизмененным для сегментной адресации вариантом листинга 4.3. Он включает две подпрограммы: процедуру вывода символа PutGraChar и процедуру очистки экрана GC1 ear- Screen для 256-цветных режимов с линейным видеобуфером.

Листинг 4.15. Процедуры вывода символа и очистки экрана

для 256-цветных режимов с линейной адресацией видеобуфера

DATASEG

; Цвет текста в графическом режиме по умолчанию DefaultCo1or

DB WHITE ;белый

; Цвет фона в графическом режиме по уиолчанию Defaul tBackgrourid

DB BLACK

; черный ENDS

CODESEG

;* ВЫВОД СИМВОЛА 8×16 НА ЭКРАН В ГРАФИЧЕСКОМ РЕЖИМЕ *

;*(для 256-цветных режимов)*

;* Все параметры передаются через регистры:*

;* AL – ASCII-код символа;*

;* DH – номер текстовой строки экрана;*

;* DL – номер текстовой колонки экрана:*

;* Используются цвет символов и цвет фона.*

;* заданные по умолчанию.*

PROC PutGraCharNEAR

pushDS pushad

movCX.[CS:MainDataSeg]

movDS.CX

eld

; Смещение символа от начала шрифта

movSI,offset Font8xl6

xorAH.AH

shlAX.4

addSI.AX

; Вычислить левый верхний угол символа

xorЕВХ,ЕВХ

movBL.DH

shlЕВХ,14 ;умножить ноиер строки на 16*1024

xorDH.DH

shlDX,3 ;умножить номер столбца на В

orBX.DX

movEDI.ЕВХ

movBL,[DefaultColor]

movDL.[DefaultBackground]

movAH.16 ;счетчик строк маски буквы MHO: lodsb

movСХ.В ;счетчик точек в строке маски

@GM1: ro1AL.1

jc Р0М2

mov [FS:EDI],DL

jmp ШЗ Р@М2:

mov [FS:EDI].BL Р@МЗ:

inc EDI loop PPM1

add EDI,Logica1StringLength-B dec AH jnz Ш0 ;Завершение процедуры

@@EndPutGraChar: popad pop DS

inc DL ret

ENDP PutGraChar

;* ОЧИСТКА ЭКРАНА В ГРАФИЧЕСКОМ РЕЖИМЕ * ;* (процедура паранетров не инеет) *

PROC GC1earScreen NEAR pushad

; Унножить высоту экрана ScreenHeigth на логическую

; ширину строки (1024 пиксела)

mov ЕСХ.ScreenHeigth shl ЕСХ,10

mov EDI,О : Заполнить видеопамять нулями

mov AL,0 :черный цвет

@@NextPixe1s:

mov [FS:EDI],AL

inc EDI

dec ECX

jnz

@@NextPixe1s

popad

ret

ENDP GC1earScreen ENDS

В листинге 4.16 приведено описание констант, глобальных пёремен- ных и макрокоманд, используемых в листингах 4.18-4.22. Макрокоманды в данном случае делают текст значительно компактнее и практически не маскируют особенности работы программы. Макрос DrawMImage осуществляет подготовку параметров и вызов функции рисования движущегося изображения DrawMovinglmage, макрос DeleteMImage — подготовку параметров и вызов функции стирания движущегося изображения Deletelmage, а макрос DrawSImage — подготовку параметров и вызов функции рисования статического изображения DrawStaticImage.

Объем данных в приведенном примере невелик, и потому все массивы размещены в основной области памяти DOS. В программах с большим размером структур данных можно свободно использовать для хранения переменных и массивов расширенную память, однако следует учитывать, что вся ответственность за распределение расширенной памяти при этом возлагается на программиста. Обычно количество простых переменных не превышает нескольких тысяч, и для их хранения лучше все-таки применять обычную память, автоматически распределяемую программой-компилятором. В расширенной памяти удобно хранить большие объекты (например, статический фон для экранного изображения) и группы объектов с повторяющейся структурой (например, маски различных фаз движения в программах с двумерной анимацией). При необходимости также можно из специальных файлов загружать прямо в расширенную память крупные текстовые сообщения (используемые обычно в режиме подсказки).

Листинг 4.16. Описание констант, глобальных переменных и макрокоманд

; КОНСТАНТЫ

; Режин с разрешением 640×480, 256 цветов GraphicsMode equ 4101h

; Логическая ширина строки в пикселах LogicalStringLength equ 1024

; Ширина экрана в пикселах ScreenLength equ 640

; Высота экрана, строк ScreenHeigth equ 480

; Количество "облаков" на небе (облака накладываются

; друг на друга) MaxCloudNum equ 10

; Число кадров взлета (разгона) ракеты MaxRktStartFrame equ 56

; Число кадров взрыва MaxExplFrameNumber equ 10 : Число осколков при взрыве SplinterMaxNumber equ 40

; Радиус "разлета осколков" при взрыве ExpR equ 32

; Квадрат расстояния "подрыва" (от центра ракеты

; до цента цели)

TargetDistanceSQ equ 576 ;(расстояние подрыва 24)

; Максимально допустимое количество

; пропущенных самолетов MaxSavedPlanes equ 3

; Максимальное число эпизодов игры

MaxEpNumber equ 100

; МАКРОСЫ

; Вызов функции рисования движущегося объекта MACRO DrawMImage ObjS. ObjC. Objl_, ObjH,Obj, ObjA. ObjF : Передать паранетры подпрогранне рисования

mov AL.CObjF]

mov [ImageF].AL

mov EAX, [ObjS]

mov [ImageS],EAX

mov EAX,[ObjC]

mov [ImageC],EAX

mov [dword ptr ImageL],ObjL

mov [dword ptr ImageH],ObjH

mov [ImageMaskOffset], offset Obj

; Нарисовать объект call DrawMoviriglmage

; Запомнить адрес изображения в видеопамяти

mov EAX,[ImageA]

mov [ObjA],EAX

ENDM

; Вызов функции восстановления фона : (стирание изображения объекта) MACRO DeleteMImage ObjA.ObjL.ObjH.ObjF

mov EAX,[ObjA]

mov [ImageA],EAX

mov [dword ptr ImageL],ObjL

mov [dword ptr ImageH],ObjH

mov AL,[0bjF]

mov [ImageF],AL call Deletelmage

ENDM

*

; Вызов функции рисования неподвижного объекта MACRO DrawSImage 0bjS,0bjC,0bjL,0bjH.0bj

; Передать параметры подпрограмме рисования

mov ЕАХ.[ObjS]

mov [ImageS],ЕАХ

mov EAX.[ObjC]

mov [ImageC],EAX

mov [dword ptr ImageL].ObjL

mov [dword ptr ImageH],ObjH

mov [ImageMaskOffset], offset Obj

; Нарисовать объект

call DrawStaticImage

ENDM DATASEG

; ОСНОВНЫЕ ПЕРЕМЕННЫЕ СОСТОЯНИЯ ИГРЫ

; Счетчик игровых эпизодов

Листинг 4.16 (продолжение) EpisodeNumber DW ?

; Счетчик игрового времени (число видеокадров

; от начала игрового эпизода) GameTimeCounter DW ?

; Флаг изменения состояния игры GameStateGhange

DB ?

; Состояние самолета (0 – ожидание. 1 – полет.

; 2 • взрыв) PlnState

DB ?

; Состояние ракеты (0 – ожидание запуска. 1 – запуск,

; 2 • полет, 3 – взрыв, 4 – разлет осколков,

; 5  ракеты нет на экране)

RktState

DB ?

: Счетчик самолетов

PlnCounter DW ?

; Счетчик сбитых самолетов

DestroedPlns DW ?

; Счетчик пропущенных самолетов

EscPlns DW ?

; Счетчик потраченных ракет

RktCounter DW ?

; ИНФОРМАЦИЯ ОБ ОТОБРАЖАЕМЫХ ОБЪЕКТАХ

; Позиция маски самолета на экране

PlnS DD ? ;строка

PlnC DD ? ;колонка

PlnA DD ? ;текущий линейный адрес

PlnFl

DB ? ;флаг наличия объекта в текущей странице

; Признак попадания ракеты

HitFlag DW ?

: Скорость движения самолета PIaneSpeed DD ?

; Направление движения самолета

PlaneDirection

DB ?

; Задержка пуска самолета

PlaneDeltaT DW ?

; Позиция маски ракеты на экране

RktS DD ? ;строка

RktC DD ? ;колонка

RktA DD ? ;текущий линейный адрес

RktFl

DB ? ;флаг наличия объекта в текущей странице

RktStartFrameNumber DW ? :номер кадра старта ракеты

; Позиция маски пламени на экране

FlmS DD ? ;строка

FlmA DD ? ;текущий линейный адрес

FlmFl

DB ? ;флаг наличия объекта в текущей странице

; Параметры взрыва

ExpS DD ? ;строка

ExpC DD ? ;колонка

ЕхрА DD ? ;текущий линейный адрес

ExpFl

DB ? ;флаг наличия объекта в текущей странице

ExpFrameNumber DW ? ;номер кадра взрыва

; Параметры дерева

TreeS DD ? ;строка

TreeC DD ? :колонка

; Параметры облака

CloudS DD ? ;строка

CloudC DD ? ;колонка

; Позиция пусковой установки на экране

PRS DD ?

; строка

PRC DD ? -.колонка

; ТЕКСТОВЫЕ СООБЩЕНИЯ EndTxt

DB 0,32,"РЕЗУЛЬТАТЫ ИГРЫ".О

DB 12,0,"Сыграно эпизодов:",О

DB 14,0,"Выпущено ракет: ",0

DB 16,0,"Сбито самолетов:",0

DB 18,0,"Пропущено самолетов:",0

DB 29,29,"Нажмите любую клавишу",0

; ДИНАМИЧЕСКИЕ МАСКИ

PlnMask

DB 1024 DUP(?) ;текущая маска самолета ExpMask

DB 4096 DUP(?) ;динамическая маска взрыва ENDS

Листинг 4.17 включает в себя маски статических (дерево, облако, пусковая установка) и динамических (самолет, ракета, пламя из сопла ракеты) объектов, а также массивы координат статических объектов и массивы случайных параметров для динамических объектов. К сожалению, в процессе окончательного оформления листингов при подготовке книги к печати приходится придерживаться жестких ограничении на длину строки программы. Каждая строка данных в масках широких объектов (самолета и облака) оказалась поделенной на две части, в результате чего была утрачена наглядность, однако все остальные объекты просматриваются достаточно четко.

Листинг 4.17. Набор масок объектов, массивов координат

и случайных чисел для игры «Самолет и ракета»

DATASEG

; Маска самолета

PlnML equ 32

; ширина маски самолета PlnMH equ 32

; высота маски самолета Plane

DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0

DB 0,0.0,0,0,0.0,0,0,0,0,0,0,0,0,0

DB 0,0,0,0,0,0,0,0,0,0,0.0,0.0,0,0

DB 0.0,0.0,0.0,0,0,0,0,0,0,0,0,0.0

Листинг 4.17 (продолжение)

DB 0.0.0,0.0.0,0,0,0,0,0.0.0.0,0.0

DB 0.0,0,0.0.0,0,0,0,0,0,0,0.0.0.0

DB 0,0.0,0.0.0.0,0,0.0.0,0.0.0.0.0

DB 0,0.0.0,0.0.0.0,0,0.0.0.0,0.0.0

DB 0,0,0,0,0.0.0.0,0,0.0,0,0,0.0.0

DB 0.0,0,0,0,0.0.0.0.0.8.0,0,0,0,0

DB 0.0,0.0,0,0.0.0,0,0.0,0.0,0.0,0

DB 0,0,0,0.0.0,0.0.0.8,7,8,0,0,0,0

DB 0,0.0,0.0,0,0,0,0.0.0.0,0,0.0,0

DB 0.0.0,0.0,0,0.0,0,8.7.7,8.0,0,0

DB 0.0.0,0,0,0,0,0,0.0,0.0,0.0.0,0

DB 0.0.0,0.0.0.0,0,0.8,7,7.7,8.0,0

DB 0.0.0.0.0.0.0,0,0.0,0.0,0,0,0,0

DB 0,0,0.0,0,0,0,0,0.0.8,7,7,7.8.0

DB 0,0.0.0,0,0,0,0,0,0,0,0,0,0.0.0

DB 8,8,0.0.0,0.0.0,0,0.8.7,7,7,7.8

DB 0.0.0,0,0.0.0.0.0,0.0.0,0.0,0.0

DB 8.7.8.0.0,0.0.0.0.0.8.7.7.7.7,7

DB 8.0,0.0,0,0.0.0.0,0.0,0.0.0,0,0

DB 8.7.7.8.0.0,0,0.0,0,0,8.7.7.7,7

DB 7.8.0,0.0.0.0.0.0,0.0.0.0.0.0,0

DB 8,7,7,8,0,0,0,0.0,0,0,0,7.7,7,7

DB 7.7.8.0.0.8,8,8.0,0,0,0,0,0,0,0

DB 8.7.7.7.8.8,8,8,8,8,8,8,8.8.8.8

DB 8,8,8.8.8.7.7,7.8.8,8.0.0.0,0.0

DB 0,8,8.8,8.7.7,7,7,7.7.7,7.7,7,7

DB 7.7.7,7.8.8.8,8,8,7,7,8.8.8.0.0

DB 0.8,8,8,8.7.7.7,7,7.7,7,8,8,8,8

DB 8,8,8.7.7.7.7.7,7.7.7.7.7.7.8,8

DB 0,7.7.7,8,7.7.7,7.7.7,8,7,7,7,7

DB 7,7.7.8.7.7,7.7.7.7.7.8.8,8,0,0

DB 8,7,7,B,8,8,8.8,8.8.8.8,7,7.7.7

DB 7,7,8.8.8.8.8,8.8,8.8.0,0,0,0.0

DB 8.7.8.0,0.0.0,0,0,0.0.8.7.7,7,7

DB 7,8,0,0,0,0,0.0.0,0,0,0,0,0,0,0

DB 8,8,0.0,0.0.0,0.0.0.8.7.7.7.7,7

DB 8.0,0.0.0.0,0,0,0.0,0,0.0,0.0,0

DB 8,0.0,0.0,0.0.0.0.0.8.7.7.7,7,8

DB 0,0,0.0.0,0.0,0.0.0.0.0,0,0,0.0 D8 0.0.0,0,0,0,0,0,0.0.8,7.7,7.8,0

DB 0.0.0,0.0.0,0.0.0.0,0,0,0,0,0.0

DB 0,0,0,0.0.0,0.0.0.8.7.7.7.8.0,0

DB 0.0.0,0.0.0.0,0,0,0.0.0.0.0.0.0

DB 0,0,0,0,0.0,0,0,0.8,7,7,8,0.0,0

DB 0.0,0,0,0,0,0,0,0,0,0,0,0.0,0,0

DB 0,0,0,0,0,0,0,0.0,8,7,8.0,0,0.0

DB 0.0,0.0.0,0,0,0,0,0,0,0,0,0,0.0

DB 0,0.0,0.0,0.0,0,0,0.8,0,0,0,0.0

DB 0.0.0.0,0.0.0,0.0,0.0,0,0,0.0.0

DB 0.0.0,0.0,0,0.0.0.0,0.0,0,0,0,0

DB 0,0,0,0,0,0,0,0.0,0.0.0,0.0.0,0

DB 0,0,0,0.0,0.0.0.0,0,0.0.0,0.0,0

DB 0.0,0.0,0,0,0.0,0,0,0,0,0.0,0,0

DB 0.0.0.0.0.0.0,0.0,0.0,0.0,0.0,0

DB 0.0.0,0,0,0,0,0,0,0.0,0,0,0.0,0

DB 0.0,0.0,0,0,0.0,0.0,0.0,0.0.0И)

DB 0,0,0,0,0,0,0,0,0.0,0,0,0.0,0,0

DB 0,0,0,0,0.0,0,0.0,0.0.0.0.0.0.0

DB 0,0,0,0,0.0.0,0.0,0,0,0.0,0.0,0

; Маска ракеты

RktML equ 16

; ширина маски ракеты RktMH equ 32

; высота маски ракеты

Rkt

DB 0, 0, 0. 0. О, 0. 0. 7. 0. О, 0. 0. О, О,0. О

DB О, 0, 0. О, 0. 0. 0. 7. 0. О, 0, 0. О, О,О, О

DB 0, 0. 0. О, 0. 0. 7.15, 7. О, 0, 0. 0. О,0. О

DB 0, 0. 0. 0. 0. 0. 7,15, 7. 0. О, 0. 0. 0.0. О

DB 0. 0. 0. О, 0. 0. 7,15, 7. О, 0. 0. 0. О,0. О

DB 0. 0. О, 0. 0. 7.15,15.15, 7. 0. 0. 0. 0.О, О

DB 0. 0. О, 0, 0. 7,15,15,15, 7. О, 0. 0. О,0. О

DB О, 0, 0. 0. 0. 7,15,15,15, 7, 0. 0. 0. 0.0. О

DB О, О, 0, 0. 0. 2. 2, 2, 2. 2, 0, 0. 0. 0.0. О

DB 0, 0. О, 0. О, 6,10. 2, б, 2. 0. О, 0. 0.0. О

DB О, 0. О, 0, 0. 2, 2, 2, 2, 2, 0, 0, 0. 0.0. О

DB 0, 0. 0. 0. 2. 2. б. 2. 2. 2. 2. О, 0. 0.О, О

DB 0, 0. О, 2. 2. 2.10, 2.14. 2, 2. 2. 0. О,О, О

DB 0. О, 2, б. 2. 2. 2, 2, 2. 2,14, 2. 2, 0.О, О

DB 0. 2. 6.14. б, 2, б. 2, б. 2. 6,10, б. 2.0. О

DB 2. 2. 2. 2. 2, 2. 2. 2,10, 2. 2, 2. 2. 2.2, О

DB О, О, 0, 0. 0. 2, б. 2,14, 2, 0. 0. О, О,О, О

DB 0. 0. 0. О, 0. 2, 6,10. 6. 2. О, 0. 0. О,О, О

DB 0. 0. 0. 0. 0. 2.14. 6, 2. 2, 0. О, 0. О,0. О

DB 0, 0. 0. О, 0. 2, 2. 2, 2. 2, 0, 0. О, О,О, О

DB 0. О, 0. 0. 0. 2. 2. 2. 2. 2, 0. 0. О, 0.О, О

DB 0. 0. О, 0. 0. 2, 6, 2.10. 2. 0. 0. 0. О,0. О

DB 0. 0. 0. 0. 0. 2,10,14, 6. 2, 0, 0. 0. О,0. О

DB 0. 0. О, 0. 0. 2, 6. 2. 6, 2, 0. 0. 0. О,0. О

DB 0. 0. 0. 0. О, 2,14, 6. 2, 2, 0. 0. О, О,0. О

DB 0. 0. 0. О, 0. 2. 6, 2,14, 2. 0. О, 0. 0.0. О

DB О, 0. О, 0. О, 2,10, 6,10, 2. 0. 0. 0. 0.0. О

DB О, 0. О, 0, 0. 2,14, 2, 2. 2. 0. 0. 0. 0.0. О

DB О, О, 0. О, 2, 2,10, 2. 6. 2, 2, 0. О, 0.О, О

DB О, 0, 0. 2. 6. 2. 2. 2,14, 2,14, 2, 0, 0.0. О

DB 0, 0. 2,14, 2, 2, 6. 2.10. 2. 6. 2. 2, О,О, О

DB 0, 2, 2. 2, 2. 2. 2. 2. 2. 2, 2. 2, 2, 2.2. О

; Маска ппамени из сопла ракеты FlmML equ 16

; ширина маски пламени FlmMH equ 16

; высота маски пламени

Листинг 4.17(продолжение)

Flm DB0. 0, О, 0,0,12.15,15,15,12,О,О, О, О,О,О

DB0. О, О, 0,0,12,15,15,15,12,0,0, 0. 0.0.О

DBО, О, 0,0.0,12,14,15,14,12,0.0. О, О,О,О

DB0. О, 0,0.0,12,14,14,14,12,0.О, О, О,О,О

DBО, 0, 0, 0.0.12,14,14,14,12,О,0, 0. 0.О,О

DBО, О, 0, 0.0,12,14,14,14,12,0,0. О, О,О,О

DBО, О, О,О,0, 4,12,14,12, 4,О,0, 0.0.О,О

DBО, О, О,О,0, 4,12,14,12, 4,0,0. О, О,0.О

DB0. О, О,0,0. 4,12,14,12, 4,О,О, 0. О,0.О

DBО, О, О, О,0, 4,12,14,12, 4,0,0. 0. О,О,О

DBО, О, 0.О,0. О, 4,12, 4, О,О,О, 0.О,О,О

DBО, О, 0.О,О, 0, 4,12, 4, О,О,О, О, 0.О,О

DB0. О, О,О,0. О, 4,12, 4. О,О,О, 0. О,О,О

DBО, 0. О,О,О, О, 0, 4, О, О,0.О, О, О,О,О

DB0. 0. 0. О,О, О, 0, 4, О, О,О,О, О, О,О,О

DBО, 0. О,0.О, 0. 0. 4. О, О,О,О, О,О,О,О

; Маскадерева

TreeML equ 16; ширина иаски дерева

TreeMH equ 32; высота иаски дерева

Tree DBО, 0,0. 0. 0. О, 0, б, 0. 0. 0. О,0.О,О,О

DB0, 0,0, 0, 0. О, 0, б, 0, 0. 0. 0.0.0.О,о

DB0. О,0, 0. 0, 0, 0, 6, 0, 0, 0, о,0.0.о,о

DB0. О,0. О, 0, 0,10.10,10. 0. 0. О,0.0.о,о

DB0. О,0, 0. 0. О, 0, б, 0. о, о, о,0.о,о,о

DB0, 0,0. 0. 0. О, 0. б, о, о, о, о,о,0.о,о

DBо, О,О, 0, 0,10,10,10,10,10, о, о,о,0.о,о

DB0. О,0, 0, 0. О, 0, б. о, 0. О, 0.о,о,о,о

DB0. О,о, 0, 0, 0, 0, б, 0. 0. 0. о,о,о,о,о

DBО, 0,0. О, 0, 0,10,10,10, о, о, о,о,о,о,о

DBО, О,0, 0,10,10,10,10,10.10.10, 0,0.0.0.о

DB0, 0,0, 0, 0, 0, б, б, б. О, 0. 0.0.0.о,о

DBО, 0,0, 0, 0, 0, б, б, б, о, о, о,0.о,о,о

DB0. 0.0, 0, 0,10,10,10,10,10, о, 0.о,о,о,о

DBО, 0,0,10,10,10,10,10,10,10,10,10,о,о,о,о

DBо, 0,0, 0, 0, 0, б. б, б, 0, 0, о,0.0.о,о

DB0, 0.0. 0. 0. 0, б, б, б. 0. 0. о,о,о,о,о

DBо, 0.О, 0. 0. О, б. б, б, о, 0. о,о,о,о,о

DB0. О,о, 0. 0,10,10,10,10,10. о, о,о,о,о,о

DB0, 0.0,10,10,10,10.10.10,10,10.10,о,0.о,о

DB0,10.10.10,10.10,10,10,10,10,10.10,10,10,о,о

DB0. 0.0. 0. 0. 0. 6. б, б, 0, 0. 0.о,0.о,о

DB0, 0.0. о, 0, 0, б, б, б. 0. 0. О,0.О,0.о

DBО, О,О, О, 0. 0. б. б, б, 0. о, о,о,о,о,о

DB0, о,0. 0,10,10.10.10,10,10.10, 0,0.0.о,о

DB0. 0.10,10,10.10,10,10,10,10.10,10,10,о,о,о

DB10,10,10,10,10,10,10.10,10,10,10,10.10.10,10,о

DB0, 0,0, 0, 0. О, б, б, б, 0, 0, 0.0.о,о,о

DBо, 0.О, 0, 0, 0. 6, б. б, о, 0, 0.0.о,о,о

DB О, О, О. О, О, О, б, б. б, О, О, О, о, о, о, о

DB О, О, О, О, О, О, б, б, б, О, О, О, О. О. о, о

DB О, О. О, О, О, О, 6. б. б, о, о, о, о, о, о, о

; Маска пусковой установки

PRML equ 16

; ширинамаски пусковой установки

PRMH equ 32

; высотамаски пусковой установки Platf

DB 16,16,16,16,16,16.16,22,16,16,16,16,16,16,16, О

DB 7, 7, 7,2,2, 2,16,22,16, 2, 2,2, 7, 7, 7, О

DB 16.16,16,2.2. 2,16,22,16, 2, 2.2,16,16,16, О

DB 7. 7, 7,2,2. 2,16,22,16, 2, 2.2. 7, 7, 7, О

DB 16.16,16,2,2, 2,16,22,16, 2. 2,2,16,16,16, О

DB 7, 7. 7, 2,2, 2.16.22,16, 2. 2,2, 7. 7, 7. О

DB 16,16,16. 2,2, 2,16,22,16, 2, 2,2,16,16,16. О

DB 7, 7, 7, 2,2, 2,16,22,16, 2. 2,2, 7, 7. 7. О

DB 16,16,16, 2,2. 2,16,22,16, 2. 2,2,16,16,16, О

DB 7, 7, 7,2,2. 2,16,22,16, 2, 2,2. 7. 7. 7. О

DB 16.16.16,2,2. 2,16,22,16, 2, 2.2,16,16,16, О

DB 7. 7, 7.2,2, 2,16,22,16, 2, 2.2. 7. 7, 7, О

DB 16.16.16,2,2, 2.16,22,16, 2, 2,2,16.16.16. О

DB 7, 7. 7,2,2, 2,16,22,16. 2, 2,2. 7, 7, 7. О

DB 16,16.16,2.2. 2,16,22,16, 2, 2,2,16,16,16, О

DB 7, 7, 7, 2,2, 2,16,22,16, 2. 2,2. 7, 7, 7. О

DB 16,16.16, 2,2, 2,16,22,16, 2, 2,2,16,16.16, О

DB 7, 7, 7.2,2, 2,16,22,16, 2. 2,2. 7. 7. 7. О

DB 16,16,16,2.2, 2.16,22,16, 2. 2,2,16,16,16, О

DB 7, 7. 7,2,2. 2,16,22,16, 2. 2.2. 7. 7. 7. О

DB 16.16.16,2.2, 2,16,22,16, 2. 2.2,16,16,16. О

DB 7. 7. 7,2.2. 2,16,22,16, 2, 2,2. 7. 7, 7. О

DB 16,16,16, 2,2. 2,16,22,16, 2, 2,2,16,16,16. О

DB 7, 7. 7, 2,2, 2,16,22,16, 2, 2,2, 7, 7. 7. О

DB 16,16,16, 2,2, 2,16,22,16, 2, 2,2,16,16,16, О

DB 7, 7, 7. 2,2. 2,16,22,16, 2. 2,2, 7. 7, 7, О

DB 16,16.16.2,2. 2,16.22,16, 2, 2,2,16,16,16, О

DB 7. 7, 7,2,2, 2,16,22,16, 2. 2,2. 7, 7. 7, О

DB 16,16,16,2,2, 2,16,22,16, 2, 2.2,16,16,16, О

DB 7. 7. 7, 2,2. 2.16,22,16, 2, 2.2. 7, 7, 7, О

DB 16,16,16, 2.2, 2,16,22,16. 2, 2,2.16,16.16. О

DB 7, 7, 7,16,16,16,16,16,16,16,16.16, 7, 7. 7, О

; Маска облака

CloML equ 32

; ширина маски самолета CloMH equ 16 : высота маски самолета Cloud

DB 0. 0. О, 0. 0. 0,2В, 0, 0. О, 0. 0,31, 0,31,30

DB 31,31. О, 0, 0,25. О, 0. 0. О, 0, 0. О, 0. О, О

DB О, О, О, О, О, О, О, 0. О, 0, 0.30, 0,30,30, О

DB 30,30,30,30, 0, 0, 0, 0, 0. О, 0. О, 0, 0, 0. О

DB О, 0, 0.27.32, 0, 0.29,30. 0.2В,29,30,29, 0,29

DB 0,29,27, 0.30, 0,27, 0, 0, 0,25. О, 0, 0. О, О

DB 0.24, 0, 0. 0. 0,30. 0.29, 0,31, 0,27,31,29.30

Листинг 4.17 (продолжение)

DB 31,26. 0.29. 0,28. 0,31. 0.30, 0. 0,29,24. 0. 0

DB 0. 0.30. 0,27. 0,31.30, 0,29,30,27. 0,29, 0,28

DB 0,31,27,29,28,30,29.27.26,29. 0.28, 0. 0. 0, 0

DB 0,23, 0.30, 0.26.31.28,29,27.29, 0,28. 0.29,30

DB 29.29. 0.27.29, 0,25.29,28,28,26,27.29. 0,25, 0

DB 0. 0. 0,24,2В,29. 0.24.27, 0.26.29,25.24.27, 0

DB 29.23,30. 0,26,27.24, 0,28, 0,25,30, 0,24, 0. 0

DB 0,25,29.24, 0,30.26,24,28,27,25. 0.26, 0,26,24

DB 28,24,25,27.28. 0.25,23.26.27.23,27,24. 0.25,22

DB 24,23, 0,25.22.27,24, 0.25.23.22,28.24,26,30.23

DB 25, 0.24.22, 0,25,23,22, 0,25,25. 0.26.24,27, 0

DB 25, 0,31,23,26, 0,29.24.23. 0.26.24,27.26.23. 0

DB 22,25.26, 0.29.26.24, 0,26, 0,24,26, 0,25, 0. 0

DB 0.25.20. 0,23,25,22,25, 0,23, 0,30, 0.24.25.27

DB 24. 0,27.19,28.26, 0,20.24,22.21.25.23.25.22, 0

DB 22. 0.23.24.20. 0.24.23.20.21,22.24.25.23. 0.24

DB 21.22.19. 0,24,23,24,22,25. 0.21, 0.23.20, 0, 0

DB 0.19,20,24. 0,20,23, 0,18,19,17, 0.21.20,19,20

DB 18.21.22.20.23. 0.20.23,22.20.23,22.18, 0, 0,19

DB 0, 0,18. 0.20, 0.17.21,20, 0,19,17,16.19, 0,18

DB 0,24.18,19, 0.17,20,22.18,17. 0,20, 0,23. 0, 0

DB 0. 0. 0. 0,20,23. 0.24,20,18,21,23. 0,20.19. 0

DB 22,21, 0,22,18.19,24, 0.20, 0,19,22,20. 0. 0. 0

DB 0. 0, 0,18, 0,17.20.19.20.19.21. 0,20,17.19.21

DB 20,18,17.20.20.19.20.17,20. 0.19.22, 0, 0, 0. 0

DB 0,22. 0, 0,19, 0. 0.16,17. 0.21,23,25. 0.19, 0

DB 0,16, 0,17,18, 0, 0, 0,20, 0,19, 0. 0,18, 0, 0

; Массив координат облаков: первое значение в паре -

; номер строки (Y), второе – номер колонки (X) CloudPos DD 100,200, 200,300. 200.310. 200,320. 160,500 DD 160,515, 165,505, 300,110, 295,105, 293.115

; Массив пар приращений координат для ракеты и пламени

; из ее сопла: первый элемент пары – приращение

; координаты ракеты, второй – смещение маски

; пламени относительно начала маски ракеты

; (изменяется от 16 до 32). RktStartStep DD 0.16, 0,17, 0.18, 0.19 DD 0,20, 0,21. 0,22. 0,23 DD 0,24. 0.25, 0,26. 0.27 DD 1,28. 1.29, 2,30. 2.31 DD 3.32. 3.32, 4.32, 4.32 DD 5,32, 5.32, 6,32. 6.32 DD 7,32. 7,32, 8.32, 8,32 DD 9.32, 10,32, 11,32, 12,32 DD 13.32. 14,32. 15.32. 16,32 DD 17.32. 18.32. 19.32. 20.32 DD 21,32. 22.32. 23.32, 24,32

DD 26,32, 2В.32. 30,32, 32,32 DD 34,32, 36,32, 3B.32, 40,32 DD 42.32, 44,32, 46,32, 48,32

; Массив шагов (смещений) осколков из

; 2*SplinterMaxNumber элементов: первый элемент

; пары – шаг по Y. второй – шаг по X SplStep

DB 0.1. 1.0. 0.-1. -1,0, 1,1, 1,-1, -1.1. -1,-1

DB 0.2. 2.0, 0,-2, -2,0, 2.2. 2,-2, -2.2. -2,-2

DB 1.2. 1.-2. -1,2, -1,-2, 2,1, 2,-1, -2,1,-2.-1

DB 1.3. 1,-3, -1.3. -1,-3, 3,1, 3,-1, -3,1,-3,-1

DB 2,3, 2.-3. -2.3. -2.-3. 3.2, 3.-2, -3.2,-3,-2

; Массив цветов осколков

ExpColors

DB WHITE,WHITE.YELLOW.YELLOW,LIGHTRED

DB LIGHTRED.RED,RED.DARKGREY,DARKGREY

; МАССИВЫ СЛУЧАЙНЫХ ЧИСЕЛ : Массив смещений самолета по высоте Deltas DD 182,3,52,230,162.6.142,227,122,1В9 DD 72,91,128,214,209,76,45,169,31,3 DD 125,153,139,168.27,220,66.176,174.233 DD 205,B3,137,66,1B4,51,134,192,Bl,24 DD 71,1B5.173,164,69,B,90,233,16,153 DD 25,69,170,233,79.73,110,15,147,21 DD 225,81,13.145.161,125,166,167,B0.220 DD 105,228,129,1B1.162,11B,127,207.30,16 DD 144,B4,9.125,146,135,B0.1,90,229 DD 1B9,235,97,47.193,107.162,30,234.B2

; Массив начальных задержек запуска самолета DeltaT DW 148.ВЗ,65,149,54,IB,131,2,55,166 DW B7,97.12В,51,117,IBB,96.163.61,177 DW 115,197,7В,177,56,73,152,144,99,4В DW 5,35,163,97,96.117,79,70,55,192 DW 143,11,42,160.141,135,192,B3.9B.63 DW 4,1B3,41,1B9,63.118,9.2,146,1B1 DW 26.31,91,174,5В,23.94,1B2.6B,34 DW 199,14,1B9,44.63,131,135.172,150.2B DW 76,102,25,94,76.191,127,104,134,B2 DW B2,1B9.136.88,121,IB,139,162,B2,43

; Массив направлений полета (0 – слева направо,

; 1 – справа налево)

Direction

DB 0,1.1.1,1,0.0.0,1.1.0,0,0,1,1,0.1.0,1,0

DB 0.0,1,1,0.1,1.0.1.0.1.1,0,0.0.1.1.1.0.0

DB 0,0,0,1,0,1.0,1.1.1.0,1,1,0,1,0,0,1,1,0

DB 1,1,1.0.1,1,0,1,0,0,1,0.1.1,1,1.0,0,1.0

DB 1.1.1.1,1,0,0,1,1,1,1.0.0.1.1,1.0.0.0,1 : Массив скоростей полета самолета PlnSpeed DD 2,1,2.1.1.1,2.1.2,1,2.2.1.2,2,2.2.2.1.2 DD 2,1,1,2,2,1.2,2.2,1.1,2,1.2.1.2.2.2.2,1

Листинг 4.17 (продолжение)

DD 2.1.1,2.1,2.1,1,2.2,2.2.2.1,1.1.1.1,2,2 DD 1,2.2.2,1,1.1,2.2,1.1,1.1.2,2,1.1.2,2.2 DD 2,1,1.1.1,1,1,2.1.2.2.2,1,2,2.1,1.2,1,2

ENDS

ПРИМЕЧАНИЕ

При создании масок я не использовал никаких специальных редакторов, а непосредственно набирал числовые коды цветов из стандартной палитры VGA. Первые 16 кодов стандартной палитры соответствуют кодам 16-цвет- ных режимов (см. листинг 1.5), а следующие 16 кодов — оттенкам серого цвета (от черного до белого). Используя анимационные пакеты или самодельные редакторы изображений, можно создавать гораздо более красивые маски, если задействовать все 256 доступных оттенков и перепрограммировать регистры ЦАП при помощи функции 10h прерывания Int 10h (подфункция 12h).

Листинг 4.18 содержит группу подпрограмм, специфических для од- ностраничного режима (для режима с переключением страниц в них нужно вносить дополнения). Процедура DrawMovinglmage обеспечивает вывод динамических (движущихся) изображений в видеопамять, a Deletelmage — их стирание путем восстановления фона. Для рисования статических объектов фона используется подпрограмма DrawStaticImage. Процедура ShowBackground полностью копирует фон в видеопамять в начале каждого нового эпизода. Процедура Show- EscapedPl anes служит для вывода в левый верхний угол экрана количества пропущенных самолетов.

Листинг 4.18. Набор подпрограмм для вывода фона

и динамических объектов в одностраничном режиме

; Номер начальной строки видеостраницы VPageOStartString equ О : Начальный адрес видеостраницы VPageOAddress equ О

DATASEG

; ПАРАМЕТРЫ ПОДПРОГРАММЫ РИСОВАНИЯ СПРАЙТА

; Указатель на маску объекта

ImageMaskOffset DW ?

; Размеры маски изображения

ImageL DD ? -.ширина иаски

ImageH DD ? ;высота маски

; Позиция маски изображения на экране

ImageS DD ? ;строка

ImageC DD ? ;колонка

ImageA DD ? ;линейный адрес

ImageF

DB ? ;флаг присутствия объекта на экране ENDS

CDDESEG

;* ПЕРЕПИСАТЬ ФОН ИЗ ОПЕРАТИВНОЙ ПАМЯТИ В ВИДЕОПАМЯТЬ * ;*(процедура параметров не имеет)*

PROC ShowBackground near pushad eld

mov EDI ,0

; Загрузить в счетчик размер области фона

mov ЕСХ.Logi calStri ngLength*ScreenHei gth : Скопировать фон m:

mov AL.[GS:EDI]

mov [FS:EDI],AL

inc EDI dec ECX t jnz P@N popad ret

ENDP ShowBackground

;*HAPHCDBATb ДИНАМИЧЕСКИЙ ОБЪЕКТ*

;* Для передачи параметров используются глобальные * ;* переменные ImageMaskOffset. ImageL. ImageH. * ;* ImageS, ImageC, ImageA, ImageF.*

• ‘Яг/г ic/ck "kit "fcmc ‘WnHrtWrW’k^/rWnfc jcicicjcic W W^WrA^W^^HrHr^^fiHriHt

PROC DrawMovinglmage near pushad

cmp [ImageF],0

je

@@End eld

; Вычислить адрес начальной точки для вывода маски

; Умножить длину строки на номер строки (Y)

mov EAX.LogicalStringLength

mov EDX,[ImageS] mul EDX

: Прибавить номер колонки (X)

add EAX,[ImageC]

mov [ImageA],EAX ;запомнить смещение

mov EDI,EAX ;результат – в индексный регистр

; Записать адрес маски в индексный регистр

mov SI,[ImageMaskDffset]

; Вывести изображение

mov DX.[word ptr ImageH] ;высота маски

@@М0:

; Вывести очередную строку маски

mov CX,[word ptr ImageL] ;ширина маски

Листинг 4.18(продолжение)

@@М1: : Проверить точку маски lodsb

and AL.AL;код цвета равен нулю?

jz

@@М2пропустить точку

mov [FS:EDI].AL ;вывести точку

@@М2:

; Перейти к следующей точке

inc EDI loop

@@М1

: Перейти на следующую строку

add EDI.LogicalStringLength sub EDI,[ImageL] dec DX jnz

@@M0

@@End: popad ret

ENDP DrawMovinglmage

*НАРИСОВАТЬ ЭЛЕМЕНТ ФОНА*

;* Для передачи параметров используются глобальные * ;* переменные ImageMaskOffset. ImageL, ImageH. * ;* ImageS, ImageC.*

• Жк’ккк i: "kick’it’tc’ic’icic’k k’fc’k’k’k’k ‘kkkicietc ‘kk’k к’к’к’к’кк’к’кк’к’к к А А ‘k’kicfck’k’k

PROC DrawStaticImage near pushad eld

; Загрузить начальное смещение

mov EDI.VPageOAddress

; Вычислить адрес начальной точки для вывода маски

; Умножить длину строки на номер строки (Y)

mov ЕАХ,LogicalStringLength

mov EDX,[ImageS] mul EDX

: Прибавить номер колонки (X)

add EAX,[ImageC]

; Прибавить результат к начальному смещению

add EDI,ЕАХ

; Записать адрес маски в индексный регистр

mov SI,[ImageMaskDffset]

; Вывести изображение

mov DX,[word ptr ImageH] ;высота маски

@@M0:

; Вывести очередную строку маски

mov CX,[word ptr ImageL] :ширина маски

@@М1: : Проверить точку маски lodsb

and AL.AL;код цвета равен нулю?

jz

@@М2пропустить точку

mov [GS:EDI],AL :вывести точку

@@М2: : Перейти к следующей точке

inc EDI loop

@@M1

; Перейти на следующую строку

add EDI.LogicalStringLength

sub EDI,[ImageL]

dec DX

jnz mo

popad

ret

ENDP DrawStaticImage

•* СТЕРЕТЬ ИЗОБРАЖЕНИЕ (ВОССТАНОВИТЬ ФОН) * ;* Для передачи параметров используются глобальные * ;* переменные ImageL, ImageH, ImageA, ImageF. *

PROC Deletelmage near pushad

cmp [ImageF],0

je

@@End

; Записать в индексные регистры адрес изображения

mov EDI.[ImageA]

; Вывести исходное изображение

mov DX,[word ptr ImageH] :высота маски

@@М0:

mov CX.[word ptr ImageL] ;ширина маски

@@M1:

mov AL, [GS:EDI]

mov [FS:EDI],AL

inc EDI loop

@@M1

add EDI.LogicalStringLength sub EDI,[ImageL] dec DX jnz

@@M0

@@End: popad ret

ENDP Deletelmage

;* ОТОБРАЗИТЬ ЧИСЛО ПРОПУЩЕННЫХ САМОЛЕТОВ * ;* (в левом верхнем углу экрана) *

PRX ShowEscapedPlanes NEAR pushad

mov SI,offset Font8xl6

mov AX,[EscPlns]

add AX,’0′ shl AX.4

add SI. AX

mov EDI.4*LogicalStringLength+8

; Определить цвет цифры

Листинг 4.18 (продолжение)

mov BL.LIGHTGREEN cmp [EscPlns],0

je

@@Bkgr

mov BL,YELLOW cmp [EscPlns],l

je

@@Bkgr

mov BL.LIGHTRED

@@Bkgr:

mov DL.BLUE

; Вывести цифру

mov AH,16 ;счетчик строк каски буквы

@@М0: lodsb

mov СХ.8 ;счетчик точек в строке маски

@@М1: rol AL.1 jc 00М2

mov [FS:EDI],DL

mov [FS:EDI+Logi calStri ngLength].DL

inc EDI

mov [FS:EDI],DL

mov [FS:EDI+LogicalStringLength],DL jmp

@@M3 ia@M2:

mov [FS:EDI],BL

mov [FS:EDI+Logi calStri ngLength],BL

inc EDI

mov [FS:EDI],BLi

mov [FS:EDI+Log1calStri ngLength],BL

@@M3:

inc EDI loop О0М1

add EDI,2*LogicalStri ngLength-16

dec AH

jnz @0MO

popad

ret

ENDP ShowEscapedPlanes ENDS

В листинге 4.19 собраны универсальные подпрограммы, которые могут использоваться как в одностраничном режиме, так и в режиме переключения страниц:

•   процедура InitEpisode инициализирует переменные состояния динамических объектов в начале каждого эпизода — это просто линейный участок кода, вырезанный из основного модуля и оформленный в виде подпрограммы для большей наглядности;

•   процедуры GShowDecByte, GShowDecWord и GShowDecDWora служат для вывода на экран в графическом режиме байта, слова и двойного слова данных в десятичном коде;

•    подпрограмма ShowGameResults после завершения игры выводит на экран ее результаты, используя процедуры вывода десятичных чисел;

•    процедуры CopyPl aneMask и Mi rrorPl aneMask предназначены для создания копии маски самолета, но CopyPl aneMask просто дублирует маску, a Mi rrorPl aneMask отражает ее слева направо (в начале каждого эпизода вызывается только одна из этих процедур — в зависимости от направления движения самолета; полученная таким образом копия маски используется в дальнейшем при выводе изображения самолета в видеопамять);

•    процедура DrawMainBackground создает в дополнительной памяти компьютера фон изображения: верхняя область (небо) закрашивается в синий цвет, нижняя (земля) — в черный и зеленый (в клеточку); после этого поверх земли рисуются деревья в четыре ряда и пусковая установка в центре, а на небо наносятся облака (путем многократного наложения одной и той же маски);

•    подпрограмма ExpMaskCl ear предназначена для очистки (обнуления) маски взрыва, а процедура ExplosionFrame — для генерации очередных кадров изображения взрыва (маска взрыва генерируется динамически путем наложения друг на друга цветных точек — осколков, разлетающихся в разные стороны с разными скоростями).

Листинг 4.19. Универсальные подпрограммы, пригодные

для одностраничного и многостраничного режимов

C0DESEG

;* ВЫВОД БАЙТА НА ЭКРАН В ДЕСЯТИЧНОМ КОДЕ * ;* Подпрогранна выводит содержимое регистра AL * ;* в десятичном коде в указанную позицию экрана. * ;* Координаты позиции передаются через глобальные * ;* переменные ScreenString и ScreenColumn.*

PROC GShowDecByte NEAR push EAX and EAX.OFFh cal1 GShowDecDWord pop EAX ret

ENDP GShowDecByte

;*ВЫВОД 16-РАЗРЯДНОГО СЛОВА НА ЭКРАН*

;*В ДЕСЯТИЧНОМ КОДЕ*

Листинг 4.19 (продолжение)

;* Параметры:*

;* АХ  число, которое будет выведено на экран. * ;* Номер строки передается через глобальную*

;* переменную ScreenString, номер столбца – через * ;* переменную ScreenColumn, цвет текста определяется * ;* глобальной переменной TextColorAndBackground. *

PROC GShowDecWord NEAR push EAX and EAX,OFFFFh call GShowDecDWord pop EAX ret

ENDP GShowDecWord

;*ВЫВОД 32-РАЗРЯДНОГО СЛОВА НА ЭКРАН*

;*В ДЕСЯТИЧНОМ КОДЕ*

;* Параметры:*

;* ЕАХ – число, которое будет выведено на экран. * ;* Номер строки передается через глобальную*

;* переменную ScreenString, номер столбца – через * ;* переменную ScreenColumn, цвет текста определяется * ;* глобальной переменной TextColorAndBackground. *

PROC GShowDecDWord NEAR pushad push DS

; Настроить регистр DS на глобальный сегмент данных

mov SI,[CS:MainDataSeg]

mov DS.SI

; Перевести число в десятичный код

mov [Data_Int32],EAX call Int32_to_String

mov DH,[byte ptr ScreenString]

mov DL,[byte ptr ScreenColumn] : Вывести число на экран

mov СХ.10

mov SI.offset Data_String PONextChar:

lodsbзагрузить цифру в AL

and AL.AL проверка на 0 (конец строки) jz

@@EndOfString call PutGraChar loop PONextChar

@@EndDfString:

pop DS

popad

ret

ENDP GShowDecDWord

;* УСТАНОВИТЬ НАЧАЛЬНОЕ СОСТОЯНИЕ ПЕРЕМЕННЫХ ЭПИЗОДА * ;* (вспомогательная процедура, регистры не сохраняет) *

PROC InitEpisode near

; Перерисовать фон

cal1 ShowBackground

; Сбросить начальные адреса изображений объектов

mov [Р1пА],0

mov [RktA],О

mov [FlmA].0

mov [ExpA].0

; Сбросить признаки наличия объектов

mov [PlnFl],0

mov [RktFl],0

mov [FlmFl].0

mov [ExpFl],0

; Сбросить счетчик времени

mov [GameTimeCounter].0 : Сбросить признак попадания ракеты в самолет

mov [HitFlag],0

; Установить начальное состояние самолета

mov [PlnStateLO

; Прибавить случайное смещение по высоте

mov BX.[Epi sodeNumber] shl BX.2

add BX.offset DeltaS

mov EAX.[BX]

add EAX.BO

mov [PlnS],EAX

; Запомнить направление движения самолета

mov BX.[Epi sodeNumber]

add BX.offset Direction

mov AL,[BX]

mov [PlaneDirection],AL

; Вычислить скорость самолета

mov BX,[EpisodeNumber]

shl BX.2

add BX,offset PlnSpeed

mov EAX,[BX]

mov [PlaneSpeed],EAX : Задержка пуска самолета

mov BX,[EpisodeNumber] shl BX.2

add BX,offset DeltaT

mov AX,[BX]

mov [PlaneDeltaT],AX

; Скопировать маску самолета

Листинг 4.19 (продолжение)

call CopyPlaneMask

; Сместить самолет за левую границу экрана

mov [PlnC],-PlnML

; Направление движения? cmp [Plane0irection],0

je

@@dirO

; Сместить самолет за правую границу экрана

mov [PIпС].ScreenLength

; "Отрицательная" скорость neg [PlaneSpeed] : Использовать отраженную маску call MirrorPlaneMask

; Установить начальное состояние ракеты

@@dirO:

mov [RktState],0 состояние О

; Отобразить ракету на стартовой позиции

mov [RktFl].l ;отобразить ракету

mov EAX,[PRS] ;координаты стартовой позиции

mov [RktS],EAX

mov ЕАХ,[PRC]

mov [RktC],EAX

ret

ENDP InitEpisode

;* ВЫВСТИ РЕЗУЛЬТАТЫ ИГРЫ *

PROC ShowGameResults near

pushad

; Очистить экрана

callGClearScreen

; Отобразить результаты

movSI,offset EndTxt

mov[Defaulted or], LIGHTCYAN

callGShowString

mov[Defaulted or], LIGHTGREEN

callGShowString

callGShowString

callGShowString

callGShowString

mov[DefaultColor].YELLOW

callGShowString

mov[Defaulted or], WHITE

mov[ScreenString],12

mov[ScreenColumn],18

movAX.[E pi sodeNumber]

callGShowDecWord

mov[ScreenString],14

mov[ScreenColumn],16

movAX,[RktCounter]

callGShowDecWord

mov[ScreenString],16

mov[ScreenColumn],17

movAX,[DestroedPlns]

callGShowDecWord

mov[ScreenStri ng],18

mov[ScreenColumn],21

movAX,[EscPlns]

callGShowDecWord

; Ожидать нажатия любой клавиши

callGetChar popad ret

ENDP ShowGauieResults

;* СОЗДАТЬ ОСНОВНОЙ ФОН *

PROC DrawMainBackground near pushad

; Загрузить начальное смещение (адрес видеостраницы)

mov EDI.VPageOAddress

; Умножить высоту "неба" (высота экрана минус 64 строки) : на логическую ширину строки (1024 пиксела)

mov ЕСХ,ScreenHeigth-64 shl ЕСХ,10

; Покрасить небо в синий цвет

mov AL.BLUE

@@NextPixell:

mov [GS: EDI] .Aline EDI dec ECX jnz PONextPixell : Покрасить землю в черный и зеленый цвета

mov AL.BLACK

mov AH.GREEN : Высота "земли" 64 строки

mov DX.64 PONextStr:

mov CX,LogicalStringLength/2

@@NextPixel2:

mov [GS:EDI],AX

add EDI,2 loop

@@NextPixel2 xchg AL.AH dec DX jnz PONextStr

; НАРИСОВАТЬ ЛЕС

; Начальная строка "леса"

mov [dword ptr TreeS],ScreenHeigth-80

Листинг 4.19 (продолжение)

; Число рядов деревьев

mov DX.4

; Цикл рисования леса

@@Trees:; Нарисовать очередной ряд деревьев

mov [dword ptr TreeCJ.O test DX.Olb jz

@@EvenLine : Сместить нечетные ряды

add [dword ptr TreeC],16

@@EvenLine:

mov CX,ScreenLength/(TreeML*2)

@@NextTree:

cmp CX.ScreenLength/(TreeML*4)

je

@@NoTree :"просека" в лесу cmp CX,ScreenLength/(TreeML*4)+l

je

@@NoTree ;"просека" в лесу : Нарисовать очередное дерево DrawSImage TreeS,TreeC,TreeML.TreeMH.Tree

@@NoTree:

add [dword ptr TreeC],TreeML*2

loop @PNextTree

add [dword ptr TreeS],TreeMH/2

dec DX

jnz

@@Trees

: НАРИСОВАТЬ ОБЛАКА

mov ВХ,offset CloudPos

mov CX.MaxCloudNum ;счетчик облаков

@@NextCloud:

; Нарисовать очередное облако

mov ЕАХ,[ВХ]

mov [CloudS],ЕАХ

add ВХ,4

mov ЕАХ,[ВХ]

mov [CloudC],EAX

add BX,4 DrawSImage CIoudS.CIoudC,CIoML.CIoMH,CIoud loop PONextCloud

: НАРИСОВАТЬ ПУСКОВУЮ УСТАНОВКУ

mov [PRS],ScreenHei gth-PRMH-16

mov [PRC],(ScreenLength•PRML)/2 DrawSImage PRS.PRC,PRML,PRMH,PIatf popad ret

ENDP DrawMainBackground ;* СКОПИРОВАТЬ МАСКУ САМОЛЕТА *

• к it kit "к к к kit к ick A it A ‘A’ A it к к к ‘kkkit к к kh "к

PROC CopyPlaneMask near pusha

push ES

mov AX,[CS:MainDataSeg]

mov ES.AX

mov SI,offset Plane

mov DI,offset PlnMask

: Скопировать весь нассив

mov CX,PlnMH*PlnML

rep movsb

pop . ES

popa

ret

ENDP CopyPlaneMask

;* ПОЛУЧИТЬ ОТРАЖЕНИЕ МАСКИ САМОЛЕТА *

. *********А А А Л А Л ‘А А************* Л А-А А Л А А

PROC MirrorPIaneMask near pusha

mov SI.offset Plane

mov DI,offset PlnMask

mov DX.PInMH

; Цикл по строкам

@@L0:

mov CX.PInML

add DI,PlnML-l

; Цикл по пикселам

@@L1: lodsb

mov [DI],AL dec DI loop

@@L1

add DI,PlnML+l dec DX jnz P@L0 popa ret

ENDP MirrorPIaneMask

;* СТЕРЕТЬ МАСКУ ВЗРЫВА *

;************************

PROC ExpMaskClear near pusha

push ES

mov AX,[CS:MainDataSeg]

mov ES.AX

mov DI.offset ExpMask

mov CX,4*ExpR*ExpR

mov AL,0 rep stosb

Листинг 4.19 (продолжение) pop ES рора ret

ENDP ExpHaskClear

;* НАРИСОВАТЬ МАСКУ ОЧЕРЕДНОЙ ФАЗЫ ВЗРЫВА *

PROC ExplosionFrame near pusha eld

; Вывести изображение взрыва

: Записать адрес массива смещений осколков

mov SI,offset SplStep : Записать число осколков

mov CX.SplinterMaxNumber

@@NextSplinter:

: Загрузить в DI указатель на маску взрыва

mov DI,offset ExpMask

; Умножить шаг no Y на номер кадра (плюс 1) lodsb;записать шаг по Y в AL

mov АН,[byte ptr ExpFrameNumber]

inc АН увеличить номер кадра на 1 imul АН ;умножить шаг на номер кадра

; Прибавить номер строки центра иаски

add AX,ExpR-l

: Умножить результат на длину строки иаски

mov DX.2*ExpR mul DX

: Прибавить результат к смещению в маске

add DI.AX

: Умножить шаг no X на номер кадра (плюс 1) lodsb;загшсать юаг по X в AL

mov АН,[byte ptr ExpFrameNumber]

inc АН увеличить номер кадра на 1 imul АН ;уиножить юаг на нонер кадра

; Прибавить номер колонки центра иаски

add AX,ExpR-l

; Прибавить результат к смещению в иаске

add DI.AX

: Нанести точку-осколок на маску

mov ВХ,offset ExpColors

add ВХ.[ExpFrameNumber]

mov AL,[BX]

mov [DI],AL

loop

@@NextSplinter

popa

ret

ENDP ExplosionFrame

ENDS

Листинг 4.20 содержит головной модуль одностраничного варианта игры «Самолет и ракета» PlaneAndRocket. Перед запуском игры на экран выдается текст с описанием задания (сбить все пролетающие самолеты, при трех пропущенных игра заканчивается) и управляющих клавиш (Пробел — запуск ракеты, Esc — срочный выход из игры). Самолет движется по экрану с постоянной скоростью (1 или 2 пиксела на кадр); скорость, высота и направление полета выбираются случайным образом (на каждый параметр — своя таблица случайных чисел). Ракета имеет нелинейный начальный участок разгона, а затем движется с постоянной скоростью 3 пиксела на кадр. В каждом эпизоде игры имеется только одна ракета — поспешность или задержка с запуском приводят к проигрышу в данном эпизоде. В случае промаха ракета самоликвидируется в верхних слоях атмосферы (чтобы она не попадала в зону искажения изображений).

ПРИМЕЧАНИЕ

Под мертвую зону (область искажений) в верхней части экрана выделена полоса высотой в 32 пиксела (1/15 высоты экрана), куда не должен попасть ни один динамический объект. На медленных видеоконтроллерах такой ширины мертвой зоны недостаточно, и можно наблюдать разнообразные эффекты.

Листинг 4.20. Игра «Самолет и ракета» — основной модуль программы для одностраничного режима

IDEAL

РЗВ6

LOCALS

MODEL MEDIUM

: Подключить файл мнемонических обозначений : кодов управляющих клавиш и цветовых кодов

Include "listl_03.inc"

; Подключить файл макросов

Include "listl_04.inc"

: Подключить описание констант, глобальных переменных : и макрокоманд

Include "list4_16.inc"

; Подключить файл масок объектов

Include "Iist4_17.inc"

DATASEG

: ТЕКСТОВЫЕ СООБЩЕНИЯ

Txtl

DB 0.24,"АНИМАЦИЯ В 0ДН0СТРАНИЧН0М РЕЖИМЕ",О

DB 2,2,"Параметры видеорежима: разрешение "

DB "640×480, 256 цветов, линейная адресация".О Txt2

DB 10.2В. "ИГРА ", "" , "САМОЛЕТ И РАКЕТА" ."".О

Листинг 4.20 (продолжение)

Txt3 ОВ 13.19

ОВ "Задание: сбить все пролетающие самолеты.",0

DB 15,22,"Игра завершается после 100 эпизодов".0 ОВ 16,24,"или трех пропущенных самолетов.",0 Txt4 ОВ 20.0,"Управляющие клавиши:",О Txt5 0В 22,0,"Пробел – запустить ракету:",0

ОВ 24,0,"Esc – срочный выход из программы.",0 Txt6 ОВ 29,29,"Нажмите любую клавишу".0 ENDS

SEGMENT sseg para stack ‘STACK1

DB 400h DUP(?)

ENDS

C0DESEG

;*ОСНОВНАЯ ПРОГРАММА*

PROC PlaneAndRocket

mov AX.DGR0UP

mov DS.AX

mov [CS: Ma i nDat aSeg]. AX

; Установить текстовый режим

mov АХ.З int lOh : "Захватить" текстовый шрифт

cal1 GrabRusFont

; Установить видеорежим

call SetVESAVideoMode : Установить режим прямой адресации памяти call GInitialization

: ВВОДНЫЙ ТЕКСТ

: Отобразить текстовые сообщения

mov [DefaultBackground],BLACK ;черный фон

mov [DefaultColor],LIGHTGREEN ;зеленый текст MGShowText 2,Txtl

mov [Defaultedor],LIGHTCYAN ;голубой текст MGShowString Txt2

mov [DefaultColor],LIGHTGREEN ;зеленый текст MGShowText 3,Txt3

mov [DefaultColor],YELLOW ;желтый текст MGShowString Txt4

mov [DefaultColor],LIGHTGREEN :зеленый текст MGShowText 2,Txt5

mov [DefaultColor].YELLOW ;желтый текст MGShowString Txt6 : Ожидать нажатия любой клавиши cal1 GetChar

; ПОДГОТОВКА К ОСНОВНОМУ ЦИКЛУ

call DrawMainBackground ;рисование фона : Сбросить счетчики

mov [PlnCounterLO

mov [DestroedPlns],0

mov [EscPlnsLO

mov [RktCounter],0

mov [EpisodeNumber],0

Mmiimiiiiiiiimiiiiiiii

;# ЦИКЛ ПО ЭПИЗОДАМ #

@@NextEpisode:

call InitEpisode

.mmmmmwmm

;# ЦИКЛ ВЫВОДА КАДРА #

мшштштш»

@@FrameCyclе:

; СТЕРЕТЬ ПОДВИЖНЫЕ ОБЪЕКТЫ

; Стереть самолет

DeleteMImage PInA.PI nML.PInMH,PInFl

; Стереть ракету и пламя из ее сопла DeleteMImage FlmA.FlmML.FlmMH.FlmFl DeleteMImage RktA,RktML,RktMH,RktFl

; Стереть взрыв

DeleteMImage ExpA,ExpR*2,ExpR*2,ExpFl

; ОПРОС КЛАВИАТУРЫ

call WaitChar

cmp AX,0 ;клавиша была нажата?

je

@@CommandNotInput ;нажатий не было cmp AX,20h ;нажат пробел?

je

@@Fire .-запустить ракету cmp AX.lBOOh ;нажата клавиша ESC?

je

@@Exit ;выход jmp

@@CommandNotInput ;коианды не было

; Пуск ракеты

PPFire: cmp [RktState] ,0

jne

@@RocketNotReady ;ракета не готова

mov [RktStartFrameNumber].0

mov [RktState],1 ;запуск

mov [FlmFl],l;отобразить выхлоп

inc [RktCounter]

mov [GameStateGhange],1 @0RocketNotReady:

@@CommandNotInput:

; ВЫВОД ИЗОБРАЖЕНИЯ САМОЛЕТА

cmp [PlnState].0 ;ожидание

Листинг 4.20(продолжение)

je

@@Р10

cmp [PlnState],1 ;полет

je <a@PU

cmp [PlnState],2 ;взрыв

je

@@P12

; Ожидание саиолета

(ФР10:

; Проверить истечение вреиени задержки саиолета

mov AX,[PlaneOeltaT] cmp [GameTimeCounter],AX jb

@@NoPl

inc [PlnCounter] ;увел. счетчик саиолетов

mov [PlnState],l :запустить саиолет : Полет самолета

@@Р11:

mov [PlnFl],1 ;отобразить саиолет

mov ЕАХ,[PIaneSpeed]

add [Р1пС].ЕАХ jmp

@@NoPl

; Взрыв самолета

(ФР12: cmp [ExpFrameNumber],HaxExplFrameNumber jae PffindOfEpisode

; Нарисовать маску очередной : фазы взрыва самолета call ExplosionFrame

inc [ExpFrameNumber]

@@NoPl:

; (завершена обработка состояния саиолета)

; ВЫВОД ИЗОБРАЖЕНИЯ РАКЕТЫ

cmp[RktState],l ;запуск ракеты

je@@rol

cmp[RktState],2 ;ракета в полете

je@@го2

cmp[RktState],3 :взрыв ракеты

je@@гоЗ

cmp[RktState],4 ;разлет осколков

je@@го4

jmp@@EndRktDraw : Разгон ракеты

й?го1: movВХ,[RktStartFrameNumber]

cmpBX.HaxRktStartFrame

jb@@NextRSFrame

mov[RktState],2

jmpй?го2

@@NextRSFrame:

shlBX.3.-умножить номер кадра на В

addВХ,offset RktStartStep

movEAX. [PRC]

mov[RktC],EAX

movEAX,[PRS] ;вычесть смещение ракеты

subEAX,[BX] ;из номера строки установки

mov [RktS],EAX

add EAX.[BX+4] ;прибавить смещение пламени

mov [F1mS],EAX ;к номеру строки ракеты

inc [RktStartFrameNumber]

jmp POEndRktOraw

; Полет ракеты

@Pro2: : Проверка на выход ракеты за верхнюю

; границу "атмосферы"

cmp [RktS],48 ;граница зоны искажений?

jl

@@гоЗ:самоуничтожение ракеты

: Вычислить квадрат расстояния

: между самолетом и ракетой

mov ЕАХ,[RktS]

sub ЕАХ,[PlnS]

imul ЕАХ

; (в ЕАХ – квадрат расстояния по Y)

mov EBX,ЕАХ

mov ЕАХ,[RktC] sub ЕАХ,[PInC] sub EAX,(PInML-RktML)/2 imul EAX;квадрат расстояния по X

; (в ЕАХ – квадрат расстояния по X)

add ЕАХ. EBX •

; (в ЕАХ – квадрат расстояния между самолетом

; и ракетой)

cmp EAX.TargetOistanceSQ

jae

@@DrawRocket

: Произошло попадание ракеты в самолет

mov [HitFlag],l установить флаг попадания

jmp

@@гоЗ;начать отображение взрыва

@@DrawRocket:

sub [RktS],3

mov EAX,[RktS]

add EAX.32

mov [FlmSLEAX jmp

@@EndRktDraw

: Начало взрыва ракеты

@@ro3:

; Переключить состояние ракеты

mov [RktState],4

; Очистить маску взрыва call ExpMaskClear

; Определить координаты взрыва

mov [ExpFrameNumber].0

mov ЕАХ,[RktS] sub EAX.ExpR-RktMH/2

mov [ExpS],EAX

mov EAX.[RktC] sub EAX.ExpR-RktML/2

mov [ExpC].EAX

Листинг 4.20 (продолжение)

mov [RktFl],0 :убрать ракету

mov [FlmFlLO ;убрать выхлоп

mov [ExpFl].1 ;отобразить взрыв

; Разлет осколков ракеты

@@ro4: cmp [ExpFrameNumber],HaxExpl FrameNumber jae

@@SetRktState5

; Нарисовать наску очередной фазы взрыва ракеты call ExplosionFrame

inc [ExpFrameNumber] jmp

@@EndRktDraw №SetRktState5:

mov [RktState],5 ;перезарядка установки

mov [ExpFl],0 ;убрать взрыв

; Было попадание в самолет? cmp [HitFlag],0

je PPEndRktDraw ;попадания не было

mov [PlnState],2 ;взрыв самолета

: Увеличить значение счетчика сбитых саиолетов

inc [DestroedPlns]

mov [GameStateGhange].1

; Очистить маску взрыва

call ExpMaskClear

; Определить координаты взрыва

mov [ExpFrameNumber],О

mov ЕАХ,[PInS]

sub EAX,ExpR-PlnMH/2

mov [ExpS],EAX

mov EAX.[PlnC]

sub EAX.ExpR-PlnML/2

mov [ExpC],EAX

mov [ExpFl],l .-отобразить взрыв самолета

@@EndRktDraw:

; ОТОБРАЗИТЬ ДИНАМИЧЕСКИЕ ОБЪЕКТЫ : Нарисовать саиолет

DrawMImage PlnS.PlnC,PinML.PInMH.PInMask,PInA.PInFl : Нарисовать пламя ракеты DrawMImage F1mS,RktC.FlmML.FlmMH.Flm.FlmA.FlmFl

; Нарисовать ракету

DrawMImage RktS,RktC,RktML.RktMH.Rkt,RktA,RktFl

; Отобразить взрыв

DrawMImage ExpS.ExpC,ExpR*2,ExpR*2.ExpMask.ExpA.ExpFl : Счетчик пропущенных самолетов call ShowEscapedPlanes

; ОЖИДАНИЕ НАЧАЛА СЛЕДУЮЩЕГО КАДРА call WaitVSync : Увеличить на 1 счетчик времени

inc [GameTimeCounter]

; КОНЕЦ ЦИКЛА ПО КАДРАМ

; Самолет покинул пределы экрана? cmp [PIaneDi recti on],0

je

@@LtoR;движение слева направо

cmp [PlnC],-PlnMl jl

@@P1 aneEscaped jmp

@@FrameCycle

@@LtoR: cmp [PlnC],ScreenLength

jl

@@FrameCycle №P1 aneEscaped:

: Увеличить счетчик пропущенных самолетов

inc [EscPlns]

mov [GameStateGhange].1

: КОНЕЦ ЦИКЛА ПО ЭПИЗОДАМ

@@EndOfEpisode:

; Увеличить счетчик игровых эпизодов

inc [EpisodeNumber] : Закончить игру после MaxEpNumber эпизодов cmp [Epi sodeNumber].MaxEpNumber jae

@@Exit : Конец игры, если пропущено MaxSavedPlanes самолетов cmp [EscPlns],MaxSavedPlanes jl

@@NextEpisode

@@Exit:

: Показать результаты

call ShowGameResults : Установить текстовый режим

mov AX,3 int lOh : Выход в DOS

mov AH,4Ch int 21h ENDP PIaneAndRocket ENDS

; Подключить процедуры вывода данных на экран : для текстовых режимрв

Include "listl_02.inc" : Подключить процедуры перевода чисел

Include "list2_05.inc"

; Подключить набор процедур общего назначения, : предназначенных для установки графических : видеорежимов и работы в них

Include "list4_02.inc"

: Подключить подпрограмму, настраивающую FS на : видеопамять, GS – на дополнительную память

Include "list4_14.inc"

Листинг 4.20 (продолжение)

: Подключить набор процедур вывода текста,

; предназначенный для 256-цветных режимов с

; раздельным доступом с сегментам видеопамяти

; и дополнительной памяти

Include "list4_15.inc"

; Подключить набор процедур для вывода

; изображений объектов в одностраничнон режиме

Include "list4 1B.inc"

: Подключить набор универсальных подпрограмм, пригодных

; и для одностраничного, и для двухстраничного режима

Include "list4_19.inc"

END

Листинг 4.21 содержит модернизированный для режима переключения странйц вариант листинга 4.18: внесены изменения в процедуры DrawMovingImage, Deletelmage, ShowBackground, ShowEscapedPlanes; добавлены процедура переключения видеостраниц SwitchVideoPage, процедура возврата в одностраничный режим работы RestoreNormal Mode и процедура инициализации дополнительных переменных эпизода I n i tEpi sode2. Все изменения в процедурах связаны с тем, что адрес страницы видеопамяти из константы превращается в переменную.

ПРИМЕЧАНИЕ

Обратите внимание на то, что структура области сохранения фона в дополнительной памяти в точности повторяет собой структуру видеостраниц. Если у видеостраницы есть невидимые защитные полосы, то такие же полосы есть и у массива сохранения фона. Подобная согласованность структур часто бывает необходима, чтобы избежать лишних перерасчетов и перекодировок.

Листинг 4.21. Набор подпрограмм для вывода фона и динамических объектов в режиме переключения страниц

: Номера начальных строк видеостраниц VPageOStartString equ 64 VPagelStartString equ 60В

; Начальные адреса видеостраниц VPageOAddress equ 65536 VPagelAddress equ 622592

; Начальные адрес строки статуса в оперативной памяти : (располагается вслед за областью сохранения фона) StatusStringAddress equ BOOOOh

DATASEG

; ОБЩИЕ ПАРАМЕТРЫ ВИДЕОРЕЖИМА : Номер перерисовываемой видеостраницы VPageNumber

DB О

: Адрес перерисовываемой видеостраницы

DrPageAddress DD VPageOAddress

; Адрес отображаемой видеостраницы

ShPageAddress DD VPageOAddress

; ПАРАМЕТРЫ ПОДПРОГРАММЫ РИСОВАНИЯ СПРАЙТА

; Указатели на маску и область сохранения фона

ImageMaskOffset DW ? .указатель на маску

ImageBackOffset DW ? ;указатель на фон

: Размеры маски изображения

ImageL DD ? :ширина маски

ImageH DD ? ;высота маски

; Позиция маски изображения на экране

ImageS DD ? ;строка

ImageC DD ? ;колонка

ImageA DD ? ;линейный адрес

ImageF

DB ? :флаг присутствия объекта на странице

ENDS

C0DESEG

• А А АА A AAA AAA А "А* А А А А’А’ А ‘А’ А А А А А А А А А А А А А А "А"А’ А АЛЛ АА А А

•*ПЕРЕКЛЮЧИТЬ ВИДЕОСТРАНИЦЫ*

;* Процедура использует в качестве параметров * ;* глобальные переменные VPageNumber,*

;* DrPageAddress, ShPageAddress.*

PROC SwitchVideoPage NEAR pusha

cmp [VPageNumber].0 jne

@@Pgl

mov [VPageNumber],1

mov [DrPageAddress],VPagelAddress

mov [ShPageAddress],VPageOAddress

mov DX,VPageOStartStri ng

jmp short

@@UseVESAFunction

@@Pgl:

mov [VPageNumber],0

mov [DrPageAddress].VPageOAddress

mov [ShPageAddress],VPagelAddress

mov DX,VPagelStartString

@@U seVESAFunct i on:

mov AX,4F07h

mov BX.O

mov CX.O int lOh popa ret

ENDP SwitchVideoPage

Листинг 4.21 (продолжение) ;************************************* ;* ВОССТАНОВИТЬ ОДНОСТРАНИЧНЫЙ РЕЖИМ *

;* (процедура параметров не имеет) * ;*************************************

PROC RestoreNormalMode NEAR pusha

mov AX,4F07h

mov BX.O

mov CX, 0

mov DX.O int lOh popa ret

ENDP RestoreNormalMode

;* ОТОБРАЗИТЬ ФОН НА ОБЕ ВИДЕОСТРАНИЦЫ * ;* (процедура параметров не имеет) *

PROC ShowBackground near pushad eld

: Настроить ESI на область сохранения фона

mov ESI,VPageOAddress

; Настроить E0I на первую видеостраницу

mov EDI,VPageOAddress

; Загрузить в счетчик размер изображения

mov ЕСХ,LogicalStringLength*ScreenHeigth : Скопировать фон в обе страницы сразу

mov AL,[GS:ESI]

mov [FS:EDI],AL

mov [FS:EDI+VPagelAddress-VPageOAddress],AL

inc ESI

inc EDI

dec ECX

jnz

popad

ret

ENDP ShowBackground

. **** * * * A * ************ * A A * *•* ********** ***** ***** A ***

;*НАРИСОВАТЬ ДИНАМИЧЕСКИЙ ОБЪЕКТ*

;* Для передачи параметров используются глобальные * ;* переменные ImageMaskOffset, ImageL, ImageH, * ;* ImageS. ImageC. ImageA, ImageF, DrPageAddress. *

PROC DrawMovinglmage near pushad

cmp [ImageF],0

je

@@End eld

; Вычислить адрес начальной точки для вывода наски

; Умножить длину строки на номер строки (Y)

mov ЕАХ,LogicalStringLength

mov EDX.[ImageS] mul EDX

; Прибавить номер колонки (X)

add EAX,[ImageC]

mov [ImageA],EAX :запомнить смещение

; Прибавить смещение перерисовываемой страницы

add ЕАХ,[DrPageAddress]

mov EDI,ЕАХ ;результат – в индексный регистр

; Записать адрес маски в индексный регистр

mov SI,[ImageMaskOffset]

; Вывести изображение

mov DX,[word ptr ImageH] :высота маски

@@M0:

; Вывести очередную строку маски

mov CX.[word ptr ImageL] ;ширина маски

@@М1:

; Проверить точку маски lodsb

and AL.AL;код цвета равен нулю?

jz

@@М2пропустить точку

mov [FS:EDI],AL .вывести точку

@@М2:

; Перейти к следующей точке

inc EDI loop

@@М1

; Перейти на следующую строку

add EDI,LogicalStringLength sub EDI,[ImageL] dec DX jnz

@@M0

@@End: popad ret

ENDP DrawMovinglmage

;*HAPMCDBATb ЭЛЕМЕНТ ФОНА*

;* Для передачи параметров используются глобальные * ;* переменные ImageMaskOffset, ImageL, ImageH. * ;* ImageS, ImageC.*

PROC DrawStaticImage near pushad eld

; Загрузить начальное смещение

mov EDI.VPageOAddress

; Вычислить адрес начальной точки для вывода маски

; Умножить длину строки на номер строки (Y)

mov ЕАХ,LogicalStringLength

mov EDX.[ImageS]

Листинг 4.21(продолжение) mul EDX

; Прибавить номер колонки (X)

add ЕАХ,[ImageC]

; Прибавить результат к начальному смещению

add EDI,ЕАХ

; Записать адрес маски в индексный регистр

mov SI,[ImageMaskDffset] : Вывести изображение

mov DX,[word ptr ImageH] ;высота маски

@@M0:

; Вывести очередную строку маски

mov CX,[word ptr ImageL] ;ширина маски

@@М1: : Проверить точку маски lodsb

and AL.AL;код цвета равен нулю?

jz

@@М2пропустить точку

mov [GS:EDI],AL ;вывести точку

@@М2: : Перейти к следующей точке

inc EDI loop

@@М1

: Перейти на следующую строку

add EDI.LogicalStringLength

sub EDI,[ImageL]

dec DX

jnz ечамо

popad

ret

ENDP DrawStaticImage

;* СТЕРЕТЬ ИЗОБРАЖЕНИЕ (ВОССТАНОВИТЬ ФОН) * ;* Для передачи параметров используются глобальные * ;* переменные ImageL, ImageH, ImageA, ImageF, * ;* DrPageAddress.*

PROC Deletelmage near pushad

cmp [ImageF],0

je P0End : Записать в индексные регистры адрес изображения

mov EDI,[ImageA]

mov ESI,EDI

; Прибавить начальное смещение

add EDI,[DrPageAddress]

add ESI.VPageOAddress

; Вывести исходное изображение

mov DX,[word ptr ImageH] ;высота маски

@@M0:

mov CX,[word ptr ImageL] :ширина маски @eMl:

mov AL, [GS:ESI]

mov [FS:EDI].AL

incEDI

incESI

loop@@M1

addEDI.LogicalStringLength

subEDI.[ImageL]

addESI.LogicalStringLength

subESI.[ImageL]

decDX

jnzP0MO

@@End: popad ret

ENDP Deletelmage

;* ОТОБРАЗИТЬ ЧИСЛО ПРОПУЩЕННЫХ САМОЛЕТОВ * ;* (в левом верхнем углу экрана) *

• АА AAife A’ArA’ft A AAA ?? А

PROC ShowEscapedPlanes NEAR pushad

movSI.offset FontBxl6

movAX,[EscPlns]

addAX,’0′

shlAX.4

addSI, AX

movEDI,[DrPageAddress]

addEDI. 4*Logi cal Stri ngLength+8 : Определить цвет цифры

movBL.LIGHTGREEN

cmp[EscPlns],0

je@@Bkgr

movBL,YELLOW

cmp[EscPlns],l

je@@Bkgr

movBL.LIGHTRED

PPBkgr: movDL.BLUE

; Вывести цифру

movAH,16 ;счетчик строк маски буквы «аМО: lodsb

movСХ,В ;счетчик точек в строке иаски

Р0М1: rolAL.1

jc@РМ2

mov[FS:EDI].DL

mov[FS:EDI+Logi calStri ngLength].DL

incEDI

mov[FS:EDI],DL

mov[FS:EDI+Logi calStri ngLength],DL

jmp@PM3

@@M2: mov[FS:EDI].BL

mov[FS:EDI+LogicalStringLength],BL

incEDI

mov[FS:EDI],BL

Листинг 4.21 {продолжение)

mov [FS:EDI+LogicalStringLength],BL ехаМЗ:

inc EDI loop

@@M1

add EDI,2*Logi calStri ngLength-16

dec AH

jnz P0MO

popad

ret

ENDP ShowEscapedPl anes

;* УСТАНОВИТЬ НАЧАЛЬНОЕ СОСТОЯНИЕ ПЕРЕМЕННЫХ ЭПИЗОДА *

PROC InitEpisode2 near

call InitEpisode

mov [PlnF2],0

mov [RktF2],0

mov [FlmF2],0

mov [ExpF2],0 call SwitchVideoPage ret

ENDP InitEpisode2 ENDS

Листинг 4.22 содержит головной модуль игры «Самолет и ракета» для режима с переключением видеостраниц PlaneAndRocket2. Нормальная работа этого варианта игры возможна только при наличии у видеоконтроллера не менее 2 Мбайт памяти.

Листинг 4.22. Игра «Самолет и ракета» — основной модуль

программы для режима с переключением страниц

IDEAL

РЗВ6

LOCALS

MODEL MEDIUM

; Подключить файл мнемонических обозначений

; кодов управляющих клавиш и цветовых кодов

Include "listl_03.inc"

; Подключить файл макросов

Include "listl_04.inc"

; Подключить описание констант, глобальных переменных

; и макрокоманд

Include "list4_16.inc" : Подключить файл масок объектов

Include "list4_17.inc"

DATASEG

; ИНФОРМАЦИЯ ОБ ОТОБРАЖАЕМЫХ ОБЪЕКТАХ

; Флаги наличия объектов в предыдущей странице

PlnF2

DB ?

RktF2 ОВ ?

FlmF2

DB ?

ExpF2

DB ?

; Предыдущие линейные адреса объектов

PI ПАР DD ?

RktAP DD ?

FlmAP DD ?

ExpAP DD ?

; ТЕКСТОВЫЕ СООБЩЕНИЯ

Txtl

DB 0,21."АНИМАЦИЯ В РЕЖИМЕ ПЕРЕКЛЮЧЕНИЯ СТРАНИЦ",0

DB 2,2,"Параметры видеорежима: разрешение "

DB "640×480, 256 цветов, линейная адресация",0 Txt2

DB 10.28,"ИГРА ".""."САМОЛЕТ И РАКЕТА",\0 Txt3

DB 13,19

DB "Задание: сбить все пролетающие самолеты.",0

DB 15,22,"Игра завершается после 100 эпизодов",0

DB 16,24,"или трех пропущенных самолетов.",0 Txt4

DB 20,0."Управляющие клавиши:",0 Txt5

DB 22,0,"Пробел – запустить ракету;",0

DB 24,0,"Esc – срочный выход из программы.",0 Txt6

DB 29.29,"Нажмите любую клавишу",0 ENDS

SEGMENT sseg para stack ‘STACK"

DB 400h DUP(?)

ENDS

CODESEG

• к A 1t A kk kkkkkkkkkkkkkkkk kk ‘kk к к к к к к к к A A A A’ it A A it "к Л

;*ОСНОВНАЯ ПРОГРАММА*

PROC PIaneAndRocket2

mov AX.DGROUP

mov DS.AX

mov [CS:MainDataSeg],AX : Установить текстовый режии

mov АХ.З int lOh

; "Захватить" текстовый шрифт

cal1 GrabRusFont : Установить видеорежим

call SetVESAVideoMode : Установить режим прямой адресации памяти call GInitialization

; ВВОДНЫЙ ТЕКСТ

; Отобразить текстовые сообщения

mov [DefaultBackground],BLACK :черный фон

Листинг 4.22 (продолжение)

mov [DefaultColor].WHITE;белый текст MGShowText 2,Txtl

mov [DefaultColor],LIGHTCYAN;голубой текст MGShowString Txt2

mov [DefaultColor],LIGHTGREEN;зеленый текст MGShowText 3,Txt3

mov [DefaultColor],YELLOW:желтый текст MGShowString Txt4

mov [DefaultColor].LIGHTGREEN;зеленый текст MGShowText 2,Txt5

mov [DefaultColor],YELLOW:желтый текст MGShowString Txt6

; Ожидать нажатия любой клавиши call GetChar

; ПОДГОТОВКА К ОСНОВНОМУ ЦИКЛУ

call DrawMainBackground ;создать фон

; Сбросить счетчики

mov [PlnCounter],0

mov [0estroedPlns],0

mov [EscPlns].0

mov [RktCounter],0

mov [EpisodeNumber],0

.тияшшинтт

;# ЦИКЛ ПО ЭПИЗОДАМ #

@@NextEpisode:

call InitEpisode2

;—1И1Ш

;# ЦИКЛ ВЫВОДА КАДРА #

@@FrameCycle:

; СТЕРЕТЬ ПОДВИЖНЫЕ ОБЪЕКТЫ

; Стереть самолет

DeleteMImage PlnAP.PlnML,PlnMH,PlnF2

; Стереть ракету и пламя из ее сопла DeleteMImage FlmAP.FlmML.FlmMH,FlmF2 DeleteMImage RktAP.RktML.RktMH.RktF2

; Стереть взрыв

DeleteMImage ExpAP,ExpR*2.ExpR*2.ExpF2

; ПЕРЕРИСОВЫВАТЬ ПОДВИЖНЫЕ ОБЪЕКТЫ

; Скопировать начальные адреса в "предыдущие"

mov ЕАХ,[PlnA]

mov [PInAP],ЕАХ

mov ЕАХ,[RktA]

mov[RktAP],EAX

movEAX, [FlmA]

mov[FlmAP],EAX

movEAX,[ExpA]

mov[ExpAP],EAX

: Скопироватьпризнаки наличия объектов в "предыдущие"’

movAL,[PlnFl]

mov[PlnF2],AL

movAL,[RktFl]

mov[RktF2],AL

movAL,[FlmFl]

mov[FlmF2],AL

movAL,[ExpFl]

mov[ExpF2],AL

; ОПРОС КЛАВИАТУРЫ

callWaitChar

cmpAX,0 ;клавиша была нажата?

je@@CommandNotInput :нажатий не было

cmpАХ.20h :нажат пробел?

je@@Fire :запустить ракету

cmpAX.lBOOh :нажата клавиша ESC?

jePOExit ;выход

jmp@@CommandNotInput ;команды не было : Пуск ракеты

@@Fire: cmp[RktState],0

jne@@RocketNotReady ;ракета не готова

mov[RktStartFrameNumber],0

mov[RktState],1 ;запуск

mov[FlmFl],1 ;отобразить выхлоп

inc[RktCounter]

mov[GameStateGhange].1

@@RocketNotReady:

@@CommandNotInput:

; ВЫВОД ИЗОБРАЖЕНИЯ САМОЛЕТА

cmp [PlnState],0 ;ожидание

je

@@P10

cmp [PlnState],l :полет

je

@@Pll

cmp [PlnState],2 ;взрыв

je P0P12

; Ожидание самолета

@@P10: : Проверить истечение времени задержки самолета

mov AX,[PlaneDeltaT] cmp [GameTimeCounter],AX jb №NoPl

inc [PlnCounter] увеличить счетчик самолетов

mov [PlnState],l ;запустить самолет

; Полет самолета

Р@Р11:

mov [PlnFl],l ;отобразить самолет

mov EAX,[PlaneSpeed]

Листинг 4.22(продолжение)

add [PlnCLEAX jmp

@@NoPl : Взрыв саиолета

@@P12: cmp [ExpFrameNumber],MaxExplFrameNumber jae 0@EndOfEpisode

; Нарисовать маску очередной

; фазы взрыва самолета call ExplosionFrame

inc [ExpFrameNumber]

@@NoPl:

; (завершена обработка состояния самолета)

; ВЫВОД ИЗОБРАЖЕНИЯ РАКЕТЫ

cmp [RktState],l ;запуск ракеты

je

@@rol

cmp [RktState],2 :ракета в полете

je

@@го2

cmp [RktState],3 :взрыв ракеты

je

@@гоЗ

cmp [RktState],4 ;разлет осколков

je

@@го4 jmp

@@EndRktDraw : Разгон ракеты

@@rol:

mov ВХ.[RktStartFrameNumber] cmp BX.MaxRktStartFrame jb

@@NextRSFrame

mov [RktState],2 jmp

@@ro2

@@NextRSFrame:

shl BX.3;умножить номер кадра на В

add ВХ,offset RktStartStep

mov EAX,[PRC]

mov [RktC],EAX

mov EAX,[PRS] ;вычесть смещение ракеты sub EAX,[BX] ;из номера строки установки

mov [RktS],EAX

add EAX.[BX+4] ;прибавить смещение пламени

mov [FlmS],EAX ;к номеру строки ракеты

inc [RktStartFrameNumber] jmp

@@EndRktDraw

; Полет ракеты

@@ro2: : Проверка на выход ракеты за верхнюю : границу "атмосферы" cmp [RktS],0

jl

@@гоЗ;самоуничтожение ракеты

: Вычислить квадрат расстояния между

: самолетом и ракетой

mov ЕАХ,[RktS]

sub ЕАХ.[PInS]

imul EAX

: (в ЕАХ – квадрат расстояния по Y)

movЕВХ.ЕАХ

movEAX,[RktC]

subЕАХ,[PInC]

subEAX,(PInML-RktML)/2

imulEAX ;квадрат расстояния по X

; (вЕАХ – квадрат расстояния по X)

addЕАХ.ЕВХ

: (вЕАХ – квадрат расстояния между санолетои : и ракетой)

cmpEAX.TargetDistanceSQ

jae@@DrawRocket

; Произошло попадание ракеты в санолет

mov[HitFlag].1 установить флаг попадания

jmp@@гоЗ :начать отображение взрыва

@@DrawRocket:

sub[RktS],3

movEAX,[RktS]

addEAX,32

mov[FlmS],EAX

jmp@@EndRktDraw

; Начало взрыва ракеты

@@ro3:

; Переключить состояние ракеты

mov [RktState],4

; Очистить наску взрыва call ExpMaskClear

; Определить координаты взрыва

mov [ExpFrameNumber].О

mov ЕАХ,[RktS] sub EAX.ExpR-RktMH/2

mov [ExpS],EAX

mov EAX, [RktC] sub EAX,ExpR-RktML/2

mov [ExpC],EAX

mov [RktFl],0 ;убрать ракету

mov [FlmFl],0 ;убрать выхлоп

mov [ExpFlj.l :отобразить взрыв

; Разлет осколков ракеты

@@ro4: cmp [ExpFrameNumber],MaxExplFrameNumber jae

@@SetRktState5

; Нарисовать наску очередной фазы взрыва ракеты call ExplosionFrame

inc [ExpFrameNumber] jmp

@@EndRktDraw

@@SetRktState5:

mov [RktState],5 ;перезарядка установки

mov [ExpFl],0 :убрать взрыв

; Было попадание в санолет? cmp [HitFlag],О

Листинг 4.22(продолжение)

je

@@EndRktDraw :попадания не было

mov [PlnState],2 ;взрыв самолета

: Увеличить значение счетчика сбитых самолетов

inc [OestroedPlns]

mov [GameStateGhange],1

; Очистить маску взрыва

call ExpMaskClear

; Определить координаты взрыва

mov [ExpFrameNumber],О

mov EAX,[PlnS]

sub EAX,ExpR-PlnMH/2

mov [ExpS],EAX

mov EAX,[PlnC]

sub EAX.ExpR-PlnML/2

mov [ExpC],EAX

mov [ExpFl],1 ;отобразить взрыв самолета

@@EndRktOraw:

; ОТОБРАЗИТЬ ДИНАМИЧЕСКИЕ ОБЬЕКТЫ

; Нарисовать самолет

OrawMImage PInS.PInC.PInML,PInMH,PInMask.PInA.PInFl

; Нарисовать пламя ракеты

DrawMImage F1 mS,RktC,FlmML,FlmMH,Flm,FlmA, FlmFl : Нарисовать ракету

DrawMImage RktS,RktC,RktML,RktMH,Rkt,RktA,RktFl : Отобразить взрыв

DrawMImage ExpS,ExpC,ExpR*2,ExpR*2,ExpMask.ExpA.ExpFl : Счетчик пропущенных самолетов cal 1 ShowEscapedPlanes

; ОЖИДАНИЕ НАЧАЛА СЛЕДУЮЩЕГО КАДРА call WaitVSync

; Переключить страницы call SwitchVideoPage

; Увеличить на 1 счетчик времени

inc [GameTimeCounter]

; КОНЕЦ ЦИКЛА ПО КАДРАМ

; Самолет покинул пределы экрана? cmp [PlaneDirection].0 направление движения?

je

@@LtoR cmp [PInC].-PInML jl

@@P1aneEscaped jmp

@@FrameCycle ЗЙ-toR: cmp [PlnC].ScreenLength

jl P@FrameCycle PPPlaneEscaped:

: Увеличить счетчик пропущенных самолетов

inc [EscPlns]

mov [GameStateGhange].1

: КОНЕЦ ЦИКЛА ПО ЭПИЗОДАМ

@@EndOfEpi sode:

; Увеличить счетчик игровых эпизодов

inc [EpisodeNumber]

; Закончить игру после MaxEpNumber эпизодов cmp [EpisodeNumber],MaxEpNumber jae

@@Exit : Конец игры, если пропущено MaxSavedPlanes саиолетов cmp [EscPlns],MaxSavedPlanes jl

@@NextEpi sode

@@Exit:

; Восстановить обычный режии работы с видеопамятью

cal1 RestoreNormalMode : Показать результаты

са 11 ShowGameResults

; Установить текстовый режим

mov АХ.З int 10h

; Выход в DOS

mov AH,4Ch int 21h ENDP PlaneAndRocket2 ENDS

: Подключить процедуры вывода данных на экран : для текстовых режимов

Include "listl_02.inc" : Подключить процедуры перевода чисел

Include "list2_05.inc"

: Подключить набор процедур общего назначения, : предназначенных для установки графических : видеорежииов и работы в них

Include "list4_02.inc"

; Подключить подпрограмму, настраивающую FS на

; видеопамять, GS – на дополнительную память

Include "list4_14.inc" : Подключить набор процедур вывода текста. : предназначенный для 256-цветных режимов с : раздельным доступом с сегментам видеопамяти : и дополнительной памяти

Include "list4_15.inc"

; Подключить набор процедур для вывода изображений

; объектов в режиме переключения страниц

Include "list4_21.inc"

; Подключить набор универсальных подпрограмм, пригодных : и для одностраничного, и для двухстраничного режима

Include "list4 19.inc"

END

Источник: Кулаков В. К90 Программирование на аппаратном уровне: специальный справочник (+дискета). 2-е издание. — СПб.: Питер, 2003. — 847 е.: ил.

По теме:

  • Комментарии