How to use Assembly Language with Delphi 32-bit and 64-bit
The general procedure for the use of routines written in Assembly Language and compiled to an “.obj” object file is to insert the compiler 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 will be good if the “.obj” file is in the correct format.
32-bit Delphi accepts only OMF formatted object files (as opposed to COFF). There is 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.
32-bit ASM code:
; JWASM
; \jwasm -omf asmFile32.asm
;To compile with TASM32
; \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:
;\ml64.exe asmFile64.asm
;Compile with JWASM
;\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:
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.