Чисто для такой демки ещё можно один раз выделить общий шадов, но в больших проектах категорически не рекомендуется так делать. Стек не понимает шуток и если внутри этого блока вы запросите внешнюю процедуру, то ситуация может выйти из под контроля, отправив адрес-возврата к чертям. Особенно если из тушки блока вызываются функи с переменным числом параметров, как в msvcrt.dll или wsprintf() из user32.dll. Поэтому общий шадов плохая практика, и лучше выделять по 4*8 байт при каждом вызове api, о чём и говорит Ахимов. Код (ASM): sub rsp,80 ; + шадов для 10 аргументов ;....... call MessageBox() ; всё норм call MyProc ; выход из блока ;....... add rsp,80 ; - шадов call ExitProcess ;----------------- proc MyProc call wsprintf(...) ; 6 аргументов @cycle: call wsprintf(...) ; 6 аргументов = затёрли адрес-возврата call MessageBox(...) loop @cycle ret endp А 300 строк кода для обычного окна с двумя пимпами это конечно круто (интересно, можно накодить это-же за 600 строк). У nasm'a нет своих структур чтоли? Иначе он тупо не предназначен для х64, и инструмент нужно менять. И зачем создавать буттоны в динамике, когда можно описать их в ресурсах? Тогда всё это хозяйство не займёт и 50 строк, например на fasm'e. Код (ASM): format pe64 gui 6.0 include 'win64ax.inc' entry start ;//----------------- section '.text' code readable executable start: sub rsp,8 invoke GetModuleHandle,0 invoke DialogBoxParam,rax,37,0,DialogProc,0 invoke ExitProcess,0 proc DialogProc uses rsi rdi rbx, hWnd,Msg,wParam,lParam mov [hWnd], rcx mov [wParam],r8 cmp edx,WM_COMMAND je @command cmp edx,WM_CLOSE je @close xor rax,rax jmp @ret @command: cmp [wParam],BN_CLICKED shl 16 + IDCANCEL je @close cmp [wParam],BN_CLICKED shl 16 + IDOK jne @next invoke MessageBox,0,'Hello World!','Example',0 jmp @next @close: invoke EndDialog,[hWnd],0 @next: mov rax,1 @ret: ret endp ;//-------------------- section '.idata' import data readable writeable library kernel32,'kernel32.dll',user32,'user32.dll' include 'api\kernel32.inc' include 'api\user32.inc' ;//-------------------- section '.rsrc' resource data readable directory RT_DIALOG, dialogs resource dialogs,37,LANG_ENGLISH + SUBLANG_DEFAULT,form dialog form, 'PE64 Example',0,0,130,030, WS_CAPTION + WS_SYSMENU + DS_CENTER dialogitem 'BUTTON','Ok', IDOK, 010,010,50,14,WS_VISIBLE + BS_DEFPUSHBUTTON dialogitem 'BUTTON','Exit',IDCANCEL,070,010,50,14,WS_VISIBLE + BS_PUSHBUTTON enddialog
Тимур, давайте чисто для эксперимента создадим такой пример, где будет видно что ситуация вышла из под контроля и общий шадов - это плохая практика. Чтобы это было не на словах и теории, а на уровне практики. Дело же не в том сколько строк у тебя в исходном коде, а в том в какой размер exe-файла компилируется этот исходнк. Можно вынести все во внешние инклуды, DLL-ки и код в исходнике будет состоять из каких-нибуть 10 или 20 строк. Тут нужно брать конкретый пример и делать выводы, что что-то на том же NASM x64 - невозможно реализовать, а на FASM64 - без проблем. И исходя из конкретного примера и реализации всего этого на практике делать выводы, что FASM64 - это круто и профессионально, а NASM x64 - годится только для каких-нибудь простеньких студенческих примеров.
Marylin, > зачем создавать буттоны в динамике Такое было тз: Тоесть принято что если асм и простое => обучающий семпл => нужна оконная процедура, обработка сообщений. Диалоги/ресурсы не требовались, как и ограничение по размеру: Короче нужно четко техзадание формулировать. На счет стека, абсолютно согласен: Он не знает интернал апи и не может криво вызвать, все согласно специф. ps. Можно дать изучить интернал, он соберет используя его, только в этой версии и будет работать. --- Сообщение объединено, 27 апр 2026 в 12:50 --- ..
Я не собираюсь вам что-то доказывать, а просто констатирую факт, с которым сталкивался. Хотите выделяйте один шадов на входе, мне как-то без разницы, но такие ошибки трудно потом вылавливать. Когда у вас будет в коде 5/10 тыс строк, тогда и можете нарваться на глюк. Компилятор должен работать на вас, а не вы на него. Я говорю про инструмент, у которого должно быть под катом всё, включая инклуды. Возьмите теже плюсы - там вообще нет call/invoke, а сразу имя api. Видно, что синтаксис для программиста, чтобы он сосредотачивался именно на алгоритмах, а не заполнении вручную 2-метровых простыней структур.
А что за факт, если не секрет? Давайте попробуем этот факт проанализировать. Ну, 10 тыс. строк кода я бы, наверно, и не рискнул писать на ассемблере, а взял бы, возможно, СИ. Тимур, я переслал ваш пост одному программисту, который хорошо разбирается в 64 битной архитектуре и в 64 битных ассемблерах и вот что он ответил:
Мда... что-то ваш надёжный источник вызывает сомнения - лучше бы он не упоминал раскрутку стека. Автор этих строк походу сам запутался в механизмах обработки исключений х32/64. Если вы вызываете api, то она лежит в dll, а dll имеет секции .pdata/.xdata, где и хранится инфа о раскрутке стека. У вызвавшей исключение функции свой пролог mov rbp,rsp, а потому значение внешнего rsp ей абсолютно не интересно. Аналогичную ситуацию наблюдаем и в случае, когда внутри одной api вызывается другая - эта вложенная так-же имеет свой пролог, и о внешнем rsp уже никто и не помнит. Всё-что нужно для обработки исключений х64, хранится далеко от текущего call/rsp - в отдельной секции .pdata, которая содержит массив структур RUNTIME_FUNCTION - каждая описывает одну api в данной dll (сколько fn, столько и структур). Все эти записи отсортированы по "BeginAddress", что позволяет ОС быстро находить функцию по адресу вызвавшей исключение инструкции. Код (Text): 0: kd> dt _runtime_function ole32!_RUNTIME_FUNCTION +0x000 BeginAddress : Uint4B ; RVA начала функции +0x004 EndAddress : Uint4B ; RVA её конца +0x008 UnwindData : Uint4B ; RVA на секцию .xdata, где лежит подробная инфа по раскрутке именно этой функции. 0: kd> Секция .xdata может быть отдельной, или встроенной прямо в .pdata. Когда происходит исключение, система смотрит на текущий адрес инструкции, находит через .pdata соответствующую api, читает поле UnwindData и прыгнув по нему в .xdata, попадает в структуру UNWIND_INFO - это и есть мозг раскрутки стека глючной функции. Во вложенной UNWIND_CODE лежат байт-коды, которые и описывают фактические детали обратного трейса: Код (Text): 0: kd> dt _unwind_info ole32!_UNWIND_INFO +0x000 Version : Pos 0, 3 Bits +0x000 Flags : Pos 3, 5 Bits +0x001 SizeOfProlog : UChar +0x002 CountOfCodes : UChar +0x003 FrameRegister : Pos 0, 4 Bits +0x003 FrameOffset : Pos 4, 4 Bits +0x004 UnwindCode : [1] _UNWIND_CODE 0: kd> dt _unwind_code ole32!_UNWIND_CODE +0x000 CodeOffset : UChar +0x001 UnwindOp : Pos 0, 4 Bits +0x001 OpInfo : Pos 4, 4 Bits +0x000 FrameOffset : Uint2B Таким образом .pdata служит каталогом для поиска функций, а .xdata детальной картой для каждой из них. Эта пара таблиц полностью заменила старый механизм связанного списка FS:[0], и позволяет: 1. Восстановить значения NonVolatile регистров. 2. Найти текущий кадр стека rsp/rbp. 3. Перейти к вызвавшей функции. Отладчик WinDbg на команду .fnent отзывается инфой о раскрутки стека для любой х64 api, и повторюсь она никак не связана с инструкцией call и текущим значением rsp - это заложено в генах самой API-функции: Код (Text): 0:000> .fnent CreateFileA Debugger function entry 00000000`01da4a20 for: (00000000`77371720) kernel32!CreateFileA | (00000000`77371810) kernel32!InternalFindAtom Exact matches: kernel32!CreateFileA = <no type information> BeginAddress = 00000000`00021720 EndAddress = 00000000`00021810 UnwindInfoAddress = 00000000`00101d2c Unwind info at 00000000`77451d2c, 14 bytes version 1, flags 0, prolog 14, codes 8 00: offs 14, unwind op 4, op info 6 UWOP_SAVE_NONVOL FrameOffset: 80 reg: rsi. 02: offs 14, unwind op 4, op info 5 UWOP_SAVE_NONVOL FrameOffset: 78 reg: rbp. 04: offs 14, unwind op 4, op info 3 UWOP_SAVE_NONVOL FrameOffset: 70 reg: rbx. 06: offs 14, unwind op 2, op info b UWOP_ALLOC_SMALL. 07: offs 10, unwind op 0, op info 7 UWOP_PUSH_NONVOL reg: rdi. ---------------------------- 0:000> .fnent CreateWindowEx Debugger function entry 00000000`01da4a20 for: (00000000`77480294) user32!CreateWindowEx | (00000000`7748051c) user32!_fnINOUTNCCALCSIZE Exact matches: user32!CreateWindowEx = <no type information> BeginAddress = 00000000`00010294 EndAddress = 00000000`0001051c UnwindInfoAddress = 00000000`00090b34 Unwind info at 00000000`77500b34, 1e bytes version 1, flags 0, prolog 1f, codes d 00: offs 1f, unwind op 4, op info 6 UWOP_SAVE_NONVOL FrameOffset: 168 reg: rsi. 02: offs 1f, unwind op 4, op info 5 UWOP_SAVE_NONVOL FrameOffset: 160 reg: rbp. 04: offs 1f, unwind op 4, op info 3 UWOP_SAVE_NONVOL FrameOffset: 158 reg: rbx. 06: offs 1f, unwind op 1, op info 0 UWOP_ALLOC_LARGE FrameOffset: 120. 08: offs 18, unwind op 0, op info f UWOP_PUSH_NONVOL reg: r15. 09: offs 16, unwind op 0, op info e UWOP_PUSH_NONVOL reg: r14. 0a: offs 14, unwind op 0, op info d UWOP_PUSH_NONVOL reg: r13. 0b: offs 12, unwind op 0, op info c UWOP_PUSH_NONVOL reg: r12. 0c: offs 10, unwind op 0, op info 7 UWOP_PUSH_NONVOL reg: rdi. Что касается шадов, то макрос fasm'a invoke сам резервирует стек нужного размера по кол-ву параметров функции, и отнюдь не потому, что он такой глупый. Во первых это исключает буквально всякого рода ошибки, а во вторых строго придерживается стандарта _fastcall, который написал не я, и не вы. Когда в коде 2 функции, можно рассчитать макс кол-во параметров из общего пула и выделить общий фрейм, но когда функций 100 и более, это уже проблема.
Имхо, хеллворд здорового человека. Всего 2кб в современных реалиях. Да этож круто! Код (ASM): format pe64 gui 6.0 include 'win64ax.inc' entry start ;//----------------- section '.text' code readable executable start: sub rsp,8 invoke GetModuleHandle,0 invoke DialogBoxParam,rax,37,0,DialogProc,0 invoke ExitProcess,0 proc DialogProc uses rsi rdi rbx, hWnd,Msg,wParam,lParam mov [hWnd], rcx mov [wParam],r8 cmp edx,WM_COMMAND je @command cmp edx,WM_CLOSE je @close xor rax,rax jmp @ret @command: cmp [wParam],BN_CLICKED shl 16 + IDCANCEL je @close cmp [wParam],BN_CLICKED shl 16 + IDOK jne @next invoke MessageBox,0,'Hello World!','Example',0 jmp @next @close: invoke EndDialog,[hWnd],0 @next: mov rax,1 @ret: ret endp ;//-------------------- section '.idata' import data readable writeable library kernel32,'kernel32.dll',user32,'user32.dll' include 'api\kernel32.inc' include 'api\user32.inc' ;//-------------------- section '.rsrc' resource data readable directory RT_DIALOG, dialogs resource dialogs,37,LANG_ENGLISH + SUBLANG_DEFAULT,form dialog form, 'PE64 Example',0,0,130,030, WS_CAPTION + WS_SYSMENU + DS_CENTER dialogitem 'BUTTON','Ok', IDOK, 010,010,50,14,WS_VISIBLE + BS_DEFPUSHBUTTON dialogitem 'BUTTON','Exit',IDCANCEL,070,010,50,14,WS_VISIBLE + BS_PUSHBUTTON enddialog --- -- Прогр. как раз такая сфера где теорию можно проверить на практике и узнать кто обосрался. Забавно когда в этой сфере воз никают ожесточеные споры. Достаточно собрать обе идеи и сравнить. --- Сообщение объединено, 28 апр 2026 в 01:51 --- --- -- Хочу одну фишку рассказать до которой многие не допирают. Обьясню для си подобных яп на примере delphi 7, другого под рукой нет. Очень многие люди ненавидят писать приложения на WinApi. В обрабтке сообщений все традиционно делают так: Код (Pascal): function DialogProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LongBool; stdcall; begin Result := False; // по умолчанию сообщение не обработано case uMsg of WM_COMMAND: begin if (LOWORD(wParam) = IDOK) then begin MessageBox(hWnd, 'Hello World!', 'Example', MB_OK); Result := True; // сообщение обработано end else if (LOWORD(wParam) = IDCANCEL) then begin EndDialog(hWnd, 0); Result := True; end; end; WM_CLOSE: begin EndDialog(hWnd, 0); Result := True; end; end; end; Можно сделать по другому, если попробуете на практике, поймете почему это правильно: Делаем такую функцию: Код (Pascal): function ShowMsg(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): bool; begin Result := False; if (LOWORD(wParam) = IDOK) then begin MessageBox(hWnd, 'Hello World!', 'Example', MB_OK); Result := True; end else if (LOWORD(wParam) = IDCANCEL) then begin EndDialog(hWnd, 0); Result := True; end; end; //ShowMsg Осн. фишка до которой большинство не допирает, это сделать ее такой, чтобы в нее передавалис все hWnd, wParam, lParam: И для других функций точно так же: Код (Pascal): function DialogProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LongBool; stdcall; begin Result := False; case uMsg of WM_COMMAND: ShowMsg(hWnd, uMsg, wParam, lParam); WM_CLOSE: CloseApp(hWnd, uMsg, wParam, lParam); end; end; //DialogProc Тогда основная функция будет выглядеть так: Код (Pascal): function DialogProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LongBool; stdcall; begin Result := False; case uMsg of WM_COMMAND: ShowMsg(hWnd, uMsg, wParam, lParam); WM_CLOSE: CloseApp(hWnd, uMsg, wParam, lParam); end; end; //DialogProc Когда типов сообщений много, это очень удобно. Нет длинной портянки в DialogProc. Становится в кайф писать WinApi приложения. --- Сообщение объединено, 28 апр 2026 в 02:02 --- --- -- Не могу отредактировать пред. сообщение. Основной смысл, чтобы главная функция стала такой: Код (Pascal): function DialogProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LongBool; stdcall; begin Result := False; case uMsg of WM_COMMAND: ShowMsg(hWnd, uMsg, wParam, lParam); WM_CLOSE: CloseApp(hWnd, uMsg, wParam, lParam); end; end; //DialogProc Нужно в другие функции передавать все параметры: Код (Pascal): function CloseApp(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): bool; function ShowMsg(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): bool; Тогда весь код можно вынести в них и от DialogProc не будет тошнить. --- Сообщение объединено, 28 апр 2026 в 02:13 --- + удобнее искать ошибки кода все в отдельных функциях. --- Сообщение объединено, 28 апр 2026 в 02:56 --- --- -- --- Сообщение объединено, 28 апр 2026 в 03:07 --- Ресурсы довольно забористая вещь, сделал Edit и Button на окне. DialogExample 2.
Именно про это я говорю (да, это мой пост). Как оказалось, нельзя использовать этот макрос для всего кода программы от чердака и до подвала - нужно заворачивать в этот блок лишь отдельные участки, в которых параметры всех api известны. Если внутри блока frame/endf имеется выход за его пределы, то макрос не может уже вычислить размер необходимого фрейма, и прожка может упасть в самый неподходящий момент. Я сталкивался с таким поведением, поэтому и предупреждаю. Если-же последовательность вызовов api конечна, то профит от макроса несомненно есть.
Добавление информации в ListView через MainMenu с пом. ресурсов. -- При клике на добавленный элемент выводится его текст. -- Можно удалить выбранный элемент. -- Можно вывести содержимое всех элементов ListView. -- Очистить все элементы.
Ahimov, у меня такое впечатление, что вы где-то в подвале держите в рабстве кучку китайцев и они вам бесплатно пишут ассемблерные листинги для разных диалектов ассемблеров. А нам, соответственно, вы пытаетесь внушить теорию, про то, как успешно вы обучили бота по имени Дипсик. :=) Давайте я вам два листинга (source) для NASM x64 скину. Пусть ваши обученные китайцы их оценят и выдадут вердикт - какой source для NASM x64 более правильный с точки зрения спецификации. Можно такое осуществить ?
Довольно компактный исходник получается у Дельфи, но это отражается на размере бинаря - имхо 18К это много для такого функционала. Наверное можно было удалить секции .bss/tls/rdata/reloc, тогда и похудел-бы немного exe. Для сравнения вот ListView на fasm'e - создаёт список всех файлов/папок в текущем дире, имеется сортировка по столбцам с иконкой/маркером, строка статуса в подвале, подключён манифест для современного оформления элементов окна (подсветка при наведении курсора, округленные края буттонов), и чередование строк в ListView. На выходе получил exe размером 8К. Интересно, сколько будет весить такая-же хрень на плюсах? --- Сообщение объединено, 29 апр 2026 в 06:59 --- А в чём проблема самому вскормить эти листинги дипсику - он вас забанил, или что-то другое? Ахимов пишет: -"Дипсик думал 86 сек, и т.п.".. Не знаю, но у меня он отвечает мгновенно. Как-то я ему дал линк на свои статьи (чтобы он подкинул мне новых тем), так он все их перечитал за 2 секунды, и в качестве доказательства привёл цитаты из каждой из них. Поэтому думаю проблема с тормозами не на стороне дипсика, а возможно Ахимов заходит с телефона, т.к. с компа всё летает.
А где взять msvcrt.inc для фасма ? Мне кажется вы не совсем понимаете в чем смысл delphi. К примеру есть задача: написать файловый менеджер. Если начать с языка высокого уровня то доведя исходник до ума будет ясно что именно делать на асме/си с компактным размером и без багов. Delphi это что-то типа фонаря в темной комнате. После этого будет заранее понятно какие должны быть стр. блоки. В принципе можно и на ощупь двигаться. Я не сторонник этого. Есть задачи которые ассоциативный ум впринципе плохо решает.
Вот.. но там я вроде ещё добавлял какие-то структуры, правда не помню в какой инклуд. Если не соберётся скажи, на какой ругается, и я вышлю его.