GUI - приложение с кнопкой

Тема в разделе "WASM.BEGINNERS", создана пользователем Entropy, 11 янв 2026.

  1. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    219
    Это не совсем верно. Помимо резервирования стека его еще надо выравнивать на 16. Именно отсюда у вас и взялась "+1". Но на деле этой "+1" может и не быть при четном числе аргументов.

    https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170
     
  2. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    437
    MaKsIm

    Стек это особая область, при выборке ниже рабочего набора ядро расширит стек. Если размер выборки больше машинной страницы, попытка выборки приведет к завершению процесса.
     
  3. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.172
    давайте теперь я напишу. Обычно я передаю параметры процедуре (если параметров больше 4) через mov [rsp+20h+(номер_параметра-5)*8],значение_парметра Параметры через MOV можно передавать функции в любой последовательности, а не как через PUSH только в правильной. Но иногда надоедает писать бесконечные mov [rsp+20h+8*N],XX и я использую PUSH XX, не забывая перед вызовом процедуры добавить еще и sub rsp,20h результат всё равно один и тот же
     
  4. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    566
    Как тяжело вам живется :)

    Код (ASM):
    1.  
    2. includelib user32.lib
    3. extern   __imp_CreateWindowExA: qword
    4. extern   __imp_GetMessageA: qword
    5. extern   __imp_TranslateMessage: qword
    6. extern   __imp_DispatchMessageA: qword
    7. extern   __imp_PostQuitMessage: qword
    8.  
    9. MSGSizeInDwords   equ   10
    10. MSG_message   equ   8
    11. MSG_wParam   equ   16
    12.  
    13. .code
    14.  
    15. main   proc
    16.    local  message[MSGSizeInDwords]: dword
    17.    local   hMainWnd: qword
    18.    local   arg12: qword
    19.    local   arg11: qword
    20.    local   arg10: qword
    21.    local   arg9: qword
    22.    local   arg8: qword
    23.    local   arg7: qword
    24.    local   arg6: qword
    25.    local   arg5: qword
    26.    local   args4_1[4]: qword
    27.  
    28.    xor   rcx, rcx     ; dwExStyle,
    29.    lea   rdx, editClassName   ; lpClassName,
    30.    lea   r8, initialText     ; lpWindowName,
    31.    mov   r9, 0C00000h + 40000h + 20000h + 10000h + 100000h + 200000h + 10000000h + 4 ; dwStyle ( WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_VISIBLE | ES_MULTILINE )
    32.      mov   arg5, 80000000h     ; X,
    33.      mov   arg6, 80000000h     ; Y,
    34.      mov   arg7, 80000000h     ; nWidth,
    35.      mov   arg8, 80000000h     ; nHeight,
    36.      mov   arg9, 0       ; hWndParent,
    37.      mov   arg10, 0     ; hMenu,
    38.      mov   arg11, 0     ; hInstance,
    39.      mov   arg12, 0     ; lpParam
    40.      call   __imp_CreateWindowExA
    41.      mov   hMainWnd, rax
    42.  
    43. messageLoop:
    44.    lea   rcx, message
    45.    xor   rdx, rdx
    46.    xor   r8, r8
    47.    xor   r9, r9
    48.    call   __imp_GetMessageA
    49.  
    50.    or   rax, rax
    51.    jz   messageLoopExit
    52.  
    53.    cmp   message[MSG_message], 112h   ; WM_SYSCOMMAND
    54.    jnz   messageLoop1
    55.    cmp   message[MSG_wParam], 0F060h   ; SC_CLOSE
    56.    jz   messageLoopPostQuit
    57. messageLoop1:  
    58.    cmp   message[MSG_message], 100h   ; WM_KEYDOWN
    59.    jnz   messageLoopProceed
    60.    cmp   message[MSG_wParam], 27     ; Esc
    61.    jnz   messageLoopProceed
    62. messageLoopPostQuit:  
    63.    xor   rcx, rcx
    64.    call   __imp_PostQuitMessage
    65.  
    66. messageLoopProceed:
    67.  
    68.    lea   rcx, message
    69.    call   __imp_TranslateMessage
    70.  
    71.    lea   rcx, message
    72.    call   __imp_DispatchMessageA
    73.  
    74.    jmp   messageLoop
    75.  
    76. messageLoopExit:
    77.    xor   rax, rax
    78.    ret
    79. main   endp
    80.  
    81. editClassName   db   'EDIT', 0
    82. initialText   db   'NOTEPAD--  ''Esc'' to close', 0
    83. end
     
    Application и Mikl___ нравится это.
  5. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    219
    Я не упоминал рост стека. Так что ваш комментарий явно не ко мне.
     
  6. Entropy

    Entropy Member

    Публикаций:
    0
    Регистрация:
    23 авг 2020
    Сообщения:
    233
    Dmitry_Milk, например функция принимает 10 а другая 20,то в сумме я должен зарезервировать 30*8 = 240 байт + 8 выравнивание ,мне уже не нужно будет выделять стёк для следующий функции с 20 аргументами
     
  7. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    566
    Entropy, зачем 30? 20

    Это же не stdcall, где вызываемая функция обязана была сама подчищать стек после вызова.

    В конвенции x64 после CALL у тебя RSP остается в том же самом положении, в каком был до вызова. Поэтому его можно вообще не трогать в течение всей функции, а просто сразу писать в зарезервированные места в стеке перед вызовом. А если аргументов 4 или меньше - вообще даже писать в него не надо, просто заполнять регистры.
     
  8. Entropy

    Entropy Member

    Публикаций:
    0
    Регистрация:
    23 авг 2020
    Сообщения:
    233
    Dmitry_Milk, я хотел сказать,что один раз вычесть из регистра RSP необходимое количество аргументов для всех последующих вызовов функций,что бы перед каждым вызовом функции не вычитать из RSP количество аргументов
     
  9. GRAFik

    GRAFik Active Member

    Публикаций:
    0
    Регистрация:
    14 мар 2020
    Сообщения:
    369
    Из под компилятора Visual Studio 2022:
    Код (ASM):
    1. ; асм-листинг функции  Add_3:
    2.  
    3. ; int __fastcall Add_3(int a, int b, int c, int d, int e, int f, int g, int h, int z)
    4. Add_3 proc near
    5.  
    6. x= dword ptr -18h
    7. a= dword ptr  8
    8. b= dword ptr  10h
    9. c= dword ptr  18h
    10. d= dword ptr  20h
    11. e= dword ptr  28h
    12. f= dword ptr  30h
    13. g= dword ptr  38h
    14. h= dword ptr  40h
    15. z= dword ptr  48h
    16.  
    17. mov     [rsp+d], r9d
    18. mov     [rsp+c], r8d
    19. mov     [rsp+b], edx
    20. mov     [rsp+a], ecx
    21. sub     rsp, 18h
    22. mov     eax, [rsp+18h+b]
    23. mov     ecx, [rsp+18h+a]
    24. add     ecx, eax
    25. mov     eax, ecx
    26. add     eax, [rsp+18h+c]
    27. add     eax, [rsp+18h+d]
    28. add     eax, [rsp+18h+e]
    29. add     eax, [rsp+18h+f]
    30. add     eax, [rsp+18h+g]
    31. add     eax, [rsp+18h+h]
    32. add     eax, [rsp+18h+z]
    33. mov     [rsp+18h+x], eax
    34. mov     eax, [rsp+18h+x]
    35. add     rsp, 18h
    36. retn
    37. Add_3 endp
    38.  
    39.  
    40.  
    41. ;  main proc ----> Главная(основная) функция
    42. ; int __fastcall main(int argc, const char **argv, const char **envp)
    43.  
    44. main proc
    45.  
    46. e= dword ptr -38h
    47. f= dword ptr -30h
    48. g= dword ptr -28h
    49. h= dword ptr -20h
    50. z= dword ptr -18h
    51.  
    52. sub     rsp, 58h   ;  --------------------> Выделяем место в стеке
    53.  
    54.  
    55.     mov     edx, 2          ; b
    56.     mov     ecx, 1          ; a
    57. call    Add_1
    58.  
    59.  
    60.     mov     [rsp+58h+f], 6  ; f
    61.     mov     [rsp+58h+e], 5  ; e
    62.     mov     r9d, 4          ; d
    63.     mov     r8d, 3          ; c
    64.     mov     edx, 2          ; b
    65.     mov     ecx, 1          ; a
    66. call    Add_2
    67.  
    68.  
    69.     mov     [rsp+58h+z], 9  ; z
    70.     mov     [rsp+58h+h], 8  ; h
    71.     mov     [rsp+58h+g], 7  ; g
    72.     mov     [rsp+58h+f], 6  ; f
    73.     mov     [rsp+58h+e], 5  ; e
    74.     mov     r9d, 4          ; d
    75.     mov     r8d, 3          ; c
    76.     mov     edx, 2          ; b
    77.     mov     ecx, 1          ; a
    78. call    Add_3
    79.  
    80. xor     eax, eax
    81.  
    82. add     rsp, 58h  ;  --------------------> Чистим стек
    83.  
    84. retn
    85.  
    86. main endp
     
    Последнее редактирование: 2 фев 2026
  10. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    566
    Да, только их не надо суммировать. Надо выделить под максимум аргументов. Там, где аргументов меньше - использовать только самые нижние слоты, а верхние просто не используются в таком вызове.

    Скажем, тебе надо вызвать две функции, у одной 6 аргументов, у второй 8.

    Код (ASM):
    1.  
    2.   sub   rsp, 8 * 8 + 8   ; резервируем место под 8 аргументов и выравнивание
    3.  
    4.    ...
    5.  
    6.    ; надо вызвать функцию с 6 аргументами
    7.    mov   rcx, аргумент1
    8.    mov   rdx, аргумент2
    9.    mov   r8, аргумент3
    10.    mov   r9, аргумент4
    11.    mov   [rsp + 4*8], аргумент5
    12.    mov   [rsp + 5*8], аргумент6,  дальше слоты [rsp + 6*8] и [rsp + 7*8] в данном вызове не используются
    13.    call   func_with_6_args
    14.  
    15.    ...
    16.  
    17.    ; надо вызвать функцию с 8 аргументами
    18.    mov   rcx, аргумент1
    19.    mov   rdx, аргумент2
    20.    mov   r8, аргумент3
    21.    mov   r9, аргумент4
    22.    mov   [rsp + 4*8], аргумент5
    23.    mov   [rsp + 5*8], аргумент6
    24.    mov   [rsp + 6*8], аргумент7
    25.    mov   [rsp + 7*8], аргумент8
    26.    call   func_with_8_args
    27.  
    28.    ...
    29.  
    30.    add   rsp, 8 * 8 + 8   ; восстанавливаем стек
    31.    ret
    32.  
     
  11. Entropy

    Entropy Member

    Публикаций:
    0
    Регистрация:
    23 авг 2020
    Сообщения:
    233
    а некторых книгах их советуют складывать
    --- Сообщение объединено, 9 фев 2026 в 16:39 ---
    из книги: "Йо Ван Гуй Программирование
    на ассемблере x64
    От начального уровня
    до профессионального
    использования AVX"
    или я не правильно понял ?
     
  12. GRAFik

    GRAFik Active Member

    Публикаций:
    0
    Регистрация:
    14 мар 2020
    Сообщения:
    369
    Скорее всего неправильно.
    Написано же:
    sub rsp,32+56+8 ; Скрытое пространство + 7 аргументов в стеке + выравнивание.
    Это значит, что в функции 11 агументов. Но нужно еще смотреть в каком состоянии стек. Если в адресе в конце 8 , то 8-ку прибавлять не нужно. Возврат из функции и будет этой 8-кой. Итого, что имеем? 32 +56+8=96, а 96 делится на 16 без остатка - значит с выравниванием, полный порядок. По-моему, как-то так... :)
     
  13. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    219
    Стоит уточнить контекст этого термина. По сути складывать можно применить как раз к подходу поиска максимума. Все аргументы всех вызовов функций складываются в один набор слотов.
     
  14. Marylin

    Marylin Active Member

    Публикаций:
    0
    Регистрация:
    17 фев 2023
    Сообщения:
    294
    У майков всё через известное место.
    К примеру зачем нужен _fastcall с аргументами через регистры, если всё-равно на входе в ту-же оконную процедуру приходится сохранять RCX,RDX,R8,R9 в стеке, для чего собственно и резервируются 20h байт?

    Код (ASM):
    1. proc  DialogProc hwnd, msg, wparam, lparam
    2.           mov     [hwnd],rcx
    3.           mov     [msg], rdx
    4.           mov     [wparam],r8
    5.           mov     [lparam],r9
    6.  
    7.           cmp     [msg],WM_INITDIALOG
    8.           je      @init
    9.           cmp     [msg],WM_COMMAND
    10.           je      @command
    11.           cmp     [msg],WM_CLOSE
    12.           je      @close
    13.           jmp     @next
    14. ;......
    15. endp
    Кстати с ручным выделением фрейма типа sub rsp,xx по схеме выше имеется известный глюк. Так, если зарезервировать фрейм для 7-ми аргументов sub rsp,7*8 +8, и внутри процедуры вызвать какую-нибудь функу рантайма типа printf() с (!)более чем 7 аргументов, то приложение рухнет, т.к. получим перезапись адреса-возврата.

    Код (ASM):
    1. start: push    rbp
    2.  
    3.        sub     rsp,4*8
    4.        invoke  MessageBox,0,0,0,0
    5.        call    Example    ;<-------- Ошибка!
    6.        add     rsp,4*8
    7.  
    8.        invoke  ExitProcess,0
    9. ;---------------------------
    10. proc Example
    11.       cinvoke  printf,<'%x.%x.%x.%x.%x.%x.',0>,\
    12.                        rax,rbx,rsi,rdi,rbp,rsp
    13. endp
    В fasm имеется директива frame/endf, которая на автомате выделяет один общий фрейм для всех (заключённых в этот блок) апи. Но и она не спасает, если вызывать свою процедуру указанным выше способом.
     
  15. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    566
    Необходимость сохранть регистры в зарезервированных слотах возникает только в том случае, если первые четыре аргумента требуются и после вызова какой-нибудь дочерней функции. Но если все вычисления с ними сделать до вызова первой функции, и после нее они больше не нужны - то вполне себе получается хорошая выгода по быстродействию. А особенно хорошая, если дочерняя функция первыми аргументами принимает те же самые и в том же самом порядке. Что вполне может быть реальным случаем, если текущая функция - какая-то обрабатывающая обертка для дочернего вызова (хотя компилятор с С++ это вообще разрулит скорее всего инлайнами).
     
  16. Marylin

    Marylin Active Member

    Публикаций:
    0
    Регистрация:
    17 фев 2023
    Сообщения:
    294
    Применительно к оконной процедуре (про которую в общем-то и тред) это исключено.
    Более того, в системе есть куча и других апи, которые пихают аргументы из регистров обратно в стек:

    Код (Text):
    1. 0:000> u CreateServiceA
    2. sechost!CreateServiceA:
    3. 000007fe`fe1b75e8   4c8bdc          mov   r11,rsp
    4. 000007fe`fe1b75eb   45894b20        mov   dword ptr [r11+20h],r9d
    5. 000007fe`fe1b75ef   4d894318        mov   qword ptr [r11+18h],r8
    6. 000007fe`fe1b75f3   49895310        mov   qword ptr [r11+10h],rdx
    7. 000007fe`fe1b75f7   49894b08        mov   qword ptr [r11+8],rcx
    8. 000007fe`fe1b75fb   53              push  rbx
    9. 000007fe`fe1b75fc   56              push  rsi
    10. 000007fe`fe1b75fd   57              push  rdi
    11. ;.......
     
  17. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    566
    Это еще почему? Что мешает в оконной процедуре проверить, что пришедшее сообщение не является одним из интересующих нашу специфическую логику окна (это вполне возможно без порчи регистров) и сразу вызвать DefWindowProc? Там даже регистры не придется заполнять, потому что набор аргументов тот же самый - RCX/RDX/R8/R9 сразу без изменений уйдут в
    DefWindowProc (естественно, слоты для них все равно придется зарезервировать, но это всего одна команда SUB SP, которая и так в большинстве случаев уже есть в прологе функции).
     
  18. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.172
    Dmitry_Milk,
    я вообще делаю не call, а jmp DefWindowProc (leave/jmp NtdllDefWindowProc_)
     
  19. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    566
    Ну да, можно и так. Главное не забыть вернуть стек в исходное состояние ДО jmp :)
     
  20. Marylin

    Marylin Active Member

    Публикаций:
    0
    Регистрация:
    17 фев 2023
    Сообщения:
    294
    Ну если в окне всего одна кнопка и больше нет элементов управления, то ничто не мешает.
    Однако в реальных программах вызываются сотни апи, и куда тогда сохранять эти регистры?