.Trust our experience in High-Power Network Tools and Communication Tools.

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:

; The default is to compile with
; 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:

;To compile with MASM
;\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:

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 the 32-bit/64-bit demo program source code Now!