Hello, I have begun to learn assembly and I'm trying to understand the following code:
.386
.model flat, stdcall
option casemap :none
include E:\masm32\INCLUDE\windows.inc
include E:\masm32\INCLUDE\kernel32.inc
include E:\masm32\INCLUDE\user32.inc
includelib E:\masm32\LIB\kernel32.lib
includelib E:\masm32\LIB\user32.lib
WinMain proto :DWORD, :DWORD, :DWORD, :DWORD
.data
ClassName db "WinClass", 0
AppName db "Simple Window", 0
.data?
hInstance HINSTANCE ?
.code
start:
invoke GetModuleHandle, NULL
mov hInstance, eax
invoke WinMain, hInstance, NULL, NULL, 0
invoke ExitProcess, eax
WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR,
CmdShow:DWORD
local wc:WNDCLASSEX
local msg:MSG
local hwnd:HWND
mov wc.cbSize, SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, offset WndProc
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
push hInstance
pop wc.hInstance
mov wc.hbrBackground, COLOR_WINDOW+1
mov wc.lpszMenuName, NULL
mov wc.lpszClassName, offset ClassName
invoke LoadIcon, NULL, IDI_APPLICATION
mov wc.hIcon, eax
mov wc.hIconSm, eax
invoke LoadCursor, NULL, IDC_ARROW
mov wc.hCursor, eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx, 0, addr ClassName, addr AppName, WS_OVERLAPPEDWINDOW\
or WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,\
CW_USEDEFAULT, NULL, NULL, hInst, NULL
mov hwnd, eax
.while TRUE
invoke GetMessage, addr msg, NULL, 0, 0
.break .if (!eax)
invoke TranslateMessage, addr msg
invoke DispatchMessage, addr msg
.endw
mov eax, msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.if uMsg == WM_DESTROY
invoke PostQuitMessage, 0
.else
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.endif
xor eax, eax
ret
WndProc endp
end start
As I understand it, the ret mnemonic returns to the next instruction that comes after the called function.
Why isn't there a ret mnemonic after the CreateWindowsEx function and the GetMessage, TranslateMessage, and DispatchMessage functions?
On the other hand, it's there after the while loop and DefWindowProc and WndProc.
I'm sorry if this question is dumb, but I really want to understand when to use the ret mnemonic, and when to not use it.
RET (return) is used to finish / exit from a procedure or function.
Theoretically each procedure should have at least one return but it can have many if the procedure exits in multiple places.
There is no RET after GetMessage or TranslateMessage or DispatchMessage simply because in the logic of WinMain procedure there is no need for an exit from the procedure at that location.
There is an RET lower in the body of the procedure after the WHILE loop ends because that is the location where the programmer wanted the procedure to exit.
DO you know any other programming languages? Do you understand the concept or "returning" from a function?
The ASM RET mnemonic and instruction is pretty much the same as the "return" keyword of C programming language for example.
Quote from: BogdanOntanu on May 27, 2012, 07:34:25 AM
RET (return) is used to finish / exit from a procedure or function.
Theoretically each procedure should have at least one return but it can have many if the procedure exits in multiple places.
There is no RET after GetMessage or TranslateMessage or DispatchMessage simply because in the logic of WinMain procedure there is no need for an exit from the procedure at that location.
There is an RET lower in the body of the procedure after the WHILE loop ends because that is the location where the programmer wanted the procedure to exit.
The ASM RET mnemonic and instruction is pretty much the same as the "return" keyword of C programming language for example.
Thank you for the explanation.
Quote from: BogdanOntanu on May 27, 2012, 07:34:25 AM
DO you know any other programming languages? Do you understand the concept or "returning" from a function?
I only know the basics of C (including arrays, functions and pointers), and I know what the return statement does in a C function.
I hope this makes sense, build as a console app.
;==============================================================================
include \masm32\include\masm32rt.inc
;==============================================================================
.data
.code
;==============================================================================
;-----------------------------------------------------------------------------
; These options effectively turn off the automatically generated prologue and
; epilog code, so the only code in the procedure is the code that you see.
;-----------------------------------------------------------------------------
OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE
testproc proc arg1:DWORD, arg2:DWORD
;-----------------------------------------------------------------------
; ESP always contains the address of the last item to be pushed and the
; next in line to be popped. At this point the last item to be pushed
; (as an integral part of the CALL instruction) was the return address
; (IOW the address of the next instruction after the call instruction).
;-----------------------------------------------------------------------
mov eax, [esp] ; get value at stack pointer
printf("return address %Xh\n",eax) ; display it
;------------------------------------------------------------------------
; Since the arguments are pushed onto the stack before the call, and the
; stack "grows" downwards (from higher addresses to lower addresses) as
; items are pushed, the arguments will be located on the stack above the
; return address.
;------------------------------------------------------------------------
mov eax, [esp+4]
printf("value of arg1 %Xh\n",eax)
mov eax, [esp+8]
printf("value of arg2 %Xh\n",eax)
;------------------------------------------------------------------------
; The RET instruction effectively pops the return address from the stack
; and uses it as a jump destination.
;
; The constant 8, which would normally be generated by the assembler but
; which must be added manually here, specifies the number of bytes to
; add to the stack pointer after the return, to effectively remove the
; arguments from the stack.
;------------------------------------------------------------------------
ret 8
testproc endp
;---------------------------------------------------------------
; Turn the default prologue and epilog code generation back on.
;---------------------------------------------------------------
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef
;==============================================================================
start:
;==============================================================================
push 2
push 1
call testproc
ra1:
printf("return address %Xh\n\n",ra1)
invoke testproc,1,2
ra2:
printf("return address %Xh\n\n",ra2)
inkey
exit
;==============================================================================
END start
return address 401044h
value of arg1 1h
value of arg2 2h
return address 401044h
return address 401060h
value of arg1 1h
value of arg2 2h
return address 401060h
Rets are there becouse WndProc its a callback function (a function than windows call for you) so after calling DefWindowProc you pass the control to windows again same with the call at the end of WndProc when you finish evaluating msg and you must return.
Thank you!
I wasn't thinking correctly about this. I understand it now.
Lets try this out, for ever call EXECUTED you need to have a ret but with multiple exits in a procedure/function, only 1 ret is used. It is not the instruction count that matters, it is the instructions EXECUTED that do matter.
A RET can do extra things, if the function is of the STDCALL convention, the RET statement can have a trailing number that is used to balance the stack, something that must be done in any function call. Normally with a C calling convention the procedure only executes a bare RET and the code that called the function must balance the stack on return from the function.
Multiple exit conditions are common in software, multiple extry conditions are a bit more complex as there are a number of ways of doing it, you can use a C calling convention with a variable number of arguments and you can use in both C and STDCALL calls the address of a structure as is common in modern C++.
In assembler you can do any of these variations without any great effort, all you need to do is understand the basics of what you are doing and you can roll your own.
Hi pk1983,
You can examine the code below with a debugger like Ollydbg. The pop and jmp instructions are simulating the job of RET.
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\masm32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\masm32.lib
.data
msg db 'RET simulator',0
.code
start:
call testproc
invoke Beep,1000,1000
invoke ExitProcess,0
testproc PROC
invoke StdOut,ADDR msg
pop eax ; get the return address from the stack
jmp eax ; jump back to the main procedure
; the execution will continue from
; invoke Beep,1000,1000
testproc ENDP
END start
RET gets your comp back to executing the code after the CALL instruction. example:
call ASDF_P
xor eax, eax <<--------------------------------+
etc... |
etc... |
ASDF_P PROC |
|
mov eax, blahblah |
xor eax, eax |
ret ; this instruction brings you back here+
hope this helps :P i know the line's not straght :D
Hi kroz,
Your explanation is OK. You can enclose the code portion between the [code ] tags and the lines will be straight.