Assembly and C ++ with Visual Studio 2015 and MASM x86 / 64

1

Good evening.

I use Visual Studio 2015 and I usually program in assembly language via __asm , but I still have questions about how to use MASM to create code in assembly for x64. I need help getting started.

How do I create assembly functions in an "asm" format file and call them in a "cpp" format file?

I ask you to show how to proceed in the following example, because from there I will research and learn other details. Consider a project with two files, "Header.h" and "Source.cpp", scheduled for x86.

The "Header.h" file has the formula(a,b,ret2) function, which you want to write to assembly in an assembly source file, for example "AsmSource.asm".

// Header.h
// formula(a,b,ret2) = a*a + b*b
// *ret2 = ( a*b )/( a*a/b + b*b/a )

int __cdecl formula( int a , int b , int *ret2 ){
    int sqa , sqb , dv ;
    __asm {
        MOV eax , dword ptr [a]
        MUL eax
        MOV dword ptr [sqa] , eax
        DIV dword ptr [b]
        MOV dword ptr [dv] , eax
        MOV eax , dword ptr [b]
        MUL eax
        MOV dword ptr [sqb] , eax
        DIV dword ptr [a]
        ADD dword ptr [dv] , eax
        MOV eax , dword ptr [a]
        MUL dword ptr [b]
        DIV dword ptr [dv]
        MOV edi , [ret2]
        MOV [edi] , eax
        MOV eax , dword ptr [sqa]
        ADD eax , dword ptr [sqb]
    }
    return ;
}

The "Source.cpp" file has the function main() that calls the function to be written to assembly.

// Source.cpp
# include <stdio.h>
# include <conio.h>
# include "Header.h"

int main(){
    int ret1 , ret2 ;
    ret1 = formula(12,16,&ret2) ;
    printf(" %i %i\n",ret1,ret2) ;
    _getch() ;
}

If we change the "Header.h" by an "AsmSource.asm" file, how should the "AsmSource.asm" and "Source.cpp" files be written to have the same result? I need to have an idea of what changes when I exit __asm to the assembly code file.

Thank you.

    
asked by anonymous 11.10.2016 / 07:31

1 answer

1

To create files with 64-bit assembly functions using Visual Studio 2015 (Community):

  • Right-click on your project, enter the menu Build Dependencies and then Build Customizations... . In the customizations screen, select the masm(.targets, .props) option:

  • Inyourproject,intheSourceFilesfolder,addanewitemUtilityoftypeTextFile(.txt)(textfile).Afteradding,renamethefilefrom.txtto.asm(example:funcao.txttofuncao.asm):

  • Right-clickthefuncao.asmfileandselectProperties.InthePropertieswindow,changetheItemTypefromTexttoMicrosoftMacroAssembler:

  • IntheCsourcecode,declaretheprototypeofthefunctionsasextern.IftheprogramisinC++,declareextern"C" . In your example, the main file looks like this:
#include "stdafx.h"

// delcaração da função em assembly
extern "C" long long int formula(long long int a, long long int b, long long int *r2);

// Função em C que implementa o mesmo cálculo (apenas para teste)
long long int formula2(long long int a, long long int b, long long int *r2)
{
    *r2 = (a*b) / (a*a / b + b*b / a);
    return a*a + b*b;
}

int main(int argc, char *argv[])
{
    long long int ret1;
    long long int ret2;

    printf("inicio\n");
    // Execução em C
    ret1 = formula2(12, 16, &ret2);
    printf("Resultado C: ret1=%lld, ret2=%lld\n", ret1, ret2);

    // Execução em Assembly
    ret1 = formula(12, 16, &ret2);
    printf("Resultado ASM: ret1=%lld, ret2=%lld\n", ret1, ret2);

    printf("fim.\n");
    getchar();
    return 0;
}

The type long long int defines the variables ret1 and ret2 as integers of 64 bits.

Depending on the logic you want to implement, the type may change and how the assembly code will be developed, too.

  • In the assembly source code, declare the functions as public . For your example, the file funcao.asm looks like this:
.code
public formula    
;  formula(a,b,ret2) = a*a + b*b
; *ret2 = ( a*b )/( a*a/b + b*b/a )

formula proc
    mov         r11, rdx                ; r11 <- b
    xor         rdx, rdx                ; rdx = 0
    mov         rax, rcx
    imul        rax, rax                        
    mov         r9, rax                 ; r9 <- a^2

    mov         rax, r11
    imul        rax, rax                        
    mov         r10, rax                ; r10 <- b^2

    idiv        rcx
    mov         r12, rax                ; r12 <- b^2/a

    xor         rdx, rdx
    mov         rax, r9
    idiv        r11
    add         rax, r12
    mov         r13, rax                ; r13 <- b^2/a + a^2/b

    xor         rdx, rdx
    mov         rax, rcx
    imul        rax, r11
    idiv        r13
    mov         qword ptr [r8], rax     ; resultado em rax

    mov         rax, r9
    add         rax, r10
    ret
formula endp

end 

In assembly 64 bits, parameter passing, declaration of local variables, return of functions, etc. are different from 32-bit assembly .

Here are some links that talk about it (for programming Windows , the main one is the first link):

The result, after running the program:

inicio
Resultado C: ret1=400, ret2=6
Resultado ASM: ret1=400, ret2=6
fim.

Edit

In response to the comment, to compile a mixed code (32 and 64bit), as defined in VS2015:

  • Right-click the funcao.asm file and enter Properties . In the properties window, change (if necessary) the items: Configuration to All Configurations and Platform to Win32 or Active(Win32) (if this platform is already selected)
  • Change the item Excluded From Build to Yes

Withthischange,thefuncao.asmfilewillonlybecompiledwhentheplatformissetto64-bit.

Inthesourcecode,usethe_WIN64macrotosettheprototypefunctionsasexternto64bits,orwithassemblyinline(forexample)to32bits.

Theimportantpoint(ifpossible)istokeeptheprototypefunctionsidenticalforboth32and64bits.

Afterthechanges,thesourcecodeinClookslikethis:

#include "stdafx.h" #ifdef _WIN64 // Código 64 bits // delcaração da função em assembly extern "C" long long int formula(long long int a, long long int b, long long int *r2); #else // Código 32 bits // Inline long long int _cdecl formula(long long int a, long long int b, long long int *ret2) { int sqa, sqb, dv; int ret_local; // Acrescentei esta variável __asm { mov eax, dword ptr[a] mul eax mov dword ptr[sqa], eax div dword ptr[b] mov dword ptr[dv], eax mov eax, dword ptr[b] mul eax mov dword ptr[sqb], eax div dword ptr[a] add dword ptr[dv], eax mov eax, dword ptr[a] mul dword ptr[b] div dword ptr[dv] mov [ret2], eax ; Aqui, o código estava incorreto mov eax, dword ptr[sqa] add eax, dword ptr[sqb] mov [ret_local], eax ; Armazena o retorno } return (long long int) ret_local; } #endif // _WIN64 // Função em C que implementa o mesmo cálculo (apenas para teste) long long int formula2(long long int a, long long int b, long long int *r2) { *r2 = (a*b) / (a*a / b + b*b / a); return a*a + b*b; } int main(int argc, char *argv[]) { long long int ret1; long long int ret2; printf("inicio\n"); // Execução em C ret1 = formula2(12, 16, &ret2); printf("Resultado C: ret1=%lld, ret2=%lld\n", ret1, ret2); // Execução em Assembly ret1 = formula(12, 16, &ret2); printf("Resultado ASM: ret1=%lld, ret2=%lld\n", ret1, ret2); printf("fim.\n"); getchar(); return 0; }

Note: I just made a small correction to the inline tag and added a local variable to return the result of the function.

The result of the execution is the same as that reported in the answer above.

Obs2: There are other, possibly better, ways of organizing the mixed code (32 and 64 bits) within Solution of VS2015. This answer is just a "starting point" to show how to compile the mixed code.

    
12.10.2016 / 05:32