flat assembler
Message board for the users of flat assembler.

Index > Windows > NOOB Alert! - Trying to understand the basics

Goto page 1, 2, 3  Next
Author
Thread Post new topic Reply to topic
avidichard



Joined: 22 Mar 2021
Posts: 22
Location: Quebec/Canada
avidichard
I did my first x64 Window. It's a very simple empty window, nothing more. I just wanted to get started. Here's part of the code. Basically, the part I do not understand which is probably all of it since the previous code lines were just variable declarations and app settings.

Code:
label_start:
    push rax
    invoke GetModuleHandle, NULL
    mov [l_hInstance], rax
    mov [wc.hInstance], rax
    mov [wc.cbSize], sizeof.WNDCLASSEX
    mov [wc.style], CS_HREDRAW or CS_VREDRAW
    mov [wc.lpfnWndProc], procWnd
    mov [wc.cbClsExtra], 0
    mov [wc.lpszClassName], s_WinClass
    invoke RegisterClassEx, wc
    invoke CreateWindowEx, 0, s_WinClass, s_WinTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, [l_hInstance], NULL
    mov [l_hWnd], rax
    invoke ShowWindow, [l_hWnd], SW_SHOW
    
  label_msg_loop:
    invoke GetMessage, msg, NULL, 0, 0
    or rax, rax
    je label_end_prog
    invoke TranslateMessage, msg
    invoke DispatchMessage, msg
    jmp label_msg_loop
    
  label_end_prog:
    invoke ExitProcess, [msg.wParam]
    
proc procWnd uses rbx rsi rsi, hWnd, msg, wParam, lParam
  mov [hWnd], rcx
  mov [msg], rdx
  mov [wParam], r8
  mov [lParam], r9
  cmp [msg], WM_DESTROY
  je label_destroy_wnd
  invoke DefWindowProc, [hWnd], [msg], [wParam], [lParam]
  jmp label_finish
  
  label_destroy_wnd:
    invoke PostQuitMessage,0
    
  label_finish:
    ret
endp    


I read, and re-read but I just can't understand a few things here.

I know fasm DOES NOT work like normal programming languages and everything needs to be manual. I get that and that's exactly why I am slowly converging to fasm to get freedom.

I'm used to declare a variable. THEN, assign a value to that variable then use that variable when needed. I just cannot see how this is done. I know how to DECLARE an empty variable in fasm, I just don't understand the following.

; What does the push actually do???
push rax

; My understanding for the next 2 lines is that we simply defined l_hInstance and wc.hInstance and that both are exactly the same. Now the thing I tend to believe is that my l_hInstance variable would be totally useless since I could just use wc.hInstance whenever I need to refer to that. No?
mov [l_hInstance], rax
mov [wc.hInstance], rax

; Here, the ShowWindow requires a handle to know WHICH window to show but I never assigned any value to l_hWnd so where did it come from? How does it know WHICH window to show, or which handle to show if nothing has been given? Or is it because the window DOES NOT exist that it actually creates a new windo and assigns the value to l_hWnd once it created it?
mov [l_hWnd], rax
invoke ShowWindow, [l_hWnd], SW_SHOW

Then I have the entire label_msg_loop
What does "or rax, rax" do?
How does the 2 first lines of this loop NOT cause the "je" to end the program and simply execute the last 2 lines before looping again?

Then, there is that procWnd that states "uses rbx rsi rsi". WHY? I really don't get that...Then you have to "mov" with now, rcx, rdx, r8 and r9. Why is it c, d, 8 and 9 all of a sudden I never pushed those?

And, there is the cmp which I can only presume that it "compares" something. How does the "je" understand that it must be skipped or actually jump to destroy the window?

I went through the manual, checked online, Googled. I was just shy to post my questions because I fear being judged but I took my courage and I'm hoping someone may have a bit of patience to help me understand. I know how programming works, but I just don't understand the little logics here and I know that once I'll understand, I'll be good to try things on my own I just need a little kick start if someone is kind to help me with very begginner questions like mine.
Post 10 Sep 2021, 09:20
View user's profile Send private message Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 601
Location: Russia
macomics
To begin with, I advise you to start by understanding the same program for a 32-bit platform. In the 64-bit code, we added some additional problems.

Now in order.

avidichard wrote:
; What does the push actually do???
push rax
It reserves space for the GetModuleHandle function parameter in the stack. Although I suspect that initially the code was written call GetModuleHandle, and not invoke. The invoke macro, unlike the call command, automatically reserves space in the stack for the parameters of the called function.

avidichard wrote:
mov [l_hInstance], rax
mov [wc.hInstance], rax
There is no need to store the "hInstance" value in two variables. But you need to keep in mind that the value stored in [wc.hInstance] is a parameter for the function and it can be changed by it. But the original value will be needed in the future for subsequent function calls (like CreateWindow).

avidichard wrote:
mov [l_hWnd], rax
invoke ShowWindow, [l_hWnd], SW_SHOW
After the completion of any function, the result of its operation (the return value) is passed through rax. Therefore, after returning from the CreateWindow function, rax will have the value l_hWnd, which you will pass to the ShowWindow function.

avidichard wrote:
Then I have the entire label_msg_loop
What does "or rax, rax" do?
The logical operation in this case simply generates a value in the flag register for the subsequent conditional transition.
avidichard wrote:
Then, there is that procWnd that states "uses rbx rsi rsi". WHY? I really don't get that...Then you have to "mov" with now, rcx, rdx, r8 and r9. Why is it c, d, 8 and 9 all of a sudden I never pushed those?
According to the rules of calling the API accepted in Windows, the values of the rbx, rsi and rdi registers must necessarily be restored. This does not apply to calls to functions written in fasm.

The second part of the question just refers to the problems I mentioned above. Although API functions use the stdcall call type in which all parameters must be passed through the stack in reverse order, but in the 64-bit version, the first 4 parameters are passed to the function through registers (in the appropriate order: rcx, rdx, r8, r9), but a place in the stack is still allocated for them. Your function, using mov commands, simply fills it with the values of these parameters and frees the registers for later use.

Based on the above, the cmp [msg], WM_DESTROY command can be changed to cmp rdx, WM_DESTROY. Also taking into account the above, the DefWindowProc call can be performed as follows. Or
Code:
invoke DefWindowProc, rcx, rdx, r8, r9    
or
Code:
 sub rsp, 16
call [DefWindowProc]     


avidichard wrote:
And, there is the cmp which I can only presume that it "compares" something. How does the "je" understand that it must be skipped or actually jump to destroy the window?

The cmp command <op1>, <op2> compares the values in the operands by performing a subtraction operation (<op1> - <op2>) without changing them (only the flag register changes). The subsequent conditional jump command refers specifically to the value in the flag register. The previous "or" operation was needed for the same purpose (to determine the state of the flag register, but not to change its operands).

ADD: A small clarification. If you use the proc/invoke macros, then the type of call suitable for API functions will be organized for your functions written in fasm. But this is not necessary at all. You can decide in what order and how the parameters will be passed to your functions (and also whether they will be extracted from the stack before returning from your function, or whether the calling function should do this). For example, you can pass a single parameter to your function through the rax or r15 register, or pass 6 parameters to the function through the rax, rbx, rcx, r13, r10, rdi registers. But to call other people's functions, you will have to withstand the type of call that was originally designed by the author.
Post 10 Sep 2021, 10:21
View user's profile Send private message Reply with quote
avidichard



Joined: 22 Mar 2021
Posts: 22
Location: Quebec/Canada
avidichard
What big of a change would it be if I wanted to avoid all macros and do the exact same code using pure ASM? Would the code be that much long? Here's the entire code:

Code:
format PE64 GUI 6.0
entry label_start

include 'win64w.inc'

section '.idata' import data readable writeable
  library kernel32,'KERNEL32.DLL',\
          user32,  'USER32.DLL'

  import kernel32,\
         GetModuleHandle,'GetModuleHandleA',\
         ExitProcess,'ExitProcess'
  import user32,\
         RegisterClassEx, 'RegisterClassExA',\
         CreateWindowEx, 'CreateWindowExA',\
         DefWindowProc, 'DefWindowProcA',\
         GetMessage, 'GetMessageA',\
         TranslateMessage, 'TranslateMessage',\
         DispatchMessage, 'DispatchMessageA',\
         ShowWindow, 'ShowWindow',\
         PostQuitMessage, 'PostQuitMessage',\
         LoadIcon, 'LoadIconA',\
         LoadCursor, 'LoadCursorA'

section '.data' data readable writeable
  s_winTitle db "My program's window title", 0
  s_winClass db 'FASMWIN64', 0
  wc WNDCLASSEX ?
  msg MSG ?
  l_hWnd dq ?
  l_hInstance dq ?

section '.text' code readable writeable executable
  label_start:
    push rax
    invoke GetModuleHandle, NULL
    mov [l_hInstance], rax
    mov [wc.hInstance], rax
    mov [wc.cbSize], sizeof.WNDCLASSEX
    mov [wc.style], CS_HREDRAW or CS_VREDRAW
    mov [wc.lpfnWndProc], procWnd
    mov [wc.cbClsExtra], 0
    mov [wc.lpszClassName], s_WinClass
    invoke RegisterClassEx, wc
    invoke CreateWindowEx, 0, s_WinClass, s_WinTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, [l_hInstance], NULL
    mov [l_hWnd], rax
    invoke ShowWindow, [l_hWnd], SW_SHOW
    
  label_msg_loop:
    invoke GetMessage, msg, NULL, 0, 0
    or rax, rax
    je label_end_prog
    invoke TranslateMessage, msg
    invoke DispatchMessage, msg
    jmp label_msg_loop
    
  label_end_prog:
    invoke ExitProcess, [msg.wParam]
    
proc procWnd uses rbx rsi rsi, hWnd, msg, wParam, lParam
  mov [hWnd], rcx
  mov [msg], rdx
  mov [wParam], r8
  mov [lParam], r9
  cmp [msg], WM_DESTROY
  je label_destroy_wnd
  invoke DefWindowProc, [hWnd], [msg], [wParam], [lParam]
  jmp label_finish
  
  label_destroy_wnd:
    invoke PostQuitMessage,0
    
  label_finish:
    ret
endp    
Post 10 Sep 2021, 13:22
View user's profile Send private message Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 601
Location: Russia
macomics
Code:
        format PE64 GUI 6.0
        entry label_start

; include 'win64w.inc'

section '.idata' import data readable writeable
;  library kernel32,'KERNEL32.DLL',\
;          user32,  'USER32.DLL'

        dd RVA kernel32.lookup, 0, 0, RVA kernel32.label, RVA kernel32.address
        dd RVA user32.lookup, 0, 0, RVA user32.label, RVA user32.address
        dd 0, 0, 0, 0, 0
kernel32.label          db 'KERNEL32.DLL', 0
                        rb RVA $ and 1
user32.label            db 'USER32.DLL', 0
                        rb RVA $ and 1

;  import kernel32,\
;         GetModuleHandle,'GetModuleHandleA',\
;         ExitProcess,'ExitProcess'
kernel32.lookup:
                        dq RVA GetModuleHandle.label
                        dq RVA ExitProcess.label
                        dq 0
kernel32.address:
  GetModuleHandle       dq RVA GetModuleHandle.label
  ExitProcess           dq RVA ExitProcess.label
                        dq 0
GetModuleHandle.label   dw 0
                        db 'GetModuleHandleA', 0
                        rb RVA $ and 1
ExitProcess.label       dw 0
                        db 'ExitProcess', 0
                        rb RVA $ and 1

;  import user32,\
;         RegisterClassEx, 'RegisterClassExA',\
;         CreateWindowEx, 'CreateWindowExA',\
;         DefWindowProc, 'DefWindowProcA',\
;         GetMessage, 'GetMessageA',\
;         TranslateMessage, 'TranslateMessage',\
;         DispatchMessage, 'DispatchMessageA',\
;         ShowWindow, 'ShowWindow',\
;         PostQuitMessage, 'PostQuitMessage',\
;         LoadIcon, 'LoadIconA',\
;         LoadCursor, 'LoadCursorA'
user32.lookup:
                        dq RVA RegisterClassEx.label
                        dq RVA CreateWindowEx.label
                        dq RVA DefWindowProc.label
                        dq RVA GetMessage.label
                        dq RVA TranslateMessage.label
                        dq RVA DispatchMessage.label
                        dq RVA ShowWindow.label
                        dq RVA PostQuitMessage.label
                        dq RVA LoadIcon.label
                        dq RVA LoadCursor.label
                        dq 0
user32.address:
RegisterClassEx         dq RVA RegisterClassEx.label
CreateWindowEx          dq RVA CreateWindowEx.label
DefWindowProc           dq RVA DefWindowProc.label
GetMessage              dq RVA GetMessage.label
TranslateMessage        dq RVA TranslateMessage.label
DispatchMessage         dq RVA DispatchMessage.label
ShowWindow              dq RVA ShowWindow.label
PostQuitMessage         dq RVA PostQuitMessage.label
LoadIcon                dq RVA LoadIcon.label
LoadCursor              dq RVA LoadCursor.label
                        dq 0
RegisterClassEx.label   dw 0
                        db 'RegisterClassExA', 0
                        rb RVA $ and 1
CreateWindowEx.label    dw 0
                        db 'CreateWindowExA', 0
                        rb RVA $ and 1
DefWindowProc.label     dw 0
                        db 'DefWindowProcA', 0
                        rb RVA $ and 1
GetMessage.label        dw 0
                        db 'GetMessageA', 0
                        rb RVA $ and 1
TranslateMessage.label  dw 0
                        db 'TranslateMessage', 0
                        rb RVA $ and 1
DispatchMessage.label   dw 0
                        db 'DispatchMessageA', 0
                        rb RVA $ and 1
ShowWindow.label        dw 0
                        db 'ShowWindow', 0
                        rb RVA $ and 1
PostQuitMessage.label   dw 0
                        db 'PostQuitMessage', 0
                        rb RVA $ and 1
LoadIcon.label          dw 0
                        db 'LoadIconA', 0
                        rb RVA $ and 1
LoadCursor.label        dw 0
                        db 'LoadCursorA', 0
                        rb RVA $ and 1

section '.data' data readable writeable
  s_winTitle db "My program's window title", 0
  s_winClass db 'FASMWIN64', 0
  ;wc WNDCLASSEX ? ; struct - macro too
  wc:
  wc.cbSize             dd ?
  wc.style              dd ?
  wc.lpfnWndProc        dq ?
  wc.cbClsExtra         dd ?
  wc.cbWndExtra         dd ?
  wc.hInstance          dq ?
  wc.hIcon              dq ?
  wc.hCursor            dq ?
  wc.hbrBackground      dq ?
  wc.lpszMenuName       dq ?
  wc.lpszClassName      dq ?
  wc.hIconSm            dq ?
  wc.Length = $ - wc
  ; msg MSG ? ; struct - macro too
  msg:
  msg.hwnd              dq ?
  msg.message           dd ?
                        dd ?
  msg.wParam            dq ?
  msg.lParam            dq ?
  msg.time              dd ?
  msg.pt.x              dd ?
  msg.pt.y              dd ?
  l_hWnd dq ?
  l_hInstance dq ?

section '.text' code readable writeable executable
  label_start:
    xor rcx, rcx        ; instead: mov rcx, 0
    push rcx            ;          push rax
    call [GetModuleHandle]
    add rsp, 8
    mov [l_hInstance], rax
    mov [wc.hInstance], rax
    mov [wc.cbSize], wc.Length;sizeof.WNDCLASSEX
    mov [wc.style], 3; CS_HREDRAW or CS_VREDRAW
    mov [wc.lpfnWndProc], procWnd
    mov [wc.cbClsExtra], 0
    mov [wc.lpszClassName], s_WinClass
    lea rcx, [wc]       ; instead: mov rcx, wc
    push rcx
    call [RegisterClassEx]
    add rsp, 8
    ;invoke CreateWindowEx, 0, s_WinClass, s_WinTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, [l_hInstance], NULL
    push 0      ; NULL
    push [l_hInstance]
    push 0      ; NULL
    push 0      ; NULL
    push 480
    push 640
    push 8000h  ; CW_USEDEFAULT
    push 8000h  ; CW_USEDEFAULT
    mov r9, 000CF0000h; WS_OVERLAPPEDWINDOW
    lea r8, [s_WinTitle] ; instead: mov r8, s_WinTitle
    lea rdx, [s_WinClass]; instead: mov rdx, s_WinClass
    xor rcx, rcx; instead: mov rcx, 0
    sub rsp, 32
    call [CreateWindowEx]
    add rsp, 32+64
    mov [l_hWnd], rax
    ;invoke ShowWindow, [l_hWnd], SW_SHOW
    mov rdx, 5  ; SW_SHOW
    mov rcx, rax
    sub rsp, 16
    call [ShowWindow]
    add rsp, 16
  label_msg_loop:
    ;invoke GetMessage, msg, NULL, 0, 0
    xor r9, r9          ; instead: mov r9, 0
    xor r8, r8          ; instead: mov r8, 0
    xor rdx, rdx        ; instead: mov rdx, 0
    lea rcx, [msg]      ; instead: mov rcx, msg
    sub rsp, 32
    call [GetMessage]
    add rsp, 32
    or rax, rax
    je label_end_prog
    ;invoke TranslateMessage, msg
    lea rcx, [msg]      ; instead: mov rcx, msg
    push rcx
    call [TranslateMessage]
    add rsp, 8
    ;invoke DispatchMessage, msg
    lea rcx, [msg]      ; instead: mov rcx, msg
    push rcx
    call [DispatchMessage]
    add rsp, 8
    jmp label_msg_loop
    
  label_end_prog:
    ;invoke ExitProcess, [msg.wParam]
    mov rcx, [msg.wParam]
    push rcx
    call [ExitProcess]
    add rsp, 8
    
;proc procWnd uses rbx rsi rsi, hWnd, msg, wParam, lParam
procWnd:
  push rbp
  mov rbp, rsp
  push rbx
  push rsi
  push rdi      ; instead: rsi
  mov [rbp+16], rcx
  mov [rbp+24], rdx
  mov [rbp+32], r8
  mov [rbp+40], r9
  cmp dword [rbp+24], 2; WM_DESTROY
  je label_destroy_wnd
  ;invoke DefWindowProc, [hWnd], [msg], [wParam], [lParam]

; macro generated, redundant
  ; mov r9, [rbp+40]
  ; mov r8, [rbp+32]
  ; mov rdx, [rbp+24]
  ; mov rcx, [rbp+16]

  sub rsp, 32
  call [DefWindowProc]
  add rsp, 32
  jmp label_finish
  
  label_destroy_wnd:
    ;invoke PostQuitMessage,0
    xor rcx, rcx        ; instead: mov rcx, 0
    push rcx
    call [PostQuitMessage]
    add rsp, 8
    
  label_finish:
    ;ret - macro too
    lea rsp, [rbp-24]
    pop rdi     ; instead: rsi
    pop rsi
    pop rbx
    pop rbp
    retn
;endp
    

sorry for the typos, I typed in the browser

ADD: it seems that everything has been expanded
Post 10 Sep 2021, 14:08
View user's profile Send private message Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1042
Location: Belarus
DimonSoft
Just a small correction…
macomics wrote:
avidichard wrote:
mov [l_hInstance], rax
mov [wc.hInstance], rax
There is no need to store the "hInstance" value in two variables. But you need to keep in mind that the value stored in [wc.hInstance] is a parameter for the function and it can be changed by it. But the original value will be needed in the future for subsequent function calls (like CreateWindow).

In fact, it won’t change it since the parameter to RegisterClassEx is marked as [in] in MSDN. The bigger problem is that once one has multiple window classes registered it’s hard to choose which structure should be the source of hInstance value, especially since it is not really related to any of them and is just used as a “namespace” identifier.
Post 10 Sep 2021, 17:21
View user's profile Send private message Visit poster's website Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 601
Location: Russia
macomics
DimonSoft wrote:
In fact, it won’t change it since the parameter to RegisterClassEx is marked as [in] in MSDN. The bigger problem is that once one has multiple window classes registered it’s hard to choose which structure should be the source of hInstance value, especially since it is not really related to any of them and is just used as a “namespace” identifier.

Precisely because it is marked only as "in", and not "in const", its value is after use and does not necessarily need to be determined. Moreover, I said that it is worth remembering. By the way, there is usually not even a need to save the WNDCLASS(EX) structure. The memory for them is allocated dynamically from the stack and is released immediately after calling the RegisterClass(Ex) function.
Code:
   push 0
   push wc_class_name
   push 0
   push 17; COLOR_WINDOWFRAME
   push 32512 ; IDC_ARROW
   push 32512 ; IDI_APPLICATION
   push [l_hInstance]
   push 0
   push l_wndProc
   push [constStyleAndSize]
   mov rcx, rsp
   push rsp
   call [RegisterClassEx]
   add rsp, 8+80    


https://docs.microsoft.com/en-us/windows/win32/winmsg/using-window-classes

Code:
BOOL InitApplication(HINSTANCE hinstance) 
{ 
    WNDCLASSEX wcx; 
 
    // Fill in the window class structure with parameters 
    // that describe the main window. 
 
    wcx.cbSize = sizeof(wcx);          // size of structure 
    wcx.style = CS_HREDRAW | 
        CS_VREDRAW;                    // redraw if size changes 
    wcx.lpfnWndProc = MainWndProc;     // points to window procedure 
    wcx.cbClsExtra = 0;                // no extra class memory 
    wcx.cbWndExtra = 0;                // no extra window memory 
    wcx.hInstance = hinstance;         // handle to instance 
    wcx.hIcon = LoadIcon(NULL, 
        IDI_APPLICATION);              // predefined app. icon 
    wcx.hCursor = LoadCursor(NULL, 
        IDC_ARROW);                    // predefined arrow 
    wcx.hbrBackground = GetStockObject( 
        WHITE_BRUSH);                  // white background brush 
    wcx.lpszMenuName =  "MainMenu";    // name of menu resource 
    wcx.lpszClassName = "MainWClass";  // name of window class 
    wcx.hIconSm = LoadImage(hinstance, // small class icon 
        MAKEINTRESOURCE(5),
        IMAGE_ICON, 
        GetSystemMetrics(SM_CXSMICON), 
        GetSystemMetrics(SM_CYSMICON), 
        LR_DEFAULTCOLOR); 
 
    // Register the window class. 
 
    return RegisterClassEx(&wcx); 
}     
MSDN just gives an example of such use of this input parameter.
Post 10 Sep 2021, 18:54
View user's profile Send private message Reply with quote
avidichard



Joined: 22 Mar 2021
Posts: 22
Location: Quebec/Canada
avidichard
macomics wrote:
Code:
        format PE64 GUI 6.0
        entry label_start

; include 'win64w.inc'

section '.idata' import data readable writeable
;  library kernel32,'KERNEL32.DLL',\
;          user32,  'USER32.DLL'

        dd RVA kernel32.lookup, 0, 0, RVA kernel32.label, RVA kernel32.address
        dd RVA user32.lookup, 0, 0, RVA user32.label, RVA user32.address
        dd 0, 0, 0, 0, 0
kernel32.label          db 'KERNEL32.DLL', 0
                        rb RVA $ and 1
user32.label            db 'USER32.DLL', 0
                        rb RVA $ and 1

;  import kernel32,\
;         GetModuleHandle,'GetModuleHandleA',\
;         ExitProcess,'ExitProcess'
kernel32.lookup:
                        dq RVA GetModuleHandle.label
                        dq RVA ExitProcess.label
                        dq 0
kernel32.address:
  GetModuleHandle       dq RVA GetModuleHandle.label
  ExitProcess           dq RVA ExitProcess.label
                        dq 0
GetModuleHandle.label   dw 0
                        db 'GetModuleHandleA', 0
                        rb RVA $ and 1
ExitProcess.label       dw 0
                        db 'ExitProcess', 0
                        rb RVA $ and 1

;  import user32,\
;         RegisterClassEx, 'RegisterClassExA',\
;         CreateWindowEx, 'CreateWindowExA',\
;         DefWindowProc, 'DefWindowProcA',\
;         GetMessage, 'GetMessageA',\
;         TranslateMessage, 'TranslateMessage',\
;         DispatchMessage, 'DispatchMessageA',\
;         ShowWindow, 'ShowWindow',\
;         PostQuitMessage, 'PostQuitMessage',\
;         LoadIcon, 'LoadIconA',\
;         LoadCursor, 'LoadCursorA'
user32.lookup:
                        dq RVA RegisterClassEx.label
                        dq RVA CreateWindowEx.label
                        dq RVA DefWindowProc.label
                        dq RVA GetMessage.label
                        dq RVA TranslateMessage.label
                        dq RVA DispatchMessage.label
                        dq RVA ShowWindow.label
                        dq RVA PostQuitMessage.label
                        dq RVA LoadIcon.label
                        dq RVA LoadCursor.label
                        dq 0
user32.address:
RegisterClassEx         dq RVA RegisterClassEx.label
CreateWindowEx          dq RVA CreateWindowEx.label
DefWindowProc           dq RVA DefWindowProc.label
GetMessage              dq RVA GetMessage.label
TranslateMessage        dq RVA TranslateMessage.label
DispatchMessage         dq RVA DispatchMessage.label
ShowWindow              dq RVA ShowWindow.label
PostQuitMessage         dq RVA PostQuitMessage.label
LoadIcon                dq RVA LoadIcon.label
LoadCursor              dq RVA LoadCursor.label
                        dq 0
RegisterClassEx.label   dw 0
                        db 'RegisterClassExA', 0
                        rb RVA $ and 1
CreateWindowEx.label    dw 0
                        db 'CreateWindowExA', 0
                        rb RVA $ and 1
DefWindowProc.label     dw 0
                        db 'DefWindowProcA', 0
                        rb RVA $ and 1
GetMessage.label        dw 0
                        db 'GetMessageA', 0
                        rb RVA $ and 1
TranslateMessage.label  dw 0
                        db 'TranslateMessage', 0
                        rb RVA $ and 1
DispatchMessage.label   dw 0
                        db 'DispatchMessageA', 0
                        rb RVA $ and 1
ShowWindow.label        dw 0
                        db 'ShowWindow', 0
                        rb RVA $ and 1
PostQuitMessage.label   dw 0
                        db 'PostQuitMessage', 0
                        rb RVA $ and 1
LoadIcon.label          dw 0
                        db 'LoadIconA', 0
                        rb RVA $ and 1
LoadCursor.label        dw 0
                        db 'LoadCursorA', 0
                        rb RVA $ and 1

section '.data' data readable writeable
  s_winTitle db "My program's window title", 0
  s_winClass db 'FASMWIN64', 0
  ;wc WNDCLASSEX ? ; struct - macro too
  wc:
  wc.cbSize             dd ?
  wc.style              dd ?
  wc.lpfnWndProc        dq ?
  wc.cbClsExtra         dd ?
  wc.cbWndExtra         dd ?
  wc.hInstance          dq ?
  wc.hIcon              dq ?
  wc.hCursor            dq ?
  wc.hbrBackground      dq ?
  wc.lpszMenuName       dq ?
  wc.lpszClassName      dq ?
  wc.hIconSm            dq ?
  wc.Length = $ - wc
  ; msg MSG ? ; struct - macro too
  msg:
  msg.hwnd              dq ?
  msg.message           dd ?
                        dd ?
  msg.wParam            dq ?
  msg.lParam            dq ?
  msg.time              dd ?
  msg.pt.x              dd ?
  msg.pt.y              dd ?
  l_hWnd dq ?
  l_hInstance dq ?

section '.text' code readable writeable executable
  label_start:
    xor rcx, rcx        ; instead: mov rcx, 0
    push rcx            ;          push rax
    call [GetModuleHandle]
    add rsp, 8
    mov [l_hInstance], rax
    mov [wc.hInstance], rax
    mov [wc.cbSize], wc.Length;sizeof.WNDCLASSEX
    mov [wc.style], 3; CS_HREDRAW or CS_VREDRAW
    mov [wc.lpfnWndProc], procWnd
    mov [wc.cbClsExtra], 0
    mov [wc.lpszClassName], s_WinClass
    lea rcx, [wc]       ; instead: mov rcx, wc
    push rcx
    call [RegisterClassEx]
    add rsp, 8
    ;invoke CreateWindowEx, 0, s_WinClass, s_WinTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, [l_hInstance], NULL
    push 0      ; NULL
    push [l_hInstance]
    push 0      ; NULL
    push 0      ; NULL
    push 480
    push 640
    push 8000h  ; CW_USEDEFAULT
    push 8000h  ; CW_USEDEFAULT
    mov r9, 000CF0000h; WS_OVERLAPPEDWINDOW
    lea r8, [s_WinTitle] ; instead: mov r8, s_WinTitle
    lea rdx, [s_WinClass]; instead: mov rdx, s_WinClass
    xor rcx, rcx; instead: mov rcx, 0
    sub rsp, 32
    call [CreateWindowEx]
    add rsp, 32+64
    mov [l_hWnd], rax
    ;invoke ShowWindow, [l_hWnd], SW_SHOW
    mov rdx, 5  ; SW_SHOW
    mov rcx, rax
    sub rsp, 16
    call [ShowWindow]
    add rsp, 16
  label_msg_loop:
    ;invoke GetMessage, msg, NULL, 0, 0
    xor r9, r9          ; instead: mov r9, 0
    xor r8, r8          ; instead: mov r8, 0
    xor rdx, rdx        ; instead: mov rdx, 0
    lea rcx, [msg]      ; instead: mov rcx, msg
    sub rsp, 32
    call [GetMessage]
    add rsp, 32
    or rax, rax
    je label_end_prog
    ;invoke TranslateMessage, msg
    lea rcx, [msg]      ; instead: mov rcx, msg
    push rcx
    call [TranslateMessage]
    add rsp, 8
    ;invoke DispatchMessage, msg
    lea rcx, [msg]      ; instead: mov rcx, msg
    push rcx
    call [DispatchMessage]
    add rsp, 8
    jmp label_msg_loop
    
  label_end_prog:
    ;invoke ExitProcess, [msg.wParam]
    mov rcx, [msg.wParam]
    push rcx
    call [ExitProcess]
    add rsp, 8
    
;proc procWnd uses rbx rsi rsi, hWnd, msg, wParam, lParam
procWnd:
  push rbp
  mov rbp, rsp
  push rbx
  push rsi
  push rdi      ; instead: rsi
  mov [rbp+16], rcx
  mov [rbp+24], rdx
  mov [rbp+32], r8
  mov [rbp+40], r9
  cmp dword [rbp+24], 2; WM_DESTROY
  je label_destroy_wnd
  ;invoke DefWindowProc, [hWnd], [msg], [wParam], [lParam]

; macro generated, redundant
  ; mov r9, [rbp+40]
  ; mov r8, [rbp+32]
  ; mov rdx, [rbp+24]
  ; mov rcx, [rbp+16]

  sub rsp, 32
  call [DefWindowProc]
  add rsp, 32
  jmp label_finish
  
  label_destroy_wnd:
    ;invoke PostQuitMessage,0
    xor rcx, rcx        ; instead: mov rcx, 0
    push rcx
    call [PostQuitMessage]
    add rsp, 8
    
  label_finish:
    ;ret - macro too
    lea rsp, [rbp-24]
    pop rdi     ; instead: rsi
    pop rsi
    pop rbx
    pop rbp
    retn
;endp
    

sorry for the typos, I typed in the browser

ADD: it seems that everything has been expanded

Code:
dd RVA kernel32.lookup, 0, 0, RVA kernel32.label, RVA kernel32.address
...
kernel32.label db 'KERNEL32.DLL', 0
...
kernel32.lookup:    

So, as I can understand, the "dd RVA kernel32.lookup" is pointing to a label which you named "kernel32.lookup". A label name with a periode "." inside. It got me a bit confused because I usually make a distinction between labels and variables. But this is not an ultra official code so I take it as just an example which I hugely appreciate. so the "kernel32.label" is a variable and you named a lable "kernel32.lookup" if I am reading the code right.
Code:
mov [wc.style], 3; CS_HREDRAW or CS_VREDRAW    

Could this have been just defined in the ".data" section?
Code:
CS_HREDRAW db, 2
CS_VREDRAW db, 1    

and then written like this?
Code:
mov [wc.style], CS_HREDRAW or CS_VREDRAW    

Code:
xor rcx, rcx    

This code, if I understand properly, simply empties rcx.
Code:
push rcx
call [GetModuleHandle]
add rsp, 8
mov [l_hInstance], rax    

That's something I cannot understand and probably is the hardest point I cannot put a logical explanation to. How can we mov from rax when rax has never been pushed? You pushed rcx but not rax???

Code:
xor rcx, rcx        ; instead: mov rcx, 0
lea rcx, [wc]       ; instead: mov rcx, wc
lea r8, [s_WinTitle] ; instead: mov r8, s_WinTitle
lea rdx, [s_WinClass]; instead: mov rdx, s_WinClass
xor r9, r9          ; instead: mov r9, 0
xor r8, r8          ; instead: mov r8, 0
xor rdx, rdx        ; instead: mov rdx, 0
lea rcx, [msg]      ; instead: mov rcx, msg    

Why use the lea and xor instad of the mov?
Code:
;proc procWnd uses rbx rsi rsi, hWnd, msg, wParam, lParam
procWnd:
  push rbp
  mov rbp, rsp
  push rbx
  push rsi
  push rdi      ; instead: rsi
  mov [rbp+16], rcx
  mov [rbp+24], rdx
  mov [rbp+32], r8
  mov [rbp+40], r9
  cmp dword [rbp+24], 2; WM_DESTROY
  je label_destroy_wnd
  ;invoke DefWindowProc, [hWnd], [msg], [wParam], [lParam]    

This part makes my teeth grind a bit because I count 4 parameters but there's only 3 pushes, why? and I don't understand the "mov rpb, rsp" that's the line that puzzles me the most because I don't know what's happenning with that line. It even puzzled me in my original code with the macros. And rcx,rdx, r8 and r9 are never pushed either.
Post 10 Sep 2021, 22:00
View user's profile Send private message Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1042
Location: Belarus
DimonSoft
macomics wrote:
DimonSoft wrote:
In fact, it won’t change it since the parameter to RegisterClassEx is marked as [in] in MSDN. The bigger problem is that once one has multiple window classes registered it’s hard to choose which structure should be the source of hInstance value, especially since it is not really related to any of them and is just used as a “namespace” identifier.

Precisely because it is marked only as "in", and not "in const", its value is after use and does not necessarily need to be determined.

Sorry, I was too focused on the parameter description and forgot to take a look at the function prototype itself which declares the only parameter as a pointer to const, so the question now is who would change the structure contents during the function call if the function itself is not supposed to?

macomics wrote:
By the way, there is usually not even a need to save the WNDCLASS(EX) structure. The memory for them is allocated dynamically from the stack and is released immediately after calling the RegisterClass(Ex) function.

Yep. But my point was that storing and then taking the hInstance value from some WNDCLASSEX is a bad idea from the logical point of view in the first place.
Post 10 Sep 2021, 22:04
View user's profile Send private message Visit poster's website Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 601
Location: Russia
macomics
avidichard wrote:
So, as I can understand, the "dd RVA kernel32.lookup" is pointing to a label which you named "kernel32.lookup". A label name with a periode "." inside. It got me a bit confused because I usually make a distinction between labels and variables. But this is not an ultra official code so I take it as just an example which I hugely appreciate. so the "kernel32.label" is a variable and you named a lable "kernel32.lookup" if I am reading the code right.
On fasm, the use of . in the variable name is not prohibited. Moreover, without any macros, declarations of names starting with a dot are connected to the previous name without a dot at the beginning.
Code:
  ParentName:
    .ChildName0:
    .ChildName1   db "Hello", 0
    .ChildName2  = 5
    .ChildName3   dq 0xFFFF0000FFFF0000
     repeat .ChildName2
                   nop
     end repeat
     mov rax, [.ChildName3]
     lea rcx, [.ChildName1]
     call .ChildName0
  NewParentName:
     repeat ParentName.ChildName2
                   nop
     end repeat
     mov rax, [ParentName.ChildName3]
     lea rcx, [ParentName.ChildName1]
     call ParentName.ChildName0
    
avidichard wrote:

Could this have been just defined in the ".data" section?
Code:

CS_HREDRAW db, 2
CS_VREDRAW db, 1


and then written like this?
Code:

mov [wc.style], CS_HREDRAW or CS_VREDRAW
No. These values must be defined anywhere in the program (even outside the data section) as symbolic or numeric constants. Only then will their similar application become possible. For more information, see the manual:
http://flatassembler.net/docs.php?article=manual
avidichard wrote:

xor rcx, rcx


This code, if I understand properly, simply empties rcx.
Code:

push rcx
call [GetModuleHandle]
add rsp, 8
mov [l_hInstance], rax


That's something I cannot understand and probably is the hardest point I cannot put a logical explanation to. How can we mov from rax when rax has never been pushed? You pushed rcx but not rax???
The value placed by the push rax command does not matter. The main point of this command is to reserve memory in the stack for the parameter of this function. The value itself must be placed in the rcx register. I just don't see much difference between the push rax and push rcx commands in this case.
avidichard wrote:
Why use the lea and xor instad of the mov?
A relative address, not an absolute address, can be defined behind the name. In order not to run into these rakes every time, I have already developed the habit of using lea to load addresses.
Code:
 relativeValue   equ qword [rcx+8]
MyFunction:
virtual at rbp+16
  .argValue0       dq ?
  .argValue1       dq ?
  .argString        dq ?
end virtual
  mov rcx, [.argValue0]
  lea rax, relativeValue
  lea rdx, [.argValue1]
  mov rsi, [.argString]
    

avidichard wrote:

Code:

;proc procWnd uses rbx rsi rsi, hWnd, msg, wParam, lParam
procWnd:
  push rbp
  mov rbp, rsp
  push rbx
  push rsi
  push rdi      ; instead: rsi
  mov [rbp+16], rcx
  mov [rbp+24], rdx
  mov [rbp+32], r8
  mov [rbp+40], r9
  cmp dword [rbp+24], 2; WM_DESTROY
  je label_destroy_wnd
  ;invoke DefWindowProc, [hWnd], [msg], [wParam], [lParam]        



This part makes my teeth grind a bit because I count 4 parameters but there's only 3 pushes, why? and I don't understand the "mov rpb, rsp" that's the line that puzzles me the most because I don't know what's happenning with that line. It even puzzled me in my original code with the macros. And r8 and r9 are never pushed either.
Here you need to explain the points:
1. An assembly language function can start with any label in the code section. What happens after calling this function will be the body of the function.
2. Designing the design of the function falls on the shoulders of its author.
2.1) The placement of function parameters can be in general or special purpose registers, stack or global variables.
2.1.a: The placement of function parameters in global variables does not carry any design features and is characterized only by the use of absolute addresses in the function body.
2.1.b: When using general/special purpose registers, no design changes are also required, but you will have to watch for possible overwriting of the input parameters of the function before/after calling the subfunctions.
2.1.c: When using a stack to pass parameters to a function, a temporary structure is formed on its top with a certain set of variables prepared by the function that called it to work with them in the body of your function. You can work with such a structure in 64-bit addressing both with respect to the rsp register (but then you will have to control the stack filling), but also with respect to any other register in which the stack address will be fixed at the time of entering the function (in the IA-64 architecture, the rbp register is provided for fixing the stack).In order for the calling function to use it for the same purpose, its value must be saved and restored at the entering and leaving of your function.
Code:
 sample1:
  push rbp   ; saving the value when entering
  mov rbp, rsp ; fixing the stack pointer
; now all parameters get fixed relative addresses anywhere in the function
;  qword [rbp + 0] = old rbp
;  qword [rbp + 8] = return address
;  qword [rbp + 16] = arg0
;  qword [rbp + 24] = arg1
; . . .
<body>
  mov rsp, rbp ; return the stack pointer to the position before entering the function
  pop rbp   ; restoring the value when leaving
  . . .    


Last edited by macomics on 11 Sep 2021, 01:05; edited 2 times in total
Post 10 Sep 2021, 23:01
View user's profile Send private message Reply with quote
avidichard



Joined: 22 Mar 2021
Posts: 22
Location: Quebec/Canada
avidichard
macomics wrote:
No. These values must be defined anywhere in the program (even outside the data section) as symbolic or numeric constants. Only then will their similar application become possible. For more information, see the manual: http://flatassembler.net/docs.php?article=manual

So, if I understand properly, I would set:

CS_HREDRAW db, 2
CS_VREDRAW db, 1

BEFORE the ".idata" section. so this would make "mov [wc.style], CS_HREDRAW or CS_VREDRAW" now possible?

I also saw that you did ".ChildName0:" was the colon ":" volantary? Because if I'd follow your example, "ParentName:" is, in my dictionary, a lable because it ends with a ":". Now, what you are saying is that if you add "." on any variables under like all of your ChildNames, this means that ParentName is no longer a label but a structure of some sort. And if you really intended to have a ":" after ChildName0, then ChildName1 is linked to ChildName0 where ChildName1,2 and 3 are now accessible like this: "ParentName.ChildName0.ChildName1" and "ParentName.ChildName1" is not accessible.
macomics wrote:
The value placed by the push rax command does not matter. The main point of this command is to reserve memory in the stack for the parameter of this function. The value itself must be placed in the rcx register. I just don't see much difference between the push rax and push rcx commands in this case.

So, I don't know if I get this right. BUT, if I use "push rcx" this would automatically reserve the space for rax and rbx? If I "push rdx" this would automatically reserve rax, rbx and rcx?

macomics wrote:
A relative address, not an absolute address, can be defined behind the name. In order not to run into these rakes every time, I have already developed the habit of using lea to load addresses.
Code:
 relativeValue   equ qword [rcx+8]
MyFunction:
virtual at rbp+16
  .argValue0       dq ?
  .argValue1       dq ?
  .argString        dq ?
end virtual
  mov rcx, [.argValue0]
  lea rax, relativeValue
  lea rdx, [.argValue1]
  mov rsi, [.argString]
    

So I understand that both do the job "mov" and "lea" but one should know to use proper programming practices for the right situations and not just be lazy using mov all the time just because it works?
Post 10 Sep 2021, 23:31
View user's profile Send private message Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 601
Location: Russia
macomics
2.2) In addition to the parameters of the function, local (temporary) variables can be defined that will be available before exiting this function. To organize access to them and their placement, the same technique is used that describes the parameters passed through the stack.
Code:
sample2:
  .local_variables_bytes = 128
push rbp
mov rbp, rsp
sub rsp, .local_variables_bytes
; now all variables get fixed relative addresses anywhere in the function
;   qword [rbp-8] = var0
;   qword [rbp-16] = var1
;   qword [rbp-24] = var2
;   qword [rbp-32] = var3
; . . .
mov rsp, rbp ; now this command finds its main meaning
pop rbp
. . .    

2.3) When using parameters passed through the stack, it is also necessary to determine the order of the parameters in the stack (the C language uses the reverse enumeration of parameters, but Pascal preferred direct enumeration) and the mechanism for its release from them. If in the case of local variables, the function should always release the memory occupied by them, then with parameters it is another matter. Depending on the type of call, parameters may be pushed off the stack when returning from your function, or each call to your function must be supplemented with a stack balancing command:
Code:
sample3:
  .args_bytes_count = 8
. . .
  retn .args_bytes_count

sample4:
  .args_bytes_count = 16
  . . . 
  retn

callerFunc:
   call sample4
   add rsp, sample4.args_bytes_count
   call sample3
   call sample4
   add rsp, sample4.args_bytes_count    

2.4) In the 64-bit (LONG) mode, only near function calls are available for the user program to form, but other types of calls are also possible in other modes (call far 0x0023:0x00401000)
3. As I have already said, it is accepted in Windows OS that it is necessary to restore the values of the rbx, rbp, rsi, rdi registers after returning from any callback function. Therefore, I save and restore the values of these registers (although I just expanded the code that the macros will generate, and it says: uses ebx, rsi, rdi)
4. Then there is a vestige of the problem, which I also already mentioned warning you in the first post. In the 64-bit version of Windows, it is customary to place the values of the first 4 parameters of the function in registers, but to save these parameters, the calling function allocates a block of memory in the stack (although it does not add the values of these parameters to it, it is strange why). With the complication of the program, the window function will grow abundantly and in order not to erase these values at the very beginning, we add them to the memory allocated for them.
5. Again, I was just unwrapping the macro code and therefore used the value that was stored on the stack. But nothing prevents you from writing the edx register instead of dword [rbp+24]
Post 10 Sep 2021, 23:36
View user's profile Send private message Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 601
Location: Russia
macomics
avidichard wrote:

So, if I understand properly, I would set:

CS_HREDRAW db, 2
CS_VREDRAW db, 1

BEFORE the ".idata" section. so this would make "mov [wc.style], CS_HREDRAW or CS_VREDRAW" now possible?
Not quite. Your mistake is that you define the physical data placed in the program (the db directive), and not the constants substituted by the compiler during its assembly (the define, equ, fix and = directives)
avidichard wrote:

I also saw that you did ".ChildName0:" was the colon ":" volantary? Because if I'd follow your example, "ParentName:" is, in my dictionary, a lable because it ends with a ":". Now, what you are saying is that if you add "." on any variables under like all of your ChildNames, this means that ParentName is no longer a label but a structure of some sort. And if you really intended to have a ":" after ChildName0, then ChildName1 is linked to ChildName0 where ChildName1,2 and 3 are now accessible like this: "ParentName.ChildName0.ChildName1" and "ParentName.ChildName1" is not accessible.
Again, no. The use of names starting with a dot will not affect ParentName in any way. Just label names starting with a dot will be perceived by the compiler as "local" and connect them to the nearest predefined "global name" (not starting with a dot, but there may also be dots inside it)
Code:
  Parent.Name:  ; global name
    .Child.Name: ; local name ("Parent.Name.Child.Name")
    .Child.Name1  db 1 ; local name ("Parent.Name.Child.Name1" as byte value = 0x01)
    call .Child.Name      ; call Parent.Name.Child.Name
    .function         db 195 ; local name (void function: Parent.Name.function as retn )
  New.Parent.Name: ; A new global name, after which all calls to the previous local names require the mandatory indication of the preceding global name
   .Child.Name: ; local name ("New.Parent.Name.Child.Name")
    call Parent.Name.Child.Name
    
Only adding a dot to the beginning of the name leads to such actions. The use of a dot inside a name is perceived by the compiler as a single indivisible name.
avidichard wrote:

So, I don't know if I get this right. BUT, if I use "push rcx" this would automatically reserve the space for rax and rbx? If I "push rdx" this would automatically reserve rax, rbx and rcx?
And again it is incorrect. The size of all general-purpose registers is the same. Therefore, that the push rax command, that push rcx will reserve the same number of bytes in the stack (eight). With the same success, you can use the sub rsp, 8 command, its length is several bytes longer than that of the push rXx commands.
avidichard wrote:
So I understand that both do the job "mov" and "lea" but one should know to use proper programming practices for the right situations and not just be lazy using mov all the time just because it works?
In addition to loading addresses into registers, the lea command helps to perform simple expressions and one way or another you come to its active use.
Code:
string  db "abracadabra . . .", 0
lea rdi, [string]
or rcx, -1
xor rax, rax
mov rdx, rdi
repne scas byte [rdi]
lea rcx, [rdi-1]
mov rdi, rdx
sub rcx, rdx
mov al, ' '
repne scas byte [rdi]
jnz no_space
lea rsi, [rdi-1]
 . . .
no_space:
 . . .
    

DimonSoft wrote:
Sorry, I was too focused on the parameter description and forgot to take a look at the function prototype itself which declares the only parameter as a pointer to const, so the question now is who would change the structure contents during the function call if the function itself is not supposed to?

It depends on the implementation of the API functions and the presence of an interception function.
DimonSoft wrote:
Yep. But my point was that storing and then taking the hInstance value from some WNDCLASSEX is a bad idea from the logical point of view in the first place.

And why store it at all, when it is possible to call GetModuleHandle(NULL) {GetWindowLong(hWnd,GWL_HINSTANCE) / GetClassLong(hWnd, GCL_HINSTANCE) } at any time. And WNDCLASS(EX) can be read by calling GetClassInfo(Ex).


Last edited by macomics on 11 Sep 2021, 01:07; edited 1 time in total
Post 11 Sep 2021, 00:00
View user's profile Send private message Reply with quote
Overclick



Joined: 11 Jul 2020
Posts: 524
Location: Ukraine
Overclick
Am I alone to see you guys don't understand each other? You complicate everything you talking about.
Just use it like that:
CS_HREDRAW = 2
CS_VREDRAW = 1
And don't push first four parameters to stack in 64bit mode winapi. Functions need them in rcx,rdx,r8,r9. Use Invoke to be sure the args passed correctly. Threre is few rules it uses for integer, float and rest of params. The result (usualy) returns in rax.
Post 11 Sep 2021, 16:05
View user's profile Send private message Visit poster's website Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 601
Location: Russia
macomics
Code:
macro invoke proc,[arg]
 { common fastcall [proc],arg }

macro fastcall proc,[arg]
 { common local stackspace,argscount,counter
    if argscount < 4
     stackspace = 4*8
    else if argscount and 1
     stackspace = (argscount+1)*8
    else
     stackspace = argscount*8
    end if
    counter = 0
    if stackspace
     if defined current@frame
      if current@frame<stackspace
       current@frame = stackspace
      end if
     else
      if stackspace
       sub rsp,stackspace
      end if
     end if
    end if    

This I still reserve a little in the stack. in general, invoke always reserves 32 bytes in the stack. Even when the arguments are 0.
Post 11 Sep 2021, 16:26
View user's profile Send private message Reply with quote
Overclick



Joined: 11 Jul 2020
Posts: 524
Location: Ukraine
Overclick
Quote:

This I still reserve a little in the stack. in general, invoke always reserves 32 bytes in the stack. Even when the arguments are 0.


Description:
Filesize: 46.84 KB
Viewed: 5400 Time(s)

Capture.PNG


Post 11 Sep 2021, 17:02
View user's profile Send private message Visit poster's website Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 601
Location: Russia
macomics
[quote="Overclick"] The function being called is not required to save input parameters passed through the registers into the stack but reserving space in the stack allows to do this if necessary.
Post 11 Sep 2021, 17:11
View user's profile Send private message Reply with quote
Overclick



Joined: 11 Jul 2020
Posts: 524
Location: Ukraine
Overclick
Microsoft decided to use it like that. Seems it was needed for 32 to 64 old-code migration.
Post 11 Sep 2021, 17:24
View user's profile Send private message Visit poster's website Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 601
Location: Russia
macomics
No automatic compiler will generate code to save a non-existent parameter in the buffer for parameters, even if it needs to release the register rcx, rdx, r8, r9. But some self-made function from a craftsman can easily create assembly commands like this.
Post 11 Sep 2021, 17:30
View user's profile Send private message Reply with quote
Overclick



Joined: 11 Jul 2020
Posts: 524
Location: Ukraine
Overclick
Not like that. They reserved stack to call old 32bit winapi when you don't even know what's happening ))
I don't believe they recompilled tons of code to 64bit. Not that fast at days WinXP-64 become.
Post 11 Sep 2021, 17:49
View user's profile Send private message Visit poster's website Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 601
Location: Russia
macomics
Of course, I understand that you are in a smartphone, but the latest news: Microsoft has already refused even to support Windows 7/8. WinXP-64-the last decade.
Post 11 Sep 2021, 17:57
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  
Goto page 1, 2, 3  Next

< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2020, Tomasz Grysztar. Also on GitHub, YouTube, Twitter.

Website powered by rwasa.