push 0 = 2 байта push ebx = 1 байт + скорость +8 регистров SSE еще неизвестно, что с fpu и соотв. mmx
thx. А, ну да. Только почему ? [ADD] )) Сначала в отладчике посмотрел, что и вправду mov eax, 0 по размеру больше, чем mov eax, ebx. Потом спросил про push 0 и push ebx. Вот нет чтобы сразу в отладчике посмотреть =))) Так нет, сначала спросил - а потом посмотрел Зато "что быстрее" узнал [/ADD]
Ну, не написано, может еще fpu-регистры появились. А если появились, то увеличилось и кол-во mmx регистров, т.к. они представляют собой младшие 64 бита fpu-регистров.
Так наверное на сайте AMD должно быть написано? =) Вот... раньше вроде работала... AMD64 Architecture Programmer's Manual Volume 1:Application Programming http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_doc s/24593.pdf
cresta Вспоминаем азы программирования на ассемблере: mov eax,[var] - поместить в EAX значение по адресу переменной var mov eax,var - поместить в ЕАХ адрес переменной var В масме квадратные скобки не обязательны, а для получения адреса переменной используют ключевое слово offset. В тасме (в режиме IDEAL - единственно правильном и корректном режиме) — то же самое, как и в фасме. fasm не убог, это давно стало истиной де-факто. А masm отличается большим количеством багов и именно "убогими" возможностями макро. LOL Да. Да, но r12-r15 "зарезервированы" для сохранения (как в stdcall ebx,esi,edi. А добавлены только XMM, количество FPU/MMX регистров осталось прежним.
IceStudent mov eax,[var] - поместить в EAX значение по адресу переменной var mov eax,var - поместить в ЕАХ адрес переменной var вспоминаем азы программирования: mov eax, dword ptr[var] mov eax, var обе инструкции загружают в eax дворд, расположеный по адресу переменной var. Чтобы загрузить адрес переменной, а не дворд по этому адресу, умные дяди специально придумали инструкцию lea, LoadEffectiveAddress. lea eax,var - помещает в eax адрес переменной var. можно воспользоваться и таким способом: mov eax,offset var. P.S. это дурное влияние фасма сказывается. вы уже не помните, что такое lea и что такое mov, какая разница между ними. А бажный фасм со своим кривым пониманием инструкций действительно убогий. Да и пример с ret в этом топике наглядно показывает это.
cresta Сомневаюсь, что IceStudent этого не помнит =) А вот я... Я начал изучать асм по рассылке Калашникова. Правда всю ее я не прочитал Но он использовал для загрузки адреса именно offset вместе с mov (№004 - "(3) mov dx,offset My_string") Так что ФАсм здесь не причем И lea во всей рассылке встречалась ровно один раз ("lea dx,Mess_hello ;!!! LEA DX вместо MOV DX, OFFSET") =) А вот баги - это действительно неприятно :/ Вот чего я толком не понял, так это про эти ret / retn 0 По идее (ну... как я думаю) асм должен заменять ret на retn или retf в зависимости от процедуры. Почему он этого не делает? А также - почему мы пишем retn 0 ??? 0 указывет на то, сколько надо убрать из стэка байт. Т.к. у n_sub нет параметров, то пишется 0 ? Тогда почему 0, ведь call должен ложить в стэк адрес возврата и когда мы возвращаемся обратно, то он должен оттуда убираться, а при retn 0 ничего не будет убираться из стэка? Попытаюсь высказаться более понятно Итак. При вызове процы split в стэк кладется алрес возврата. Далее идут различные операции. Затем call'ом вызывается n_sub и при этом в стэк кладется еще один адрес возврата. Так? Затем снова к.л. действия и наконец должен идти возврат ret'ом с убиранием из стэка одного адреса возврата. Но при retn 0 (имхо) ничего не убирается. Как так? Будто call не ложит ничего в стэк Сорри, за громадное количесвто глупых вопросов...
cresta Вот именно, но синтаксис кардинально отличается (видимо, MS позаботилась о ленящихся либо чтобы легче было си-программерам). О ней никто не забывал, но это "runtime" вычисление, а зачем заставлять процессор это делать, если можно получить адрес переменной при компиляции? Повторяю, синтаксис фасма базируется на синтаксисе тасма в режиме IDEAL. Зазгляните в мануалы интела и посмотрите, какой у них синтаксис. Явно не masm. Между прочим NRET я для масма написал себе, т.к. он так же генерил освобождение стекового фрейма.
Вот что сгенерил масм на код процедуры split из первого ответа: Код (Text): 0040179A . 56 PUSH ESI 0040179B . 57 PUSH EDI 0040179C . FC CLD 0040179D . 8D35 CD304000 LEA ESI,DWORD PTR DS:[4030CD] 004017A3 . 8D15 D8304000 LEA EDX,DWORD PTR DS:[4030D8] 004017A9 > 8BFA MOV EDI,EDX 004017AB . E8 0A000000 CALL InputBox.004017BA 004017B0 . 3C 20 CMP AL,20 004017B2 . 8D52 0A LEA EDX,DWORD PTR DS:[EDX+A] 004017B5 .^74 F2 JE SHORT InputBox.004017A9 004017B7 . 5F POP EDI 004017B8 . 5E POP ESI 004017B9 . C3 RETN 004017BA /$ AC /LODS BYTE PTR DS:[ESI] 004017BB |. AA |STOS BYTE PTR ES:[EDI] 004017BC |. 3C 20 |CMP AL,20 004017BE |. 75 04 |JNZ SHORT InputBox.004017C4 004017C0 |. C607 00 |MOV BYTE PTR DS:[EDI],0 004017C3 |. C3 |RETN 004017C4 |> 84C0 |TEST AL,AL 004017C6 |.^75 F2 \JNZ SHORT InputBox.004017BA 004017C8 \. C3 RETN Где здесь освобождение фрейма по ret? В отличие от фасма, масм отсебятину не втыкает к код: ему сказали - ret, они и сделал ret (retn). О ленящихся заботится томаш со своим фасмом: специально для них mov eax, dword ptr[var] превратили в mov eax,[var], что неявно может привести к ошибке (особенно у начинающих), связанной с размером загружаемого операнда. Масм такую херню не позволит, тем самым предотвращая возможные ошибки.
Если же волнует освобождение стекового фрейма в процедурах, которые принимают через стек параметры (как например у LOL), то не надо проецировать на масм ограниченность фасма и изобретать макросы NRET - в масме есть такая штука: option prologue : none option epilogue : none и никаких стековых фреймов не формируется и не освобождается.
Код (Text): masm: func proc var ret func endp fasm: proc func var ret endp masm: 0001100B /$ 55 PUSH EBP 0001100C |. 8BEC MOV EBP,ESP 0001100E |. C9 LEAVE 0001100F \. C2 0400 RETN 4 fasm: 0001105F /$ 55 PUSH EBP 00011060 |. 89E5 MOV EBP,ESP 00011062 |. C9 LEAVE 00011063 \. C2 0400 RETN 4 В данном случае код одинаков. Про prologue/epilogue я знаю, но NRET использовался в большой функции, где самому следить за стеком было бы накладно, поэтому от стекового фрейма не отказывался. Хотя в фасме возможен фрейм на esp-регистре, что и сделал S_T_A_S_ пару лет назад. В размер ЕАХ не ошибится никто, следовательно dword ptr избыточен в данном случае. Код (Text): varb db 0 vard dd 0 mov eax,[varb] mov eax,[vard] Результат компиляции для масма и фасма одинаков: успешен при vard и ошибка размера операнда при varb. Где предотвращение масма? И от чего оно? В фасме она тоже есть. Где ограниченность фасма? Она лишь в одном (пока): отсутствие отладочной информации, что связано с его кроссплатформенностью. Да и то, она обходится.
Ну вот опять никчемные дискусии - уже не раз вопрос "правильного" синтаксиса пережевывали и все равно каждый остался при своем мнении. Но я тоже не могу удержаться от флейма и не вставить свое словечко ) Можно говорить о "чистом асме" максимально приближенном к железу и оперирующим только адресами\смещениями и мнемониками инструкций\регистров x86, а можно говорить об Х-асме как языке программирования со своими прибамбасами и элементами HLL. "Железному" асму неведомы абстрактные типы и объявления переменных, поэтому например строчки Код (Text): myInt dd 0 myStr db "Hello, World!",0 "железный" асм должен 1) интерпретировать просто как директиву выделения и инициализации в области данных или кода соответсвующего количества байт и 2) myInt\myStr должен рассматривать не как какие-то "переменные" определенного типа, а просто как метки, т.е. мнемонические обозначения адресов памяти. Соответсвенно и мнемоники mov eax,myInt и mov eax,myStr означают загрузку в регистр адреса соответсвующей метки, а инструкции mov eax,[myInt] и mov eax,[myStr] - загрузку в регистр 4-х байт по соответсвующим адресам. С точки зрения железа все просто, логично и однозначно. Например, для чего спрашивается специально указывать mov eax,dword ptr [myStr] если в IA-32 есть один единственный вариант трактовки mov r32,m32 и ничего другого кроме dword этой интсрукцией в r32 засунуть в принципе невозможно Но это невозможно для тех, кто опирается на железо и руководствуется первоисточниками - мануалами от Intel и AMD. А как же быть с "широкими массами", привыкшими к HLL, к типированным переменным, к тому что компилятор за них все проверит и подскажет что не так ? Да никак, просто создать убогий гибрид куцего HLL и железного асма Пусть нелогично, зато привычно и необычно, есть над чем глову поломать и толстые книги пописать ) Привычно в инструкциях mov eax,myInt и необычно в lea eax,[myInt+1] или mov al,byte ptr [myInt+1] - в первом случае myInt это значение переменной, а во втором - адрес, какое-то "раздвоение личности". Гибрид он и есть гибрид, и хваленая проверка типов на деле не всегда логично работает. Например, ml.exe (v 6.14.8444) нормально проглатывает mov eax,[myInt+1] бе всякого dword ptr (хотя именно здесь и может закрасться ошибка), но почему-то ругается на mov al,[myInt+1] (хотя по железной логике x86 тут ошибки быть не может и никакого byte ptr тут по идее не требуется). Ну и разумеется отдельная песня о ret. Тут уж я вообще не понимаю, как можно переопределять макросами инструкции x86 - остается выбросить на помойку мануалы Intel и AMD и заново учиться прибамбасам и загадочным ноу-хау очередного Х-асм. Неужели нельзя спец.мнемонику придумать: не знаю как в последних версиях фасма, а в 1.56 вроде как инструкцию ret = retn никто не переопределял и для выхода из stdcall-процедуры использовался макрос return. Это как раз великий заботливый masm всегда грешил такой подменой понятий. Неужели и у Томаша в погоне за "бездумно-ленивым" электоратом крыша поехала ))
IceStudent интересно взял мою фразу из поста о процедуре без параметров, откомпилировал процедуру с параметрами и предъявляешь как одинаковый код ))) Я уже упоминал отдельным постом, что если есть параметры, то для этого в масме существует option prologue. А если в фасме есть такая директива, почему же не посоветовал автору топика использовать её? Речь не о размере eax, а о размере var. И очень можно даже ошибиться, если переменных сотня - другая, и они разных типов. И dword указывает на размер var, а не размер eax. leo Если myInt - это dd, то данная метка известна компилятору как dword. Потому и ругается, что пытаешься в al затолкать дворд. Если в программе сотня-другая переменных разной размерности, ты что, помнишь их всех? Вот для этого и существует проверка, т.к. можно просто ошибиться в размере переменной. И вообще, хотел бы я посмотреть как бы вы отлаживали программу, в которой гипотетически заблокирована ошибка error A2070: invalid instruction operands
leo Народ выпросил совместимость с масмом. Его синтаксис (правда, подключаемый), определение структур, и переопределение ret тоже Так фасм ведёт себя точно так же. cresta С параметрами или без - насчёт этого я в курсе, я про своё писал, почему не стал использовать без пролога/эпилога. Потому что тогда теряется удобство стекового фрейма.
Никак не ожидал, что из банального вопроса получится такая оживленная дискуссия )) Ну раз разбираться, то разбираться до конца 1. Что такое стэковый фрейм? В нем хранятся переменные, переданные процедуре. Хотя... они ж в стэке. Локальные переменные или что? 2. Ну а на мой вопрос так ни кто и не ответил (( Правда я его неправильно задал Почему вместо ret генерируется leave / retn 0C? И каким образом это связано со стэковым фреймом? "Команда leave выполняет действия, противоположные действиям последней команды enter. Она логически уничтожает созданный командой enter стековый кадр со всеми содержащимися в нем локальными переменными и подготавливает стек к выполнению команды irct, завершающей переход в вызывающую процедуру". Почему в отладчике нету команды enter и irct ? Вместо enter'а этот стэковый фрейм создает call ? 3. fasm: mov eax, DWORD [var] masm: mov eax, DWORD PTR [var] Это же вроде одно и тоже? Тогда о чем спор? Боишься не уследить за размерами всех переменных - пиши там дворд, ворд и т.д. 4. А что она (и ее аналог в фасме) дает-то??? 5. а) А вот в mov al,[myInt+1], имхо, это уже в философию . Согласно определению cresta, а также его же фразе о том, что , это не что иное, как "загрузить в al (размер которого известен) байт расположенный по адресу [myInt+1]" И какая разница какой тип у myInt, ведь myInt указывает в данном случаем всего лишь на адрес! б) Ошибка если переменная myInt имеет размер, например, 1 байт, а асм сочтет ее за дворд? Ну так опять же - myInt указывает на адрес - если хочешь байт, то и пиши byte ptr и т.д. Пожалуйста, помимо споров ответьте и на мои вопросы, ok?
LOL 1. Глянь в книжку. Стековый фрейм - это указатель на стек в момент входа в процедуру, который сохраняется в ebp. К локальным переменным теперь можно обращаться через [ebp-XXX] (адресуя стек выше адреса возврата), а к аргументам - [ebp+XXX]. 2. Забота компилятора. Потому что вместо неё push ebp/mov ebp,esp, что одно и то же. Об опциональности скобок в mov eax,var, что разъяснил leo 4. Не создаёт фрейм, ты сам будешь писать mov eax,[esp+4+N] вместо mov eax,arg1 и следить за стеком (если он изменился, то изменять N в зависимости от разницы в esp на момент входа в подпрограмму.
LOL Скорее тогда наоборот - не пиши ничего. Потому как например есть: Код (Text): var db ? а где-то вдали что-то вроде Код (Text): mov al, [var] or al, al jz anylabel а потом через пару дней тебе вдруг надо изменить на Код (Text): var dd ? ассемблер тебя будет хулить, и будет прав - а вдруг не отловил по ошибке? Размер переменной поменялся, а проверишь только младший байт А вот если написано Код (Text): mov al, byte[var] ассемблер молчит - откуда ему знать что ты имел ввиду? Такие ошибки (по собственному опыту скажу) очень тяжко ловить. С другой стороны если нужен БАЙТ - независимо от размера переменной - пиши байт, и будет щастье! Так что проблемы по-моему нет. Нет. Например во всех вменяемых Це-компиляторах фрейм создается: Код (Text): push ebp mov ebp, esp sub esp, sizeoflocals что полность идентично Код (Text): enter 0, sizeoflocals только эффективней. А вместо leave подставляют Код (Text): mov esp, ebp pop ebp По той же причине. Но если компилятор не только вменяемый, но и оптимизирующий, то он такой фигней вовсе не занимается, а пишет в прологе Код (Text): sub esp, sizeoflocals а в эпилоге Код (Text): add esp, sizeoflocals но считается, что для человека это труднее, поскольку адресация идет через esp, который может меняться на протяжении функции. Хотя я, например, никакой сложности здесь не ощущаю.