65.9K
CodeProject 正在变化。 阅读更多。
Home

终于,64位Delphi和64位BASM来了!

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.62/5 (10投票s)

2011年10月3日

CPOL

2分钟阅读

viewsIcon

38028

downloadIcon

451

在32位和64位Delphi程序中使用Borland汇编语言。

引言

Delphi曾经是一种很棒且流行的Pascal编程语言,但多年来由于糟糕的管理和虚假的承诺,开发者们转而选择了更流行的语言,如C#。

主要的批评之一是缺乏对64位计算的支持。但现在,在Embarcadero的管理下(该公司从CodeGear手中接管了公司,而CodeGear又从Inprise手中接管了公司,Inprise又从Borland手中接管了公司),情况真的发生了变化。

我一直在测试新的Delphi XE2几天了。我现在可以肯定的是,Embarcadero能够聚集一支出色的编码团队,并推出一款很棒的产品。 真的有很多令人惊叹的功能,而且我确信大多数功能都没有被开发者们正确地宣传。

嗯,营销和文档很重要,而且程序员们从来都不擅长这些,所以我希望Embarcadero能够组建其他团队来制作像样的文档,并向开发者们展示这款产品有多么出色。这可能比在公司董事会附近进行营销更重要。

在使用Delphi XE2的过程中,我特别惊讶于64位BASM的功能。嗯,我年轻的时候就开始学习汇编语言,并且仍然无法抗拒在有机会的时候进行一些实验。

使用BASM,我们可以在同一个源文件中混合汇编语言和Delphi代码(甚至在32位BASM的同一个函数中内联)。微软Visual Studio也允许将C/C++与汇编语言混合,但仅限于32位代码。因此,Embarcadero在这方面获胜了,因为它支持32位和64位。 真是太棒了。

我知道BASM存在局限性,微软内联汇编语言也存在许多局限性,当然,对于非常复杂的汇编语言部分,最好还是将其编译成单独的文件并链接在一起。

使用代码

我准备了一个小演示程序,其中BASM从Delphi调用。它将同时在32位和64位下编译。该程序很简单,但比你在互联网上找到的大多数示例更高级。该程序还演示了如何从调用中返回大量信息,在本例中是一个数组。

Basm1.jpg

你通过查看下面的代码会立即注意到的是,32位汇编语言与64位汇编语言完全不同。因此,如果你想将32位例程移植到64位,那就忘记吧,从头开始重新编写。

Basm2.jpg

这是源代码(你可以从提供的链接下载演示程序的完整源代码)

unit basmTestUnit;

interface

uses
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, 
    Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
    TBasmTest = class(TForm)
    GroupBox1: TGroupBox;
    lblCaption: TLabel;
    edCaption: TEdit;
    lblInput: TLabel;
    edInput: TEdit;
    btGo: TButton;

procedure btGoClick(Sender: TObject);
private

public
    { Public declarations }
end;

var
    BasmTest: TBasmTest;

implementation

{$R *.dfm}

var
    myReturnString : string = 'I am back in Delphi!';
    charCount : integer;

type
    bigarray = array[0..127] of char;


function assemblyTestFunction(myCaption: string; myText : string): bigarray;
{$IFDEF CPUX86}
asm
    // This is the 32-bit case with the default Register (Borland Fastcall) calling convention
    // where the Result is an extra var parameter.
    // Arguments are passed in EAX, EDX and ECX, left to right. So, the Result (i.e, the 
    // return value pointer) will come to the function in ECX.
    push edi
    push esi
    push dword ptr 0
    push dword ptr myCaption
    push dword ptr myText
    push dword ptr 0
    mov edi, ecx; // Save the return pointer to EDI because we need ECX as a counter and 
                  // EDI is the destination pointer in a rep movsw instruction
    call MessageBox
    mov esi, dword ptr myReturnString
    mov ecx, [charCount]
    rep movsw
    pop esi
    pop edi
    ret
end;
{$ELSE}
{$IFDEF CPUX64}
    // This is the 64-bit case
    // General rule: Integer and pointer arguments are passed left to right in RCX, RDX, R8
    // and R9. HOWEVER, when there is a large return value, this is the case, RCX contains
    // a pointer to the return space when the callee is called and all Registers usage are
    // pushed one to the right
    //
asm
    push rsi
    push rdi
    push rbx
    push rbp // Yes, it is a kludge. No need to push rbp, it is just to align the stack.
             // The alternative is 'and rsp,-16'
    sub rsp, 28h

    xor r9, r9
    mov rbx, rcx
    mov rax, rdx
    mov rdx, r8
    mov r8, rax
    xor rcx, rcx
    call MessageBox

    mov rdi, rbx
    mov rsi, qword ptr myReturnString
    mov ecx, [charCount]
    rep movsw
    add rsp, 28h
    pop rbp
    pop rbx
    pop rdi
    pop rsi
    ret
end;
{$ENDIF CPUX64}
{$ENDIF}

procedure TBasmTest.btGoClick(Sender: TObject);
var
    retValue : bigArray;
begin
    fillchar(retValue, sizeof(bigArray),0);
    charCount := length(myReturnString);
    retValue := assemblyTestFunction(edCaption.Text, edInput.Text);
    showMessage(retValue);
end;

end.
© . All rights reserved.