А мы и не останавливались ... Рассмотрим конструкции MASM , которые являются удобоваримыми в сравнительном смысле с аналогичными конструкциями на си . Затем рассмотрим основной цикл в главных виндовских процедурах . После мы обратим свои взоры на Direct Draw . Поняв , как он работает , мы сможем построить свою собственную Direct Draw Library . Затем мы построим bitmap file library . И в конце можно будет написать небольшую Loading Game .
Для компиляции нам потребуется MASM32 , в частности его версия MASM 6.11+
Досовские варианты ассемблерных листингов представляют в своем большинстве собрание весьма неудобоваримых сочинений , порой непонятных даже квалифицированным программистам . Очень много меток , jmp-ов и прочей нечисти . Но asm не стоит на месте , и в 6-м MASM-е макро-конструкции становятся неотьемлемым инструментом разработки .
MASM ныне - такой-же легкий в чтении язык , что и си (чисто субьективное мнение). Давайте рассмотрим некоторые си-шные комбинации и сравним их с аналогичными в MASM-е .
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 |
The C version: do { // Code goes here } while ( var1 == var2 ); |
The MASM version: .repeat ; Code goes here .until ( var1 != var2 ) |
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 , и освобождаем память .