2014-04-17 18 views

Trả lời

13

Các mã được tạo khi tôi biên dịch này, theo thiết lập debug, cũng giống như vậy:

 
    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 

Khi mã bắt đầu thực hiện, eax thực sự là con trỏ tự. Nhưng trình biên dịch đã chọn lưu nó vào ebp-$0c và sau đó là số. Đó là thực sự lên đến trình biên dịch.

Mã trong cài đặt bản phát hành khá giống nhau. Trình biên dịch vẫn chọn zeroise eax. Tất nhiên, bạn không thể dựa vào trình biên dịch làm điều đó.

 
    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 

Hãy nhớ rằng tham số truyền xác định trạng thái thanh ghi và ngăn xếp khi hàm bắt đầu thực hiện. Điều gì xảy ra tiếp theo, cách chức năng giải mã các tham số là xuống trình biên dịch. Đó là không có nghĩa vụ để lại ảnh hưởng đến các thanh ghi và ngăn xếp được sử dụng để đi qua tham số.

Nếu bạn chèn asm vào giữa một hàm, bạn không thể mong đợi các thanh ghi dễ bay hơi như eax để có các giá trị cụ thể. Họ sẽ giữ bất cứ điều gì trình biên dịch đã xảy ra để đưa vào chúng gần đây nhất.

Nếu bạn muốn kiểm tra sổ đăng ký vào lúc bắt đầu thực hiện hàm, bạn cần sử dụng hàm asm thuần túy để đảm bảo tránh trình biên dịch sửa đổi thanh ghi đã được sử dụng để chuyển tham số:

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

Trình biên dịch sẽ lựa chọn rất nhiều phụ thuộc vào mã trong phần còn lại của hàm. Đối với mã của bạn, sự phức tạp của việc lắp ráp chuỗi để chuyển đến ShowMessage gây ra một phần mở đầu khá lớn. Hãy xem xét mã này để thay thế:

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; 

Trong trường hợp này mã đơn giản đủ để trình biên dịch rời khỏi eax một mình. Mã xây dựng bản phát hành được tối ưu hóa cho Sum là:

 
    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 

Và khi bạn chạy mã, chú thích của biểu mẫu được thay đổi thành giá trị mong đợi.


Hoàn toàn trung thực, lắp ráp nội tuyến, được đặt dưới dạng khối asm bên trong hàm Pascal, không hữu ích lắm. Điều về viết assembly là bạn cần hiểu đầy đủ trạng thái của thanh ghi và ngăn xếp. được xác định rõ ở đầu và cuối của một hàm, được xác định bởi ABI.

Nhưng ở giữa một hàm, trạng thái đó phụ thuộc hoàn toàn vào các quyết định của trình biên dịch. Việc tiêm các khối asm trong đó yêu cầu bạn phải biết các quyết định mà trình biên dịch đã thực hiện. Nó cũng có nghĩa là trình biên dịch không thể hiểu được các quyết định mà bạn đã thực hiện. Điều này thường không thực tế. Thật vậy cho trình biên dịch x64 Embarcadero cấm các khối asm nội tuyến như vậy. Cá nhân tôi chưa bao giờ sử dụng khối nội tuyến asm trong mã của tôi. Nếu bao giờ tôi viết asm tôi luôn luôn viết chức năng asm tinh khiết.

+0

Cảm ơn bạn đã bình luận am hiểu của bạn! – SOUser

0

Chỉ cần sử dụng Push/Pop để có được con trỏ của TỰ, và sau đó sử dụng các thuộc tính tự do, như thế này:

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... 
     ...... 
Các vấn đề liên quan