Jose's Blog

How to use Assembly Language with Delphi 64-bit (and 32-bit as well)


Yesterday, I wrote about using 64-bit (and 32-bit) BASM with Delphi XE2. After publishing it, a few developers asked me if I could elaborate more on the theme of Assembly Language and Delphi.
The general procedure to use routines written in Assembly Language and compiled to an “.obj” object file is to insert the compiler directive directive $L in the Delphi source code. For example, if you have a myCompiledAsmFile.obj file to link in with Delphi, just insert the directive $L myCompiledAsmFile.obj and you are good, if the “.obj” file is in the correct format.
32-bit Delphi accepts only OMF formatted object files (as opposed to COFF). There are a small number of Assemblers that can produce OMF object files compatible with Delphi (i.e, not all Assemblers that can produce OMF object files, produce OMF object files compatible with Delphi). Tasm32.exe, distributed with Delphi is one of those Assemblers. The other one I am aware of is JWasm.
For 64-bit Delphi, Embarcadero did not came up with any OMF format for 64-bit, and that was a great decision. 64-bit COFF Object files link just fine with 64-bit Delphi.
There is no 64-bit Assembler from Embarcadero, but we can use the 64-bit Microsoft Macro Assembler (MASM). We can also use JWasm mentioned above with different command line switches.
I include another demo program with this article, that you can download  from the link at the bottom of this page. If you read the article I posted yesterday you will notice many similarities.

32-bit ASM code:

; The default is to compile with
; JWASM
; <path>\jwasm -omf asmFile32.asm

;To compile with TASM32
; <path>\tasm32.exe asmFile32.asm
; Remove the semi-colon below:
; _TASM_ EQU 1

IFDEF _TASM_
extern MessageBoxW : NEAR
public showMsgGetArray32
public addValues32
ELSE
externdef MessageBoxW : NEAR
ENDIF

.386
.model flat

.data
myReturnString dw “I”,” “,”a”,”m”,” “,”b”,”a”,”c”,”k”,” “,”i”,”n”
dw ” “,”D”,”e”,”l”,”p”,”h”,”i”,”!”,0
endOfString dw 0

.code

addValues32 proc
add eax, edx
ret
addValues32 endp

showMsgGetArray32 proc
push edi
push esi
push dword ptr 0
push eax
push edx
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 MessageBoxW
mov esi, offset myReturnString
mov ecx, offset endOfString – offset myReturnString
shr ecx, 1
rep movsw
pop esi
pop edi
ret
showMsgGetArray32 endp

end

64-bit ASM code:

;To compile with MASM
;<path>\ml64.exe asmFile64.asm
;Compile with JWASM
;<path>\jwasm -win64 asmFile64.asm

externdef MessageBoxW : NEAR

.data
myReturnString dw “I”,” “,”a”,”m”,” “,”b”,”a”,”c”,”k”,” “,”i”,”n”
dw ” “,”D”,”e”,”l”,”p”,”h”,”i”,”!”,0
endOfString dw 0

.code

;General rule: Integer and pointer arguments are passed
;left to right in RCX, RDX, R8 and R9
;Simple results are returned in RAX

addValues64 proc
mov rax,rcx
add rax, rdx
ret
addValues64 endp

;The general rule is 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 here,
;RCX contains a pointer to the return space when the callee
;is called and all Registers usage are pushed one to the right

showMsgGetArray64 proc
push rsi
push rdi
push rbx
push rbp
sub rsp, 28h

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

mov rdi, rbx
mov rsi, offset myReturnString
mov ecx, offset endOfString – offset myReturnString
shr ecx, 1
rep movsw
add rsp, 28h
pop rbp
pop rbx
pop rdi
pop rsi
ret
showMsgGetArray64 endp

end

The Delphi source file is as follows:

unit asmTest;

interface

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

type
TForm2 = class(TForm)
GroupBox1: TGroupBox;
btAdd: TButton;
lblInt1: TLabel;
edInt1: TEdit;
edInt2: TEdit;
lblInt2: TLabel;
GroupBox2: TGroupBox;
lblCaption: TLabel;
Label2: TLabel;
btGo: TButton;
edCaption: TEdit;
edMessage: TEdit;
procedure btAddClick(Sender: TObject);
procedure btGoClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form2: TForm2;

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

{$IFDEF CPUX86}
{$L asmFile32.obj}
function addValues32(val1: integer; val2: integer): integer; external;
function showMsgGetArray32(myCaption: string; myText : string): bigarray; external;
{$ELSE}
{$IFDEF CPUX64}
{$L asmFile64.obj}
function addValues64(val1: integer; val2: integer): integer; external;
function showMsgGetArray64(myCaption: string; myText : string): bigarray; external;
{$ENDIF}
{$ENDIF}

implementation

{$R *.dfm}

procedure TForm2.btAddClick(Sender: TObject);
var
retValue : dword;
value1, value2 : dword;
begin
value1 := strToInt(edInt1.Text);
value2 := strToInt(edInt2.Text);
{$IFDEF CPUX86}
retValue := addValues32(value1, value2);
{$ELSE}
{$IFDEF CPUX64}
retValue := addValues64(value1, value2);
{$ENDIF}
{$ENDIF}
showMessage(‘The result is: ‘+inttostr(retValue));
end;

procedure TForm2.btGoClick(Sender: TObject);
var
retValue : bigArray;
begin
{$IFDEF CPUX86}
retValue := showMsgGetArray32(edCaption.Text, edMessage.Text);
{$ELSE}
{$IFDEF CPUX64}
retValue := showMsgGetArray64(edCaption.Text, edMessage.Text);
{$ENDIF}
{$ENDIF}
showMessage(retValue);
end;

end.

Download fileDownload the 32-bit/64-bit demo program source code now

All articles are written by Jose Pascoa and if you quote them you must not misrepresent were you took the information from!

Advertisement

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: