|
Finally, 64-Bit Delphi is here and with 64-Bit BASM! - written
by Jose Pascoa
(See
also the article "How to use Assembly Language with Delphi
64-bit (and 32-bit as well)"
Delphi was once a great and popular Pascal programming
language, but after years of bad management and false promises,
developers just abandoned it in favor of more fashionable
languages like C#.
One of the main criticisms was the lack of support for 64-bit
computing. But now, things have really changed under the
management of Embarcadero (which took over the company from CodeGear, which had taken it from Inprise, which in turn had
done the same from Borland).
I have been testing the new Delphi XE2 for a couple of days.
One thing I have no doubts right now is that Embarcadero was
able to gather a fantastic team of coders and come up with a
great product. There are really amazing features, and I am
pretty sure most of them are not being properly advertised
to developers.
Well, marketing and documentation are important and coders
were never great guys for that, so I hope Embarcadero can
assemble other teams of guys/girls to produce decent
documentation and to show the developers how great the product
is. This is probably more important than doing marketing near
the board of directors of companies.
During my tour with Delphi XE2, I was particularly surprised
with the 64-bit BASM facility. Well, I started as an Assembly
Language nerd in my old days and still can not resist to
experiment a bit when the opportunity arises.
With BASM we can mix Assembly Language with Delphi
code in the same source file (even inline in the same function
for 32-bit BASM).
Microsoft Visual Studio allows as well the mix of C/C++ with Assembly
Language but only in 32-bit code. So, Embarcadero won here
because it supports both 32-bit and 64-bit. Really amazing.
I know there are limitations in BASM, there are also many
limitations with the Microsoft inline Assembly Language and, of
course, for very complex Assembly Language parts is still better
to compile in a separate file and link all together.
I have prepared a small demonstration program were BASM is
called from Delphi. It will compile both in 32-bit and 64-bit.
The program is simple although more advanced than most
samples you can find in the internet. The program shows as well
how you can return a large amount of information from the call,
in this case an array.
One thing you will notice immediately by looking at the code
below is that 32-bit Assembly
Language is totally different from 64-bit Assembly language. So,
if you want to port 32-bit routines to 64-bit, forget about that
and start new ones from scratch.
Here is the source code (you can download the demo program from
the link at the bottom of this page).
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.
Download the Delphi BASM
32-bit/64-bit demo program source code Now!
More articles here |