如何在VC中汇编,调用Nasm编写的函数

    我们一般会使用汇编来编写程序中效率瓶颈部分, 而这又包括两种方法, 一是采用内联汇编, 这是十分简洁方便的一个方法, 我们可以不必考虑如寄存器保护, 函数参数退栈之类繁琐的问题, 但其缺点是可移植性不强, 影响代码风格等. 而另一个就是本文要讲的, VC链接纯汇编函数.好,言归正传, 我们开始

一,工具
1) VC++2003, 本文所讲的操作都是基于此版本.
2) NASM 0.98.39.
   NASM(Netwide Assembly)是一个为可移植性与模块化而设计的一个 80x86 的开源汇编器, 其优点有:
  1支持相当多的目标文件格式.包括 Linux 和'NetBSD/FreeBSD','a.out','ELF','COFF',微软 16位的'OBJ'和'Win32'。它还可以输出纯二进制文件。
  2 支持最新的指令集. 包括'Pentium','P6','MMX','3DNow!','SSE' and 'SSE2'指令集.
  大家可以在网上搜到NASM的下载及学习资料, 下面是部分链接:
  下载地址:http://www.kernel.org/pub/software/devel/nasm/
  参考手册:http://blog.csdn.net/yanhuang82/articles/601973.aspx
  学习文章:http://blog.csdn.net/yanhuang82/articles/601961.aspx
 

二,步骤

1 新建一个名位CallNASM的VC工程, 类型为Win32控制台, 接受默认设置.

2 在该工程下新建一个名为Sort.asm的汇编源文件

3 在该文件中输入代码:

[bits 32]                      ;使用32位模式的处理器
[section .text]                ;text段, 代码段,只读并可执行

global _bubble_sort@8;         ;申明函数为global的, 这样可以被外部模块调用

_bubble_sort@8:                ;定义函数开始,C++中原型为int bubble_sort(int*                                    ; pArray, int dArraysize)
   push ebp                    ;ebp入栈
   mov ebp,esp                 ;esp->ebp
   mov esi,[ebp+8];            ;取出第一个参数,数组地址
   sub esi, 4                  ;数组地址前移一个,为排序做准备
   mov ecx,[ebp+12];           ;取第二个参数, 数组长度
.outloop:                      ;外循环
   mov edx,ecx;
   .inloop:                    ;内循环
    mov eax, [esi+ecx*4];
    mov ebx, [esi+edx*4];
    cmp eax, ebx;
    jnb .noxchg;           
    mov [esi+ecx*4], ebx;
    mov [esi+edx*4], eax;
    .noxchg:
    dec edx;
  jnz .inloop;               
loop .outloop;
  mov esp, ebp;                ;恢复esp, ebp
  pop ebp
  ret 8                        ;参数退栈


  说明:
  1) 进入函数
  esp为堆栈指针, 进入函数后栈的存储情况及esp指向可能如下:
  |参数2| <-------------------- 栈底
  |参数1|
 
  |函数返回地址|
 
  |EBP|
 
  |局部变量...|
<---------------- esp
  也就是说函数参数, 返回地址, ebp寄存器, 局部变量等都会相继压入栈中, 而esp始终指向栈顶, 这里要注意的是, 在汇编中,栈底地址是大于栈顶地址的, 栈是向下生长的.所以, 当我们取参数时栈的情况为:
  |参数2|                    esp+12
  |参数1|                    esp+8
  |函数返回地址|             esp+4
  |EBP|  <----------------  esp
  此时ebp已入栈, 参数1的地址为:esp+8, 参数2的地址为:esp+12, 但一般我们不直接用esp访问参数, 因为esp随时都会改变(如声明一个局部变量会使esp往下指), 所以我们用ebp保存了这个初始状态下栈的指针, 以便统一访问参数,于是, 取参数的代码为:
  mov esi,[ebp+8];            ;取出第一个参数,数组地址
  mov ecx,[ebp+12];            ;取第二个参数, 数组长度

  2) 算法部分
  标准的最简单的冒泡排序法, 二层循环.
  3) 退出函数
   mov esp, ebp;                  ;恢复esp
   pop ebp
   ret 8                    ;参数个数
   这里第一句恢复esp后, 函数栈中还有4个元素, 然后pop ebp后还剩下3个, ret 8修正两个参数使用的堆栈(如果还有第三个参数, 且大小为4, 则ret 12). 从代码上看esp并没有完全恢复(还剩下 1 个), 但在ret 8之前堆栈应该自动pop了函数的返回地址(不然怎么return:)),函数返回地址的入栈与出栈都是自动完成的.
 
 

4 为Sort.asm添加自定义生成属性.
   1) 右键->属性,  弹出sort.asm属性页
   2) 配置属性->自定义生成步骤->常规
   3) 命令行:nasmw $(InputName).asm  -f win32 -o $(IntDir)/$(InputName).obj
      输出: $(IntDir)/$(InputName).obj

    说明:
    1)这里将文件$(InputName).asm编译为win32格式obj文件
    2)要使这个命令能够成功运行, 应该将nasmw.exe (w表示windows)拷贝到工程目录下, 或者更通用点, 直接拷贝到windows/system32下面

5 在CallNasm.cpp中调用此汇编函数, 代码如下:

#include "stdafx.h"
#include <iostream>
using namespace std;

//extern function from NASM
extern "C" int _stdcall bubble_sort(int* pArray, int dArraysize);

//helper function to print sort result
void PrintArray(string strBeforeorAfter);

//array to be sorted
int Array[10] = {2, 1, 4, 6, 5, 3, 7, 9, 10, 8};

int _tmain(int argc, _TCHAR* argv[])
{
    PrintArray("Before Sort: ");
    bubble_sort(Array, 10);
    PrintArray("After  Sort: ");
    return 0;
}

void PrintArray(string strBeforeorAfter)
{
    cout<<strBeforeorAfter.c_str();
    for(int i = 0; i < 10; i++)
    {
        cout<<Array[i]<<" ";
    }
    cout<<endl;
}

说明:
1) extern "C" int _stdcall bubble_sort(int* pArray, int dArraysize);
用extern引用外部模块的函数,并采用stdcall的调用方式
2) bubble_sort函数在汇编中的名字一般为加上"_"前缀和"@N"后缀, 这里N与参数大小有关
如bubble_sort(int* pArray, int dArraysize)为_bubble_sort@8,  而
bubble_sort(int* pArray)应为_bubble_sort@4.


三, 结论
   这样, 我们就能成功的在VC中调用NASM汇编函数了, 事实上, 在VC中调用MASM函数除了汇编语法稍有不同,应该也是类似的.

你可能感兴趣的:(function,汇编,String,assembly,vc++,iostream)