-
Допустим у меня есть такая функция с тремя параметрами: function Search(count:integer; keyword:int64; var source):integer; первый параметр, как я понимаю передается через EAX Результат тоже вроде через EAX передается. Третий похоже через EDX А что со вторым? Я не силен в ассемблере, но судя по коду он передается через стек. Если да, то где именно в стеке он находится? насколько я понял, то на вершине стека находится адрес возврата (4 байта), а следом за ним параметр, ближе к вершине более младшие его байты. Правильно? Т.е. такая функция будет работать? (поиск в массиве по полю field. Если надено - возвращается номер элемента массива, если не найдено, то возвращается -1) ...
type TWork=packed record
field:int64;
MSet:TMset;
Probe:Extended;
end;
...
workarr:packed array of TWork;
...
function Search(count:integer; keyword:int64; var source):integer;
label
lbloop, lbcont,lbbreak;
const
step=sizeof(TWork);
asm
push edx;
push ecx;
push ebx;
mov ecx,[esp+$10];
mov ebx,[esp+$14];
dec eax;
lbloop: cmp ebx,[edx+$04];
jnz lbcont;
cmp ecx,[edx];
jz lbbreak;
lbcont: add edx,step;
dec eax;
jns lbloop;
lbbreak:pop ebx;
pop ecx;
pop edx;
end;
-
Глянул скомпилированный код. Оно там вначале добавило: push ebp; // Зачем делать то, что я не просил? mov ebp,esp; push ebx;
и в конце:
mov eax,ebx; //Зачем оно мне result портит? pop ebx; pop ebp; ret $0008; //что за параметр? lea eax,[eax+$00]; //а это вообще что за чудо? или это просто мусор связанный с выравниванием?
-
> ret $0008; //что за параметр?
хотя что это такое вроде понял, если я правильно понял, то это число на которое дополнительно увеличивается ESP после возврата, из-за того что в стеке передавался параметр... А вот как насчет всего остального?
-
Вот так напиши:
function Search2(count:integer; keyword:int64; var source):integer; const step = sizeof(TWork); asm push eax mov ecx, eax mov edx, [edx] @compare: mov eax, [esp + 12] cmp [edx], eax jne @next mov eax, [esp + 16] cmp [edx + 4], eax je @done @next: add edx, step dec ecx jnz @compare mov ecx, [esp] inc ecx @done: pop eax sub eax, ecx end;
-
> mov eax, [esp + 12] > cmp [edx], eax > jne @next > mov eax, [esp + 16] > cmp [edx + 4], eax > je @done
Ну так сам компилятор делает подобным образом... Я хотел сделать побыстрее... Т.е. хранить в регистрах второй параметр, чтобы постоянно его из памяти не дергать. lbloop: cmp eax,[edx+$04];
jnz lbcont;
cmp ecx,[edx];
jz lbbreak; Вроде так заработало...: function Search(count:integer; keyword:int64; var source):integer;
label
lbloop, lbcont,lbbreak;
const
step=sizeof(TWork);
asm
push edx;
push ecx;
mov ebx,eax;
mov ecx,[ebp+$08];
mov eax,[ebp+$0C];
dec ebx;
lbloop: cmp eax,[edx+$04];
jnz lbcont;
cmp ecx,[edx];
jz lbbreak;
lbcont: add edx,step;
dec ebx;
jns lbloop;
lbbreak:pop ecx;
pop edx;
end; Хотя какие-то странности пока имеются... Но возможно они связаны с другими участками кода.
-
> Хотя какие-то странности пока имеются... Но возможно они > связаны с другими участками кода.
А. ну да. понял. я же по массиву вверх иду, а счетчик вниз считает... :)))
-
в регистрах? ну тогда вот так:
function Search2(count:integer; keyword:int64; var source):integer; const step = sizeof(TWork); asm push eax push esi mov esi, [esp + 16] push edi mov edi, [esp + 24] mov ecx, eax mov edx, [edx] @compare: cmp [edx], esi jne @next cmp [edx + 4], edi je @done @next: add edx, step dec ecx jnz @compare mov ecx, [esp + 8] inc ecx @done: pop edi pop esi pop eax sub eax, ecx end;
-
ну и пример вызова:
var workarr: packed array of TWork; begin try SetLength(workarr, 10); workarr[0].field := 456; workarr[7].field := $CDB81F21CDB81F20; Writeln(Search2(10, $CDB81F21CDB81F20, workarr)); // result must be 7
-
> dec ecx > jnz @compare
и еще вопрос: Вроде для таких вещей можно использовать команду loop, если бы она работала с 32-разрядным регистром.
Но вот по информации в инете не могу понять: какой все-таки регистр использует команда loop : CX или ECX?
например как понять вот это:
> Команда LOOP уменьшает регистр-счетчик без изменения какого- > либо из флагов. Затем проверяются условия характерные для > конкретной формы используемой команды LOOP. Если условия > удовлетворяются, то происходит короткий переход на метку, > заданную операндом команды LOOP. > > Если атрибут размера адреса равен 16 бит, то в качестве > счетчика используется регистр CX, иначе — ECX.
Что подразумевается под атрибутом размера адреса?
-
> и еще вопрос: Вроде для таких вещей можно использовать команду > loop, если бы она работала с 32-разрядным регистром.
как правило не используют, она по тактам немного проигрывает.
> Что подразумевается под атрибутом размера адреса?
Префикс, указывающий с каким размером адресного регистра мы работаем.
Вот так цикл будет идти 65538 раз
mov ecx, 65538 @loop: loop @loop
А вот так только два раза (т.к. в регистр CX будет равен двойке):
mov ecx, 65538 @loop: db $67 // Address-size override prefix loop @loop
-
Чем отличаются команды JE от JZ и JNE от JNZ?
-
> add edx, step
и еще: будет ли LEA edx,[edx+step] в данном случае быстрее?
-
Ничем, ну только разве что читабельностью. Для булевых операций обычно примеряют E (после test/cmp), для математических (add/sub/inc/dec) Z. В данном случае вычитается единица, и ожидаем результатом ноль, поэтому JNZ (опкоды у этих инструкций идентичные)
-
> и еще: будет ли LEA edx,[edx+step] в данном случае быстрее?
нет, идентичны по тактам будут.
-
Почему в случае сабжевого кода (пусть он даже и не совсем верный), компилятор добавлял: В начале: push ebp;
mov ebp,esp;
push ebx; и в конце: mov eax,ebx;
pop ebx;
pop ebp;
ret $0008; А в случае [6], только В начале push ebp;
mov ebp,esp;
и в конце:
pop ebp;
ret $0008; т.е. чем вызвана эта самодеятельность компилятора: ...
push ebx;
...
mov eax,ebx;
pop ebx;
... ? и как в будущем предугадать такие козни компилятора?
-
>т.е. чем вызвана эта самодеятельность компилятора: Отладкой. Для Debug Kernel нужно. Точки останова и т.п.
-
Можно внешний obj подтянуть без использования ebp, но там точки останова не будут работать.
-
> dmk © (31.05.16 15:40) [16] > > Можно внешний obj подтянуть без использования ebp, но там > точки останова не будут работать.
я не про ebp (то что компилятор всегда вставляет - то не страшно), а про то, что он вставляет непонятно когда... Иногда вставляет, а иногда нет. Вот это: ...
push ebx;
...
mov eax,ebx;
pop ebx;
...
-
Сложно сказать. У меня Delphi XE6 и такого нет. Только BP, EBP, RBP. Не нравится — вставляй по старинке obj'екты. Компилируй masm или еще чем. Тогда точно будет так как написано. Во времена Trubo Pascal и Delphi 3 я так и делал. Вставлял OBJ.
Кстати проверить еще можно отключив debug. Run without Debugging Shitf+ Ctrl + F9.
-
> Почему в случае сабжевого кода (пусть он даже и не совсем > верный), компилятор добавлял: >
Потому что, по пунктам:
1.
В начале push ebp; mov ebp,esp;
и в конце:
pop ebp; ret $0008;
Это стековый фрейм, он добавляется автоматом при использовании процедурой самого стека (под локальные переменные, под входные параметры).
2.
ret $0008 чистится стек из-за соглашения FASTCALL (в частности нивелируется передача второго параметра через стек)
3.
push ebx; и в конце: mov eax,ebx; pop ebx; EBX обьектный регистр, который нужно сохранять, видя что ты его используешь, компилер тебя подстраховывает
|