Я пытаюсь написать pong на ассемблере, чтобы лучше понять, что на самом деле происходит на моем компьютере.
Я отключил свой антивирус, который помечал бы его как вредоносный, и перезагрузил компьютер с отключенным антивирусом, и код работал раньше, и я ничего не менял. Это могло быть по какой-то глупой оплошности, но, как обычно программисту высокого уровня, действительно странно, что код просто перестал работать после перезагрузки моего компьютера. Я не получаю никаких ошибок.
я получил
hello.asm(51): предупреждение A6001: нет возврата из процедуры
это как ошибка, но я мог игнорировать это раньше, и он компилируется, и, как я уже сказал, он работал раньше.
.386
.model flat, stdcall
option casemap:none
include C:\MASM32\include\masm32rt.inc
includelib user32.lib
includelib kernel32.lib
.data
txClass db "MyWinClass", 0
wcx WNDCLASSEX <WNDCLASSEX, CS_HREDRAW or CS_VREDRAW, WndProc, 0, 0, 1, 2, 3, COLOR_BTNFACE+1, 0, txClass, 4>
rect RECT <20, 0, 40, 100> ; Rectangle to hold the client area dimensions
ball RECT <200, 200, 210,210> ; Rectangle to hold the client area dimensions
ballMX DWORD 5
ballMY DWORD 5
.data? ; uninitialised data - use for handles etc
hMenu dd ?
ps PAINTSTRUCT<?>
dwStartTime DWORD ?
dwElapsedTime DWORD ?
hWnd2 DWORD ?
.code
WinMain proc uses ebx
LOCAL msg:MSG
mov ebx, offset wcx
wc equ [ebx.WNDCLASSEX]
mov wc.hInstance, rv(GetModuleHandle, 0)
mov wc.hIcon, rv(LoadIcon, NULL, IDI_APPLICATION)
mov wc.hIconSm, eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx, NULL, wc.lpszClassName, chr$("Pong"),
WS_OVERLAPPEDWINDOW or WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, wc.hInstance, NULL
mov hWnd2, eax
invoke SetTimer, hWnd2, 1, 8, NULL
.While 1
invoke GetMessage, ADDR msg, NULL, 0, 0
.Break .if !eax
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.Endw
exit msg.wParam
WinMain endp
WndProc proc uses esi edi ebx hWnd, uMsg, wParam:WPARAM, lParam:LPARAM
LOCAL hdc: HDC
SWITCH uMsg
CASE WM_CREATE
ret
CASE WM_KEYDOWN
mov eax, wParam
SWITCH eax
CASE VK_S
add dword ptr [rect.top], 5
add dword ptr [rect.bottom], 5
CASE VK_W
sub dword ptr [rect.top], 5
sub dword ptr [rect.bottom], 5
ret
ret
ENDSW
invoke InvalidateRect, hWnd, NULL, TRUE ; Force the window to repaint
ret
CASE WM_TIMER
push ebx
mov eax, ball.bottom
mov ebx, ps.rcPaint.bottom
cmp eax,ebx
jb cont
neg ballMY
cont:
mov eax,ballMY
add ball.top, eax
add ball.bottom, eax
xor ebx, ebx
mov eax, ball.right
mov ebx, ps.rcPaint.right
cmp eax,ebx
jb cont2
neg ballMX
cont2:
mov eax,ballMX
add ball.left, eax
add ball.right, eax
pop ebx
invoke InvalidateRect, hWnd, NULL, TRUE ; Request a repaint
CASE WM_PAINT
invoke BeginPaint, hWnd, ADDR ps ; Start painting the window
mov hdc, eax ; Store the device context handle
invoke CreateSolidBrush, 0000000
invoke FillRect, hdc, ADDR ps.rcPaint, eax
invoke CreateSolidBrush, 0FFFFFFh
invoke FillRect, hdc, ADDR rect, eax
invoke CreateSolidBrush, 0FFFFFFh
invoke FillRect, hdc, ADDR ball, eax
invoke EndPaint, hWnd, ADDR ps ; End painting
ret
CASE WM_DESTROY
invoke PostQuitMessage, NULL
ENDSW
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
WndProc endp
end WinMain
Я добавил вызов DeleteObject, вызов hBlackBrush DeleteObject, вызов hWhiteBrush DeleteObject, hWhiteBrush2, чтобы избежать утечки ресурсов, и добавил возвращаемое значение в WM_CREATE CASE WM_CREATE mov eax, 0 ret также добавил случай по умолчанию DEFAULT ; Обрабатывать случаи необработанных сообщений, вызывать DefWindowProc, hWnd, uMsg, wParam, lParam mov eax, 0 ret, но он по-прежнему не создает окно, но компилируется, и если я его запускаю, он не падает
В случае по умолчанию, когда вы вызываете DefWindowProc
, вы должны вернуть значение, возвращаемое этой функцией, поэтому оно должно следовать за ret
без mov eax, 0
.
RegisterClassEx
не удалось
invoke RegisterClassEx, addr wc
Заменен исходный код новым кодом, который заполняет структуру WNDCLASSEX необходимыми переменными.
Запуск кода создает это окно
pong.asm
.386
.model flat, stdcall
option casemap:none
include C:\MASM32\include\masm32rt.inc
includelib user32.lib
includelib kernel32.lib
.data
txClass db "MyWinClass", 0
rect RECT <20, 0, 40, 100> ; Rectangle to hold the client area dimensions
ball RECT <200, 200, 210,210> ; Rectangle to hold the client area dimensions
ballMX DWORD 5
ballMY DWORD 5
.data? ; uninitialised data - use for handles etc
hMenu dd ?
ps PAINTSTRUCT<?>
dwStartTime DWORD ?
dwElapsedTime DWORD ?
hWnd2 DWORD ?
.code
WinMain proc hInst :DWORD,
hPrevInst :DWORD,
CmdLine :DWORD,
CmdShow :DWORD
;====================
; Put LOCALs on stack
;====================
LOCAL wc :WNDCLASSEX
LOCAL msg :MSG
LOCAL Wwd :DWORD
LOCAL Wht :DWORD
LOCAL Wtx :DWORD
LOCAL Wty :DWORD
;==================================================
; Fill WNDCLASSEX structure with required variables
;==================================================
mov wc.cbSize, sizeof WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW \
or CS_BYTEALIGNWINDOW
mov wc.lpfnWndProc, offset WndProc
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
m2m wc.hInstance, hInst ;<< NOTE: macro not mnemonic
mov wc.hbrBackground, COLOR_BTNFACE+1
mov wc.lpszMenuName, NULL
mov wc.lpszClassName, offset txClass
invoke LoadIcon,hInst,500 ; icon ID
mov wc.hIcon, eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor, eax
mov wc.hIconSm, 0
invoke RegisterClassEx, ADDR wc
test eax, eax
jnz regOk
invoke MessageBox,0,SADD("RegistrationFailed"),
SADD("Oops"),
MB_OK or MB_ICONINFORMATION
jmp done
regOk:
invoke CreateWindowEx, NULL, wc.lpszClassName, chr$("Pong"),
WS_OVERLAPPEDWINDOW or WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, wc.hInstance, NULL
mov hWnd2, eax
invoke ShowWindow,hWnd2,SW_SHOWNORMAL
invoke UpdateWindow,hWnd2
invoke SetTimer, hWnd2, 1, 8, NULL
.While 1
invoke GetMessage, ADDR msg, NULL, 0, 0
.Break .if !eax
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.Endw
done:
exit msg.wParam
WinMain endp
WndProc proc uses esi edi ebx hWnd, uMsg, wParam:WPARAM, lParam:LPARAM
LOCAL hdc: HDC
SWITCH uMsg
CASE WM_CREATE
ret
CASE WM_KEYDOWN
mov eax, wParam
SWITCH eax
CASE VK_S
add dword ptr [rect.top], 5
add dword ptr [rect.bottom], 5
CASE VK_W
sub dword ptr [rect.top], 5
sub dword ptr [rect.bottom], 5
ret
ret
ENDSW
invoke InvalidateRect, hWnd, NULL, TRUE ; Force the window to repaint
ret
CASE WM_TIMER
push ebx
mov eax, ball.bottom
mov ebx, ps.rcPaint.bottom
cmp eax,ebx
jb cont
neg ballMY
cont:
mov eax,ballMY
add ball.top, eax
add ball.bottom, eax
xor ebx, ebx
mov eax, ball.right
mov ebx, ps.rcPaint.right
cmp eax,ebx
jb cont2
neg ballMX
cont2:
mov eax,ballMX
add ball.left, eax
add ball.right, eax
pop ebx
invoke InvalidateRect, hWnd, NULL, TRUE ; Request a repaint
CASE WM_PAINT
invoke BeginPaint, hWnd, ADDR ps ; Start painting the window
mov hdc, eax ; Store the device context handle
invoke CreateSolidBrush, 0000000
invoke FillRect, hdc, ADDR ps.rcPaint, eax
invoke CreateSolidBrush, 0FFFFFFh
invoke FillRect, hdc, ADDR rect, eax
invoke CreateSolidBrush, 0FFFFFFh
invoke FillRect, hdc, ADDR ball, eax
invoke EndPaint, hWnd, ADDR ps ; End painting
ret
CASE WM_DESTROY
invoke PostQuitMessage, NULL
ENDSW
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
WndProc endp
end WinMain
В C:\masm32\tools
и C:\masm32\examples
есть много примеров, которые используют этот код для правильной регистрации класса Windows.
RegisterWinClass proc lpWndProc:DWORD, lpClassName:DWORD,
Icon:DWORD, Cursor:DWORD, bColor:DWORD
LOCAL wc:WNDCLASSEX
mov wc.cbSize, sizeof WNDCLASSEX
mov wc.style, CS_BYTEALIGNCLIENT or \
CS_BYTEALIGNWINDOW
m2m wc.lpfnWndProc, lpWndProc
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
m2m wc.hInstance, hInstance
m2m wc.hbrBackground, bColor
mov wc.lpszMenuName, NULL
m2m wc.lpszClassName, lpClassName
m2m wc.hIcon, Icon
m2m wc.hCursor, Cursor
m2m wc.hIconSm, Icon
invoke RegisterClassEx, ADDR wc
ret
RegisterWinClass endp
Я очень благодарен за этот ответ, но по какой-то причине он все еще не показывает никаких окон. Я понятия не имею, почему он ничего не показывает. Знаете ли вы, почему код работает на вашем компьютере, а на моем не отображаются окна?
Обновил код... ошибка invoke RegisterClassEx, addr wc
. Вероятно, следует добавить проверки ошибок вокруг большего количества вызовов оконных функций.
но я точно скопировал код, и он работал, прежде чем я перезагрузил свой компьютер
Скорее всего, нам обоим повезло с окном, появляющимся случайным образом. Вероятно, какое-то причудливое внутреннее состояние программы. Работает ли новый код сейчас? Если это так, вы можете сузить его, чтобы найти причину сбоя. Спасибо.
@Jannis: Программы с ошибками, которые оставляют некоторые вещи неинициализированными, не обязательно сломаются, они могут работать в зависимости от случайного состояния, например, того, что попадает в определенный участок памяти стека. В ассемблере очень легко допустить ошибку, например, оставить что-то неинициализированным, в отличие от C. Поэтому тот факт, что он работал раньше, не доказывает, что он был правильным и перспективным. Да, перезагрузки может быть достаточно, чтобы повторно рандомизировать адрес или размер среды или что-то, что заставляло вашу программу работать.
Похоже, вы не устанавливаете возвращаемое значение из вашего WndProc. Например.
WM_CREATE
требует, чтобы вы вернули 0, чтобы продолжить создание окна. У вас также есть утечка ресурсов в вашем обработчикеWM_PAINT
.