2014-04-17 15 views

Odpowiedz

13

Kod generowany kiedy mogę skompilować ten, pod ustawień debugowania, jest tak:

 
    begin 
005A9414 55    push ebp 
005A9415 8BEC    mov ebp,esp 
005A9417 83C4E4   add esp,-$1c 
005A941A 33C9    xor ecx,ecx 
005A941C 894DEC   mov [ebp-$14],ecx 
005A941F 894DE8   mov [ebp-$18],ecx 
005A9422 894DE4   mov [ebp-$1c],ecx 
005A9425 8955F0   mov [ebp-$10],edx 
005A9428 8945F4   mov [ebp-$0c],eax 
005A942B 33C0    xor eax,eax 
005A942D 55    push ebp 
005A942E 6890945A00  push $005a9490 
005A9433 64FF30   push dword ptr fs:[eax] 
005A9436 648920   mov fs:[eax],esp 
    mov X, eax 
005A9439 8945FC   mov [ebp-$04],eax 
    mov Y, edx 
005A943C 8955F8   mov [ebp-$08],edx 

Kiedy rozpoczyna się wykonywanie kodu, eax jest rzeczywiście wskaźnik siebie. Ale kompilator wybrał zapisanie go na ebp-$0c, a następnie zerowanie eax. To naprawdę zależy od kompilatora.

Kod pod ustawieniami wydania jest dość podobny. Kompilator nadal wybiera zeroise eax. Oczywiście nie można polegać na tym kompilatorze.

 
    begin 
005A82A4 55    push ebp 
005A82A5 8BEC    mov ebp,esp 
005A82A7 33C9    xor ecx,ecx 
005A82A9 51    push ecx 
005A82AA 51    push ecx 
005A82AB 51    push ecx 
005A82AC 51    push ecx 
005A82AD 51    push ecx 
005A82AE 33C0    xor eax,eax 
005A82B0 55    push ebp 
005A82B1 6813835A00  push $005a8313 
005A82B6 64FF30   push dword ptr fs:[eax] 
005A82B9 648920   mov fs:[eax],esp 
    mov X, eax 
005A82BC 8945FC   mov [ebp-$04],eax 
    mov Y, edx 
005A82BF 8955F8   mov [ebp-$08],edx 

Należy pamiętać, że przekazywanie parametrów określa stan rejestrów i stosu, gdy funkcja rozpoczyna wykonywanie. Co dzieje się dalej, jak funkcja dekoduje parametry, sprowadza się do kompilatora. Nie ma obowiązku pozostawiać nietkniętych rejestrów i stosów, które były używane do przekazywania parametrów.

Jeśli wstrzykniesz moduł ASM w środek funkcji, nie możesz oczekiwać, że rejestry ulotne, takie jak eax, będą miały określone wartości. Będą trzymać wszystko, co ostatnio kompilator włożył w nie.

Jeśli chcesz sprawdzić rejestry na samym początku wykonywania funkcji, należy użyć czystego funkcji asm mieć pewność, aby uniknąć konieczności kompilator modyfikacji rejestrów, które zostały użyte do parametru Podania:

var 
    X, Y: Pointer; 
asm 
    mov X, eax 
    mov Y, edx 
    // .... do something with X and Y 
end; 

kompilator dokona jego wyboru bardzo dużo zależy od kodu w pozostałej części funkcji. Dla twojego kodu, złożoność zestawienia ciągu znaków do przekazania do ShowMessage powoduje dość dużą preambułę. Rozważmy ten kod zamiast:

type 
    TForm1 = class(TForm) 
    procedure FormCreate(Sender: TObject); 
    private 
    i: Integer; 
    function Sum(j: Integer): Integer; 
    end; 
.... 
procedure TForm1.FormCreate(Sender: TObject); 
begin 
    i := 624; 
    Caption := IntToStr(Sum(42)); 
end; 

function TForm1.Sum(j: Integer): Integer; 
var 
    X: Pointer; 
begin 
    asm 
    mov X, eax 
    end; 
    Result := TForm1(X).i + j; 
end; 

W tym przypadku kod jest dość proste do kompilator opuścić eax sam. Zoptymalizowany kod release build dla Sum jest:

 
    begin 
005A8298 55    push ebp 
005A8299 8BEC    mov ebp,esp 
005A829B 51    push ecx 
    mov X, eax 
005A829C 8945FC   mov [ebp-$04],eax 
    Result := TForm4(X).i + j; 
005A829F 8B45FC   mov eax,[ebp-$04] 
005A82A2 8B80A0030000  mov eax,[eax+$000003a0] 
005A82A8 03C2    add eax,edx 
    end; 
005A82AA 59    pop ecx 
005A82AB 5D    pop ebp 
005A82AC C3    ret 

A po uruchomieniu kodu, napis postaci zostaje zmieniona na wartość oczekiwaną.


Szczerze mówiąc, montaż inline, umieszczony jako blok asm wewnątrz funkcji Pascal, nie jest zbyt użyteczny. Rzecz w pisaniu asemblacji polega na tym, że musisz w pełni zrozumieć stan rejestrów i stosu. dobrze zdefiniowane na początku i na końcu funkcji zdefiniowanej przez ABI.

Ale w środku funkcji, stan ten zależy całkowicie od decyzji podejmowanych przez kompilator. Wstrzykiwanie bloków asm tam wymaga poznania decyzji podjętych przez kompilator. Oznacza to również, że kompilator nie może zrozumieć podjętych decyzji. Zwykle jest to niepraktyczne. Rzeczywiście dla kompilatora x64 Embarcadero zbanował takie wbudowane bloki asm. Osobiście nigdy nie użyłem wbudowanego bloku asm w moim kodzie. Jeśli kiedykolwiek piszę ASm, zawsze piszę czyste funkcje asm.

+0

Dziękuję za wiedzą komentarzach! – SOUser

0

Wystarczy użyć Push/Pop, aby uzyskać wskaźnik z siebie, a następnie użyj właściwości swobodnie, tak:

asm 
     push Self 
     pop edx     //Now, [edx] is the pointer to Self 

     mov ecx, [edx].FItems //ecx = FItems 
     mov eax, [edx].FCount //eax = FCount 
     dec eax    //test zero count! 
     js @Exit    //if count was 0 then exit as -1 
    @Loop:      //and so on... 
     ...... 
Powiązane problemy