OpenGL и структура команд процессора Intel
Доброго времени суток, уважаемые подписчики.
Один из наших проектов игра. Так как большинство выбравших игру интересует трёхмерное программирование, начнём именно с этого. Писать 3D среду самому долго и не совсем разумно. Direct3D из-за его com структуры является достаточно сложным в понимании (а также программировании на ассемблере), поэтому я выбрал OpenGL для нашего проекта. Сразу хочу сказать, что писать мы будем не игру определённого жанра, а 3D движок, на котором можно будет написать игру любого жанра. Для использования функций OpenGL в ассемблере Вам понадобятся следующие компоненты:
- файлы-дополнения OpenGL
- Структуры и константы OpenGL
- библиотеки импорта OpenGL
Создание OpenGL окна: OpenGL для сообщение с внешним миром использует так называемые Rendering Device Contexts (rDC), которые, в свою очередь, могут присвоены hDC окна windows. OpenGL создаёт виртуальную hDC на которую и происходит выдача изображения OpenGL, поэтому чтобы что-либо увидеть нам понадобится постоянно вызывать SwapBuffers. Достаточно важный момент это изменение формата пикселей окна, в котором мы хотим увидеть OpenGL изображение. Формат окна можно изменять лишь один раз за всю жизнь окна, поэтому чтоб повторно изменить этот формат надо убить текущее окно, создать такое же и уже ему установить нужный формат!
Для начала попытаемся сделать обычное OpenGL приложение без каких либо объектов. Посмотрим, что мы должны сделать:
- Создать окно
- Убедиться, что формат пикселей нам подходит
- Создать контекст OpenGL
- Связать его с нашим окном
- обеспечить постоянное обновление экрана (независимо от процедуры окна!)
- при выходе закрыть контекст OpenGL
Думаю, создание окна уже не является большой проблемой, поэтому сразу рассмотрим переключение формата пикселей. Формат пикселей окна может быть описан структурой PIXELFORMATDESCRIPTOR, которая выглядит следующим образом:
PIXELFORMATDESCRIPTOR STRUCT
nSize dw ?
nVersion dw ?
dwFlags dd ?
iPixelType db ?
cColorBits db ?
cRedBits db ?
cRedShift db ?
cGreenBits db ?
cGreenShift db ?
cBlueBits db ?
cBlueShift db ?
cAlphaBits db ?
cAlphaShift db ?
cAccumBits db ?
cAccumRedBits db ?
cAccumGreenBits db ?
cAccumBlueBits db ?
cAccumAlphaBits db ?
cDepthBits db ?
cStencilBits db ?
cAuxBuffers db ?
iLayerType db ?
bReserved db ?
dwLayerMask dd ?
dwVisibleMask dd ?
dwDamageMask dd ?
PIXELFORMATDESCRIPTOR ENDS
|
На первый взгляд структура кажется чем-то огромным и не понятным, но спешу вас заверить, понимать её полностью нам не понадобится, нам понадобятся лишь те пункты, которые знакомы почти каждому. После заполнения структуры передаём её адрес вместе с handle нашего окна. Выполнение следующих задач, создать контекст OpenGL и связать его с нашим окном, полностью ложиться на библиотеку OpenGL, нам же достаточно вызвать нужные функции и проверить результат на ошибки. Следующим шагом нам нужно обеспечить независимый цикл, обновляющий содержание нашего окна. Логично было бы создать отдельный тред, но мы поступим немного по-другому. У нас есть цикл, который принимает сообщения окна и передаёт их ему, так почему же нельзя параллельно обновлять содержимое окна? При этом разумнее использовать PeekMessage вместо GetMessage, так как вторая возвращает управление только если получает сообщение окну, а PeekMessage возвращает управление в любом случае ( нулевое значение в случае отсутствия сообщений). Закрытие контекста производиться функциями OpenGL. Но достаточно теории, посмотрим как всё это выглядит на практике:
include kernel32.inc
include user32.inc
include gdi32.inc
include opengl32.inc
include glu32.inc
include def32.inc
include macros.inc
include opengldef.inc
.386
.model flat
.data
ClearDepth dq 1.0f
fovy dq 45.0f
zFar dq 100.0f
zNear dq 0.1f
aspect dq ?
alpha dd 0.5f
hWnd dd ?
hDC dd ?
glhDC dd ?
PixelFormat dd ?
Width_ dd 300
Height_ dd 200
wc WNDCLASSEX <size wc,cs_hredraw or cs_vredraw or CS_OWNDC,offset WndProc,0,0,?,?,?,0,0,offset ClassName,0>
pxl PIXELFORMATDESCRIPTOR <028h,1,PFD_DOUBLEBUFFER or PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0>
msg_ MSG <?>
ClassName db 'OpenGL',0
.code
_start:
null ebx
run Getmodulehandle, ebx
mov wc.hInstance,eax
mov esi,eax
run LoadIcon, ebx,IDI_APPLICATION
mov wc.hIcon,eax
run LoadCursor, ebx, IDC_ARROW
mov wc.hCursor,eax
run RegisterClassEx, offset wc
mov ecx,CW_USEDEFAULT
;Из-за большого кол-ва параметров я решил записать обычным способом
push ebx
push esi
push ebx
push ebx
push Height_
push Width_
push ecx
push ecx
push WS_OVERLAPPEDWINDOW or WS_CLIPSIBLINGS or WS_CLIPCHILDREN
push offset ClassName
push offset ClassName
push WS_EX_APPWINDOW or WS_EX_WINDOWEDGE
run CreateWindowEx
mov hWnd,eax
mov esi,eax
run GetDC, eax
mov hDC,eax
run ChoosePixelFormat, eax, offset pxl
mov PixelFormat,eax
run SetPixelFormat, hDC, eax, offset pxl
run wglCreateContext, hDC
mov glHDC,eax
run wglMakeCurrent, hDC, eax
run ShowWindow, esi, SW_SHOWNORMAL
run SetForegroundWindow, esi
run SetFocus, esi
run ReSizeGLScene ;Процедура, сообщающая OpenGL размеры окна
run InitGL ;Включение нужных нам функций OpenGL
mov edi,offset msg_
main_loop:
run PeekMessage, edi, ebx, ebx, ebx, PM_REMOVE
cmp msg_.message,WM_QUIT
jz ProgramEnd
test eax,eax
jz glReNew
run TranslateMessage, edi
run DispatchMessage, edi
jmp short main_loop
glReNew:
run DrawGLScene ;Перерисовка сцены внутри OpenGL
run SwapBuffers, HDC ;Вывод картинки в окно
jmp short main_loop
ProgramEnd:
run KillGLWindow ;Деинициализация OpenGL
run ExitProcess, ebx
WndProc proc
push ebp
mov ebp,esp
pusha
null ebx
mov edi,[ebp+08h]
mov eax,[ebp+0ch]
cmp eax,WM_DESTROY
je close_proc
cmp eax,WM_SYSCOMMAND
je syscommand_proc
cmp eax,WM_KEYDOWN
je key_proc
Pass_Message:
popa
leave
jmp DefWindowProc
syscommand_proc:
mov eax,[ebp+10h]
cmp eax,SC_SCREENSAVE
je endWndProc
cmp eax,SC_MONITORPOWER
je endWndProc
jmp short Pass_Message
key_proc:
cmp dword ptr [ebp+10h], VK_ESCAPE
jne EndWndProc
close_proc:
run PostQuitMessage, ebx
EndWndProc:
popa
leave
ret 16
WndProc endp
ReSizeGLScene proc
cmp height_,ebx
jne h_ok
inc height_
h_ok:
run glViewPort, ebx, ebx, width_, height_
run glMatrixMode, GL_PROJECTION
run glLoadIdentity
fild width_
fild height_
fdivp st(1),st(0)
fstp aspect
pushq zFar
pushq zNear
pushq aspect
pushq fovy
run gluPerspective
run glMatrixMode, GL_MODELVIEW
run glLoadIdentity
ret
ReSizeGLScene endp
KillGLWindow proc
run wglMakeCurrent, ebx, ebx
run wglDeleteContext, glhDC
run ReleaseDC, hWnd, hDC
run DestroyWindow, hWnd
ret
KillGLWindow endp
InitGL proc
run glShadeModel, GL_SMOOTH
run glClearColor, ebx, ebx, ebx, alpha
pushq ClearDepth
run glClearDepth
run glEnable, GL_DEPTH_TEST
run glDepthFunc, GL_LEQUAL
run glHint, GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST
ret
InitGL endp
DrawGLScene proc
run glClear, GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT
run glLoadIdentity
ret
DrawGLScene endp
end _start
|
К сожалению, подробно рассматривать функции OpenGL мы не будем, так как для достижения нашей цели нам будет достаточно знать многие процедуры очень поверхностно (к тому же их название в большинстве случаев говорит само за себя). Для тех кто хочет подробнее заниматься OpenGL могу посоветовать книгу Дейва Шрайнера "OpenGL - официальный справочник" или ранее мной упомянутый opengl.hlp
Другим нашим проектом является лидер голосования, дизассемблер. К сожалению в этом случае нет никаких вступлений или заранее кем-то придуманных и написанных функций, поэтому начинать надо с самого сложного. Думаю многие уверены, что дизассебляция - процесс очень лёгкий, берём таблицу имён и по коду команды находим её имя... К сожалению, всё не так просто, многие команды процессора имею несколько кодов, причём некоторые одинаковы с другими командами (три бита в поля в байте операндов различают их). Поэтому начнём мы с корней всех наших будущих проблем, с структуры команды Intel совместимого процессора!
Команды бывают разной длинны, причём длинна их неограниченна и с появлением всё новых инструкций растёт и длинна команды. Все команды объединяет лишь то, что минимум первые 4 бита являются статической командой (возможно я очень не прав, но статической командой я называю ту часть команды, которая при любых параметрах остаются та же). Перед тем как мы рассмотрим структуры и примеры команд давайте заранее договоримся о названиях полей команд и их битах:
- Код = несколько байт (или бит) статической команды.
- ModR/M = 1 байт, описывающий операнды
- SIB = 1 байт, 32 битное расширение адресации
- w = бит, принимающий значение 0, ели команда работает с байтом, и 1, если с (двойным)словом
- s = бит, при помещения константы в 16/32 битный регистр. Если 0, то константа указана полностью, 1, если указан лишь младший байт.
- d = бит, указывающий направление действия
- reg = выбор одного из восьми регистров (см. ниже)
reg | 8 бит | 16 бит | 32 бита | FPU | mmx | sse |
000 | al | ax | eax | st(0) | mm0 | xmm0 |
001 | cl | cx | ecx | st(1) | mm1 | xmm1 |
010 | dl | dx | edx | st(2) | mm2 | xmm2 |
011 | bl | bx | ebx | st(3) | mm3 | xmm3 |
100 | ah | sp | esp | st(4) | mm4 | xmm4 |
101 | ch | bp | ebp | st(5) | mm5 | xmm5 |
110 | dl | si | esi | st(6) | mm6 | xmm6 |
111 | bh | di | edi | st(7) | mm7 | xmm7 |
- cond = условие действия (см. ниже) (для команд j**, cmov**, set**, fcmov**)
0000 | o |
0001 | no |
0010 | c/b/nae |
0011 | nc/nb/ae |
0100 | e/z |
0101 | ne/nz |
0110 | be/na |
0111 | nbe/a |
1000 | s |
1001 | ns |
1010 | p/pe |
1011 | np/po |
1100 | l/nge |
1101 | nl/ge |
1110 | le/ng |
1111 | lne/g |
- im = константа
- i8/i16/i32 = константа заданного размера
- ac = eax,ax,al
- r = регистр любого размера
- r8/r16/r32 = регистр указанного размера
- sr = сегментный регистр
- m = операнд в памяти
- mm = mmx
- xmm = sse
- st0 = st(0)
- sti = st(i)
Сегодня мы рассмотрим три самых простых, однобайтных типа команд. Первый встречается достаточно часто, это команды изменения флагов, коррекции чисал посе различных действий, то есть те команды, которые не имеют параметров, они содержал лишь 8 байтный код себя. В большинстве случаев они работают с регистром флагов или ac. Декодирование таких команд можно производить командой xlat, которая также является однобайтной, безоперандной командой.
Другой однобайтный вид команд выглядит следующим образом:
код команды(5 бит) | reg(3 бита) |
Обычно это команды, выполняющие ни от чего не зависящее действие с одним из регистров, примерами таких команд являются inc (40h or reg), dec (48 or reg), pop (58h or reg). Особым случаем является команда xchg, если один из операндов eax, то команда занимает 1 байт (90h or reg), если нет, то 2 (86h or W; reg shl 3).
В следующем уроке составим полную таблицу возможных команд...
Всем, кто программирует на ассемблере, C(++) или Delphi советую заглянуть сюда
На сегодня это всё. Если есть какие вопросы, пишите. Пишите на Dark_Lord@RusFAQ.ru или Dark_Lord@land.ru.
Или свяжитесь со мной по ICQ, мой номер 126222874!
|