На чем мы остановились?
Синтаксис MASM
Основной игровой цикл
Связь с Direct Draw
Наша Direct Draw Library
Наша Bitmap Library
Game
А мы и не останавливались ...
Рассмотрим конструкции MASM , которые являются удобоваримыми в
сравнительном смысле с аналогичными конструкциями на си .
Затем рассмотрим основной цикл в главных виндовских процедурах .
После мы обратим свои взоры на Direct Draw . Поняв , как он работает ,
мы сможем построить свою собственную Direct Draw Library .
Затем мы построим bitmap file library .
И в конце можно будет написать небольшую Loading Game .
Для компиляции нам потребуется MASM32 , в частности его версия MASM 6.11+
Досовские варианты ассемблерных листингов представляют в своем большинстве
собрание весьма неудобоваримых сочинений , порой непонятных даже квалифицированным
программистам . Очень много меток , jmp-ов и прочей нечисти .
Но asm не стоит на месте , и в 6-м MASM-е макро-конструкции
становятся неотьемлемым инструментом разработки .
MASM ныне - такой-же легкий в чтении язык , что и си (чисто субьективное мнение).
Давайте рассмотрим некоторые си-шные комбинации и сравним их
с аналогичными в MASM-е .
IF - ELSE IF -
ELSE
The C version:
if ( var1 == var2 )
{
// Code goes here
}
else
if ( var1 == var3 )
{
// Code goes here
}
else
{
// Code goes here
}
|
The MASM version:
.if ( var1 == var2 )
; Code goes here
.elseif ( var1 == var3 )
; Code goes here
.else
; Code goes here
.endif
|
DO -
WHILE
The C version:
do
{
// Code goes here
}
while ( var1 == var2 );
|
The MASM version:
.repeat
; Code goes here
.until ( var1 != var2 )
|
WHILE
The C version:
while ( var1 == var2 )
{
// Code goes here
}
|
The MASM version:
.while ( var1 == var2 )
; Code goes here
.endw
|
Это все примеры рабочих конструкций , т.к. они чрезвычайно просты .
При компиляции MASM скомпилирует в коде все те же нужные метки ,
jmp-ы , cmp-конструкции .
Далее , рассмотрим такие ключевые слова MASM-а , как
PROTO и PROC.
Декларирование прототипов выполняется так - объявление функций :
;==================================
; Main Program Procedures
;==================================
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
WndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD
В каждой фунции задаются 4 параметра .
Вообще , каждая Windows API-функция имеет свой прототип .
Породив прототип , мы можем породить саму функцию с помощью
ключевого слова PROC :
;########################################################################
; WinMain Function
;########################################################################
WinMain PROC hInstance :DWORD,
hPrevInst :DWORD,
CmdLine :DWORD,
CmdShow :DWORD
;===========================
; We are through
;===========================
return msg.wParam
WinMain endp
;########################################################################
; End of WinMain Procedure
;########################################################################
|
При такой записи имеется доступ ко всем параметрам функции .
Не правда ли - слишком просто для Winmain ?
Приступим же к написанию основного игрового цикла .
Начнем с WinMain().
.CODE
start:
;==================================
; Получим экземпляр
; приложения
;==================================
INVOKE GetModuleHandle, NULL
MOV hInst, EAX
;==================================
; Как насчет командной строки ?
;==================================
INVOKE GetCommandLine
MOV CommandLine, EAX
;==================================
; Вызов WinMain
;==================================
INVOKE WinMain,hInst,NULL,CommandLine,SW_SHOWDEFAULT
;==================================
; Выход
;==================================
INVOKE ExitProcess,EAX
|
Обратите внимание на MOV EAX в конце - причина этого в том ,
что все виндовские функции фозвращают значеие в регистре EAX .
А теперь код :
;########################################################################
; WinMain Function
;########################################################################
WinMain PROC hInstance :DWORD,
hPrevInst :DWORD,
CmdLine :DWORD,
CmdShow :DWORD
;====================
; локальная переменная
;====================
LOCAL wc :WNDCLASS
;==================================================
; WNDCLASS - структура
;==================================================
MOV wc.style, CS_OWNDC
MOV wc.lpfnWndProc,OFFSET WndProc
MOV wc.cbClsExtra,NULL
MOV wc.cbWndExtra,NULL
m2m wc.hInstance,hInst ;<< NOTE: macro not mnemonic
INVOKE GetStockObject, BLACK_BRUSH
MOV wc.hbrBackground, EAX
MOV wc.lpszMenuName,NULL
MOV wc.lpszClassName,OFFSET szClassName
INVOKE LoadIcon, hInst, IDI_ICON ; icon ID
MOV wc.hIcon,EAX
INVOKE LoadCursor,NULL,IDC_ARROW
MOV wc.hCursor,EAX
;================================
; Register
;================================
INVOKE RegisterClass, ADDR wc
;===========================================
; окошко
;===========================================
INVOKE CreateWindowEx,NULL,
ADDR szClassName,
ADDR szDisplayName,
WS_POPUP OR WS_CLIPSIBLINGS OR \
WS_MAXIMIZE OR WS_CLIPCHILDREN,
0,0,640,480,
NULL,NULL,
hInst,NULL
;===========================================
; указатель на окошко
;===========================================
MOV hMainWnd, EAX
;====================================
; cursor
;====================================
INVOKE ShowCursor, FALSE
;===========================================
; выводим на экран
;===========================================
INVOKE ShowWindow, hMainWnd, SW_SHOWDEFAULT
;=================================
; инициализация Game
;=================================
INVOKE Game_Init
;========================================
; ошибка
;========================================
.IF EAX != TRUE
JMP shutdown
.ENDIF
;===================================
; цикл
;===================================
.WHILE TRUE
INVOKE PeekMessage, ADDR msg, NULL, 0, 0, PM_REMOVE
.IF (EAX != 0)
;===================================
; выход из цикла
;===================================
MOV EAX, msg.message
.IF EAX == WM_QUIT
;======================
; выход
;======================
JMP shutdown
.ENDIF
;===================================
; message
;===================================
INVOKE TranslateMessage, ADDR msg
INVOKE DispatchMessage, ADDR msg
.ENDIF
;================================
; вызов главного игрового цикла
;================================
INVOKE Game_Main
.ENDW
shutdown:
;=================================
; Shutdown the Game
;=================================
INVOKE Game_Shutdown
;=================================
; Show the Cursor
;=================================
INVOKE ShowCursor, TRUE
getout:
;===========================
; приплыли
;===========================
return msg.wParam
WinMain endp
;########################################################################
; End of WinMain Procedure
;########################################################################
|
Давайте проанализируем .
Обратите внимание : при инициализации локальной переменной в начале
функции не нужно никакой возни со стеком push/pop , как и в
ее конце - TEFAL всегда думает о нас ...
Это вам не ДОС-овский asm , где для перемещения данных из одного места
памяти в другое все нужно делать через - чуть не сказал , через что ,
через регистры и стек , конечно .
Далее , мы создаем окно и прячем курсор , ибо он нам в игре не нужен .
Показываем окно и вызываем Game_Init() .
Вообще , при отладке asm-процедур всегда нужно помнить ,
что должна быть одна точка входа и одна точка выхода .
Дальше идет обработка message loop , которые могут поступать откуда угодно .
Для их обработки мы используем обычную GetMessage() , поскольку
вопрос о скорости пока не стоит .
Если никаких сообщений не поступает , работает PeekMessage()
И в конце , когда мы получаем quit message , мы выходим из цикла .
Мы не будем рассматривать сам DirectX на уровне асм-а , а расмотрим
его на уровне основных концепций .
Прежде всего , необходимо понять саму концепцию Таблицы Виртуальных Функций .
Делается запрос в нее в форме смещения , и получается АДРЕС расположения
функции в ней . Т.е. под вызовом функции понимается обращение к таблице .
Которая УЖЕ существует . Адреса функций имеются в DirectX-библиотеке .
Далее , необходимо определить адрес обьекта , для которого вызывается
функция . Вычисляем виртуальный адрес и сохраняем в стеке
все параметры . Для этой цели существуют различные макросы , в частности ,
DD4INVOKE из 4-го еще директа .
Сначала определяем имя функции , затем имя обьекта , и затем параметры :
;========================================
; Создадим primary surface
;========================================
DD4INVOKE CreateSurface, lpdd, ADDR ddsd, ADDR lpddsprimary, NULL
В этом примере вызывается функция CreateSurface().
Устанавливаются указатели на обект и на поверхность .
На основе этого мы построим небольшую библиотеку .
Нам понадобятся функции для инициализации и выхода из игры ,
для определения формата пиксела , создания и прорисовки поверхностей ,
загрузки в нее битмапа .
Далее код функции инициализации :
;########################################################################
; DD_Init Procedure
;########################################################################
DD_Init PROC screen_width:DWORD, screen_height:DWORD, screen_bpp:DWORD
;=======================================================
; Устанавливается полноэкранный режим
;=======================================================
;=================================
; Local Variables
;=================================
LOCAL lpdd_1 :LPDIRECTDRAW
;=============================
; Создаем обьект
;=============================
INVOKE DirectDrawCreate, 0, ADDR lpdd_1, 0
;=============================
; Обработка ошибок
;=============================
.IF EAX != DD_OK
;======================
; Ошибка
;======================
INVOKE MessageBox, hMainWnd, ADDR szNoDD, NULL, MB_OK
;======================
; Выход
;======================
JMP err
.ENDIF
;=========================================
; Получим DirectDraw 4 object
;=========================================
DDINVOKE QueryInterface, lpdd_1, ADDR IID_IDirectDraw4, ADDR lpdd
;=========================================
; Получили ??
;=========================================
.IF EAX != DD_OK
;==============================
; Не - а
;==============================
INVOKE MessageBox, hMainWnd, ADDR szNoDD4, NULL, MB_OK
;======================
; Посему - вылет
;======================
JMP err
.ENDIF
;===================================================
; Установка cooperative level
;===================================================
DD4INVOKE SetCooperativeLevel, lpdd, hMainWnd, \
DDSCL_ALLOWMODEX OR DDSCL_FULLSCREEN OR \
DDSCL_EXCLUSIVE OR DDSCL_ALLOWREBOOT
;=========================================
; Получили ??
;=========================================
.IF EAX != DD_OK
;==============================
; Не - а
;==============================
INVOKE MessageBox, hMainWnd, ADDR szNoCoop, NULL, MB_OK
;======================
; Гуд-бай
;======================
JMP err
.ENDIF
;===================================================
; Установка Display Mode
;===================================================
DD4INVOKE SetDisplayMode, lpdd, screen_width, \
screen_height, screen_bpp, 0, 0
;=========================================
; Установили ??
;=========================================
.IF EAX != DD_OK
;==============================
; Не - а
;==============================
INVOKE MessageBox, hMainWnd, ADDR szNoDisplay, NULL, MB_OK
;======================
; Жаль
;======================
JMP err
.ENDIF
;================================
; screen info
;================================
m2m app_width, screen_width
m2m app_height, screen_height
m2m app_bpp, screen_bpp
;========================================
; Зададим параметры для surface
;========================================
DDINITSTRUCT OFFSET ddsd, SIZEOF(DDSURFACEDESC2)
MOV ddsd.dwSize, SIZEOF(DDSURFACEDESC2)
MOV ddsd.dwFlags, DDSD_CAPS OR DDSD_BACKBUFFERCOUNT;
MOV ddsd.ddsCaps.dwCaps, DDSCAPS_PRIMARYSURFACE OR \
DDSCAPS_FLIP OR DDSCAPS_COMPLEX
MOV ddsd.dwBackBufferCount, 1
;========================================
; Создадим primary surface
;========================================
DD4INVOKE CreateSurface, lpdd, ADDR ddsd, ADDR lpddsprimary, NULL
;=========================================
; Создали ??
;=========================================
.IF EAX != DD_OK
;==============================
; Не - а
;==============================
INVOKE MessageBox, hMainWnd, ADDR szNoPrimary, NULL, MB_OK
;======================
; Увы
;======================
JMP err
.ENDIF
;==========================================
; Попоробуем заполучить backbuffer
;==========================================
MOV ddscaps.dwCaps, DDSCAPS_BACKBUFFER
DDS4INVOKE GetAttachedSurface, lpddsprimary, ADDR ddscaps, ADDR lpddsback
;=========================================
; Заполучили ??
;=========================================
.IF EAX != DD_OK
;==============================
; Не - а
;==============================
INVOKE MessageBox, hMainWnd, ADDR szNoBackBuffer, NULL, MB_OK
;======================
; Зря
;======================
JMP err
.ENDIF
;==========================================
; Получим RGB format для surface
;==========================================
INVOKE DD_Get_RGB_Format, lpddsprimary
done:
;===================
; Все прекрасно
;===================
return TRUE
err:
;===================
; Ничего не прекрасно
;===================
return FALSE
DD_Init ENDP
;########################################################################
; END DD_Init
;########################################################################
|
Рассмотрим подробнее .
Для начала мы создаем т.н. default Direct Draw object с помощью
функции DirectDrawCreate() . Это еще не виртуальная функция .
В случае ошибки вызова мы просто прыгаем на метку err:
в конце функции
Далее устанавливаем режим экрана с помощью SetCooperativeLevel() и
SetDisplayMode() .
На следующем шаге создаем primary surface , и в случае успеха
создаем back buffer . При этом полученную структуру надобно очистить .
с помощью макроса DDINITSTRUCT .
После вызывается процедура для определения формата пиксела .
В следующей процедуре мы получим формат пиксела :
;########################################################################
; DD_Get_RGB_Format Procedure
;########################################################################
DD_Get_RGB_Format PROC surface:DWORD
;=========================================================
; Установим несколько глобальных переменных
;=========================================================
;====================================
; Local variables
;====================================
LOCAL shiftcount :BYTE
;================================
; получим surface despriction
;================================
DDINITSTRUCT ADDR ddsd, sizeof(DDSURFACEDESC2)
MOV ddsd.dwSize, sizeof(DDSURFACEDESC2)
MOV ddsd.dwFlags, DDSD_PIXELFORMAT
DDS4INVOKE GetSurfaceDesc, surface, ADDR ddsd
;==============================
; маски
;==============================
m2m mRed, ddsd.ddpfPixelFormat.dwRBitMask ; Red Mask
m2m mGreen, ddsd.ddpfPixelFormat.dwGBitMask ; Green Mask
m2m mBlue, ddsd.ddpfPixelFormat.dwBBitMask ; Blue Mask
;====================================
; определим red mask
;====================================
MOV shiftcount, 0
.WHILE (!(ddsd.ddpfPixelFormat.dwRBitMask & 1))
SHR ddsd.ddpfPixelFormat.dwRBitMask, 1
INC shiftcount
.ENDW
MOV AL, shiftcount
MOV pRed, AL
;=======================================
; определим green mask
;=======================================
MOV shiftcount, 0
.WHILE (!(ddsd.ddpfPixelFormat.dwGBitMask & 1))
SHR ddsd.ddpfPixelFormat.dwGBitMask, 1
INC shiftcount
.ENDW
MOV AL, shiftcount
MOV pGreen, AL
;=======================================
; определим blue mask
;=======================================
MOV shiftcount, 0
.WHILE (!(ddsd.ddpfPixelFormat.dwBBitMask & 1))
SHR ddsd.ddpfPixelFormat.dwBBitMask, 1
INC shiftcount
.ENDW
MOV AL, shiftcount
MOV pBlue, AL
;===========================================
; определим специальную переменную для 16 bit mode
;===========================================
.IF app_bpp == 16
.IF pRed == 10
MOV Is_555, TRUE
.ELSE
MOV Is_555, FALSE
.ENDIF
.ENDIF
done:
;===================
; все
;===================
return TRUE
DD_Get_RGB_Format ENDP
;########################################################################
; END DD_Get_RGB_Format
;########################################################################
|
Для начала , мы проинициализировали description structure
и делаем вызов из Direct Draw для получения surface description .
Возвращенные маски мы разместим в глобальных переменных для
их дальнейшего использования .
В нашем случае маски используются для того , чтобы поиметь
доступ к red, green, или blue - битам пиксела .
Следующая секция используется для определения числа битов для каждой
цветовой компоненты .
Например , если нам нужен цветовой режим 24 bpp, то
на каждую компоненту нужно отводить по 8 бит .
Делается это путем битового сдвига вправо и операции AND .
В случае установки 16-битного режима , переменная Is_555 становится TRUE .
Еще одна функция - для прорисовки текста .
Она использует GDI :
;########################################################################
; DD_Draw_Text Procedure
;########################################################################
DD_Draw_Text PROC surface:DWORD, text:DWORD, num_chars:DWORD,
x:DWORD, y:DWORD, color:DWORD
;=======================================================
; Эта функция будет рисовать текст
; с помощью GDI
;=======================================================
;===========================================
; Для начала получим DC
;===========================================
DDS4INVOKE GetDC, surface, ADDR hDC
;===========================================
; установим text color
;===========================================
INVOKE SetTextColor, hDC, color
INVOKE SetBkMode, hDC, TRANSPARENT
;===========================================
; запишем текст в позицию
;===========================================
INVOKE TextOut, hDC, x, y, text, num_chars
;===========================================
; release DC
;===========================================
DDS4INVOKE ReleaseDC, surface, hDC
done:
;===================
; все
;===================
return TRUE
DD_Draw_Text ENDP
;########################################################################
; END DD_Draw_Text
;########################################################################
|
Далее , получим Device Context - это первая вещь , которую нужно получить
при рисовании . Устанавливаем background mode и text color
с помощью все той же Windows GDI - и мы готовы рисовать текст
с помощью вызова TextOut().
Далее , запишем код для наложения bitmap .
Нам нужны 2 процедуры : для загрузки битмапа и его прорисовки .
В данном случае рассматривается уникальный формат файла .
Этот формат имеет 5 основных частей :
Width, Height, BPP, Size of Buffer, Buffer.
Первые 3 дают информацию о самом образе .
В данном случае применяется режимr 16 bpp .
;########################################################################
; Create_From_SFP Procedure
;########################################################################
Create_From_SFP PROC ptr_BMP:DWORD, sfp_file:DWORD, desired_bpp:DWORD
;=========================================================
; битмап будет загружаться из SFP file.
;=========================================================
;=================================
; Local Variables
;=================================
LOCAL hFile :DWORD
LOCAL hSFP :DWORD
LOCAL Img_Left :DWORD
LOCAL Img_Alias :DWORD
LOCAL red :DWORD
LOCAL green :DWORD
LOCAL blue :DWORD
LOCAL Dest_Alias :DWORD
;=================================
; Создадим этот SFP file
;=================================
INVOKE CreateFile, sfp_file, GENERIC_READ,FILE_SHARE_READ, \
NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,NULL
MOV hFile, EAX
;===============================
; error
;===============================
.IF EAX == INVALID_HANDLE_VALUE
JMP err
.ENDIF
;===============================
; Получим размер файла
;===============================
INVOKE GetFileSize, hFile, NULL
PUSH EAX
;================================
; error
;================================
.IF EAX == -1
JMP err
.ENDIF
;==============================================
; получим memory
;==============================================
INVOKE GlobalAlloc, GMEM_FIXED, EAX
MOV hSFP, EAX
;===================================
; error
;===================================
.IF EAX == 0
JMP err
.ENDIF
;===================================
; положим файл в память
;===================================
POP EAX
INVOKE ReadFile, hFile, hSFP, EAX, OFFSET Amount_Read, NULL
;====================================
; error
;====================================
.IF EAX == FALSE
;========================
; failed
;========================
JMP err
.ENDIF
;===================================
; Определим размер
;===================================
MOV EBX, hSFP
MOV EAX, DWORD PTR [EBX]
ADD EBX, 4
MOV ECX, DWORD PTR [EBX]
MUL ECX
PUSH EAX
;======================================
; Разберемся с типом буфера
;======================================
.IF desired_bpp == 16
;============================
; просто установим 16-bit
;============================
POP EAX
SHL EAX, 1
INVOKE GlobalAlloc, GMEM_FIXED, EAX
MOV EBX, ptr_BMP
MOV DWORD PTR [EBX], EAX
MOV Dest_Alias, EAX
;====================================
; error
;====================================
.IF EAX == FALSE
;========================
; облом
;========================
JMP err
.ENDIF
.ELSE
;========================================
; код для 24 bit
;========================================
;============================
; err
;============================
JMP err
.ENDIF
;====================================
; подготовим чтение
;====================================
MOV EBX, hSFP
ADD EBX, 10
MOV EAX, DWORD PTR[EBX]
MOV Img_Left, EAX
ADD EBX, 4
MOV Img_Alias, EBX
;====================================
; конвертация
;====================================
.WHILE Img_Left > 0
;==================================
; создадим color word
;==================================
.IF desired_bpp == 16
;==========================================
; прочтем по байту для blue, green , red
;==========================================
XOR ECX, ECX
MOV EBX, Img_Alias
MOV CL, BYTE PTR [EBX]
MOV blue, ECX
INC EBX
MOV CL, BYTE PTR [EBX]
MOV green, ECX
INC EBX
MOV CL, BYTE PTR [EBX]
MOV red, ECX
;=======================
; Img_Alias
;=======================
ADD Img_Alias, 3
;================================
; 555 или 565 ?
;================================
.IF Is_555 == TRUE
;============================
; 555
;============================
RGB16BIT_555 red, green, blue
.ELSE
;============================
; 565
;============================
RGB16BIT_565 red, green, blue
.ENDIF
;================================
; перевод в buffer
;================================
MOV EBX, Dest_Alias
MOV WORD PTR [EBX], AX
;============================
; делим на 2
;============================
ADD Dest_Alias, 2
.ELSE
;========================================
; код для 24 bit
;========================================
;============================
; err
;============================
JMP err
.ENDIF
;=====================
; Sub amount left by 3
;=====================
SUB Img_Left, 3
.ENDW
;====================================
; Почистим память
;====================================
INVOKE GlobalFree, hSFP
done:
;===================
; Вроде все
;===================
return TRUE
err:
;====================================
; Почистим SFP Memory
;====================================
INVOKE GlobalFree, hSFP
;===================
; Не получилось
;===================
return FALSE
Create_From_SFP ENDP
;########################################################################
; END Create_From_SFP
;########################################################################
|
Сначала создадим файл , а потом выделим для него память и прочтем данные .
После размещения файла в памяти определим размер буффера .
Далее - загрузочная функция .
Читаем 3 байта и определяем значение переменной ( 5-6-5 or 5-5-5 )
для буффера , после чего сохраняем ее там .
Каждый пиксел битмапа мы конвертируем , для чего используется макрос .
После конвертации мы возвращаем буфер с отконвертированными пикселами .
После загрузки в память битмап можно нарисовать в back buffer :
;########################################################################
; Draw_Bitmap Procedure
;########################################################################
Draw_Bitmap PROC surface:DWORD, bmp_buffer:DWORD, lPitch:DWORD, bpp:DWORD
;=========================================================
; Эта функция рисует BMP .
; используются width и height экрана
;=========================================================
;===========================
; Local Variables
;===========================
LOCAL dest_addr :DWORD
LOCAL source_addr :DWORD
;===========================
; инициализация
;===========================
MOV EAX, surface
MOV EBX, bmp_buffer
MOV dest_addr, EAX
MOV source_addr, EBX
MOV EDX, 480
;=================================
; 16 bit mode
;=================================
copy_loop1:
;=============================
; Setup num of bytes in width
; 640*2/4 = 320.
;=============================
MOV ECX, 320
;=============================
; установим source и dest
;=============================
MOV EDI, dest_addr
MOV ESI, source_addr
;======================================
; Move с помощью dwords
;======================================
REP movsd
;==============================
; variables
;==============================
MOV EAX, lPitch
MOV EBX, 1280
ADD dest_addr, EAX
ADD source_addr, EBX
;========================
; декремент
;========================
DEC EDX
;========================
; Конец ?
;========================
JNE copy_loop1
done:
;===================
; Да
;===================
return TRUE
err:
;===================
; Нет
;===================
return FALSE
Draw_Bitmap ENDP
;########################################################################
; END Draw_Bitmap
;########################################################################
|
Азбука говорит о том , что регистровый доступ наиболее быстрый ,
поэтому адреса располагаются в регистрах .
Затем вычисляется число WORDS , которое делится на 2 , и
получаем число DWORDS .
Вообще , используем 640 x 480 x 16 .
Число 320 разместим в регистре ECX.
Делаем классическое REP MOVSD .
Двигаем DWORD-ми, вычитаем из ECX по 1, сравнивая с ZERO ,
если нет , то MOVE A DWORD, до тех пор пока ECX не станет zero.
Все это здорово смахивает на си-шный for со счетчиком в ECX.
И все это повторим 480 раз - по количеству строк .
Теперь осталось все это вывести на экран .
Начнем с инициализации :
;########################################################################
; Game_Init Procedure
;########################################################################
Game_Init PROC
;=========================================================
; setup the game
;=========================================================
;============================================
; инициализация Direct Draw -- 640, 480, bpp
;============================================
INVOKE DD_Init, 640, 480, screen_bpp
;====================================
; error
;====================================
.IF EAX == FALSE
;========================
; failed
;========================
JMP err
.ENDIF
;======================================
; читаем bitmap и создаем buffer
;======================================
INVOKE Create_From_SFP, ADDR ptr_BMP_LOAD, ADDR szLoading, screen_bpp
;====================================
; error
;====================================
.IF EAX == FALSE
;========================
; failed
;========================
JMP err
.ENDIF
;===================================
; DirectDraw back buffer
;===================================
INVOKE DD_Lock_Surface, lpddsback, ADDR lPitch
;============================
; error
;============================
.IF EAX == FALSE
;===================
; err
;===================
JMP err
.ENDIF
;===================================
; рисуем bitmap
;===================================
INVOKE Draw_Bitmap, EAX, ptr_BMP_LOAD, lPitch, screen_bpp
;===================================
; back buffer
;===================================
INVOKE DD_Unlock_Surface, lpddsback
;============================
; error
;============================
.IF EAX == FALSE
;===================
; err
;===================
JMP err
.ENDIF
;=====================================
; loading
;======================================
INVOKE DD_Flip
;============================
; error
;============================
.IF EAX == FALSE
;===================
; err
;===================
JMP err
.ENDIF
done:
;===================
; да
;===================
return TRUE
err:
;===================
; нет
;===================
return FALSE
Game_Init ENDP
;########################################################################
; END Game_Init
;########################################################################
|
В этой функции мы инициализируем Direct Draw . В случае успеха
загружается битмап . Далее беремся за back buffer и пишем в него
наш битмап . После этого делаем флиппинг видимого и невидимого
буферов .
Далее - функция WndProc , которая , как известно , обрабатывает сообщения :
;########################################################################
; Main Window Callback Procedure -- WndProc
;########################################################################
WndProc PROC hWin :DWORD,
uMsg :DWORD,
wParam :DWORD,
lParam :DWORD
.IF uMsg == WM_COMMAND
;===========================
; без меню
;===========================
.ELSEIF uMsg == WM_KEYDOWN
;=======================================
; не будем программировать Direct input
;=======================================
MOV EAX, wParam
.IF EAX == VK_ESCAPE
;===========================
; грохнем application
;===========================
INVOKE PostQuitMessage,NULL
.ENDIF
;==========================
; processed it
;==========================
return 0
.ELSEIF uMsg == WM_DESTROY
;===========================
; грохнем application
;===========================
INVOKE PostQuitMessage,NULL
return 0
.ENDIF
;=================================================
; procedure handle the message
;=================================================
INVOKE DefWindowProc,hWin,uMsg,wParam,lParam
RET
WndProc endp
;########################################################################
; End of Main Windows Callback Procedure
;########################################################################
|
Пока мы имеем дело только с 2-мя сообщениями - WM_KEYDOWN и WM_DESTROY .
Все остальные сообщения по умолчанию обрабатываются функцией
DefWindowProc().
И далее - shutdown-код :
;########################################################################
; Game_Shutdown Procedure
;########################################################################
Game_Shutdown PROC
;===========================
; Shutdown DirectDraw
;===========================
INVOKE DD_ShutDown
;==========================
; Free the bitmap memory
;==========================
INVOKE GlobalFree, ptr_BMP_LOAD
done:
;===================
; We completed
;===================
return TRUE
err:
;===================
; We didn't make it
;===================
return FALSE
Game_Shutdown ENDP
;########################################################################
; END Game_Shutdown
;########################################################################
|
Итак , мы выгружаем Direct Draw library ,
и освобождаем память .
|