Перейти на главную   
  helloworld.ru - документация и книги по программированию  
helloworld.ru - документация и книги по программированию    
    главная     хостинг     создание сайтов    
Поиск по сайту:  
Смотрите также
Языки программирования
C#
MS Visual C++
Borland C++
C++ Builder
Visual Basic
Quick Basic
Turbo Pascal
Delphi
JavaScript
Java
PHP
Perl
Assembler
AutoLisp
Fortran
Python
1C

Интернет-технологии
HTML
VRML
HTTP
CGI
FTP
Proxy
DNS
протоколы TCP/IP
Apache

Web-дизайн
HTML
Дизайн
VRML
PhotoShop
Cookie
CGI
SSI
CSS
ASP
PHP
Perl

Программирование игр
DirectDraw
DirectSound
Direct3D
OpenGL
3D-графика
Графика под DOS

Алгоритмы
Численные методы
Обработка данных

Системное программирование
Драйверы

Базы данных
MySQL
SQL

Другое

Хостинг


Друзья
demaker.ru
Реклама

Лучший хостинг. Аренда серверов




helloworld.ru

Программирование игр на Ассемблере
(часть II)

На чем мы остановились?

Синтаксис MASM

Основной игровой цикл

Связь с Direct Draw

Наша Direct Draw Library

Наша Bitmap Library

Game

На чем мы остановились?

А мы и не останавливались ... Рассмотрим конструкции MASM , которые являются удобоваримыми в сравнительном смысле с аналогичными конструкциями на си . Затем рассмотрим основной цикл в главных виндовских процедурах . После мы обратим свои взоры на Direct Draw . Поняв , как он работает , мы сможем построить свою собственную Direct Draw Library . Затем мы построим bitmap file library . И в конце можно будет написать небольшую Loading Game .

Для компиляции нам потребуется MASM32 , в частности его версия MASM 6.11+

Синтаксис MASM

Досовские варианты ассемблерных листингов представляют в своем большинстве собрание весьма неудобоваримых сочинений , порой непонятных даже квалифицированным программистам . Очень много меток , 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 , мы выходим из цикла .

Связь с Direct Draw

Мы не будем рассматривать сам DirectX на уровне асм-а , а расмотрим его на уровне основных концепций .

Прежде всего , необходимо понять саму концепцию Таблицы Виртуальных Функций . Делается запрос в нее в форме смещения , и получается АДРЕС расположения функции в ней . Т.е. под вызовом функции понимается обращение к таблице . Которая УЖЕ существует . Адреса функций имеются в DirectX-библиотеке .

Далее , необходимо определить адрес обьекта , для которого вызывается функция . Вычисляем виртуальный адрес и сохраняем в стеке все параметры . Для этой цели существуют различные макросы , в частности , DD4INVOKE из 4-го еще директа .

Сначала определяем имя функции , затем имя обьекта , и затем параметры :

    ;========================================
        ; Создадим primary surface
        ;========================================
        DD4INVOKE CreateSurface, lpdd, ADDR ddsd, ADDR lpddsprimary, NULL

В этом примере вызывается функция CreateSurface(). Устанавливаются указатели на обект и на поверхность . На основе этого мы построим небольшую библиотеку .

Наша Direct Draw Library

Нам понадобятся функции для инициализации и выхода из игры , для определения формата пиксела , создания и прорисовки поверхностей , загрузки в нее битмапа .

Далее код функции инициализации :


;########################################################################
; 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 .

Наша Bitmap Library

Нам нужны 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

  Начнем с инициализации :


;########################################################################
; 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 , и освобождаем память .

 











helloworld.ru © 2001-2016
Все права защищены
Rambler's Top100 TopList Rambler's Top100