为什么要使用 NASM?
使用 inline asm 固然方便, 但是却不利于代码的移植. 加上 VC 对新指令集(3D Now! ,XMM 等)的支持速度不够, 使用起来很不方便, 所以我们往往采用外部汇编. 如果你以前熟悉 MASM 或是 TASM, 也不必更换, 否则云风推荐 NASM. NASM 及其文档在 http://www.web-sites.co.uk/nasm 可以下载的到. 使用前最好通读一遍文档. 本文只会强调一些重点部分, 而补充一点遗漏, 而不会全盘复述. NASM 支持各种最新的指令集, 有相当强大的宏语言支持. 你将得到比 VC 的 inline asm 更为灵活的使用空间.
选一个好的编程环境
将 VC 当作 NASM 的编辑环境是完全可行的, 只需要在 Tools->Customize->Tools 下加入 NASM.exe 作为外部工具. 这里不详细介绍. 云风推荐 Editplus 这款小巧实用的编辑器. 你可以指定各种关键字的颜色, 云风自己制作的语法文件在这里, 写的还不完备, 很多 nasm 的宏语句尚未加入, 另外还加了一些自己制作的宏. 所以请各位读者酌情修改. 加入 Editplus 的方法是在 Tools-> Preferences->Files->Syntax 里增加一个语法文件. Editplus 支持 output 窗口信息捕获, 在 Tools->Preferences->Tools->User Tools 里增加 NASM.exe, 并选 capture output 就可以了. 当然如果你想制作单独的 win32/pe 文件, 而不是编译成 obj 供 VC link, 那么还需要一个 link 程序. 最好再配合 makefile 使用.
如何编译成可供 VC 连接的 obj
NASM 支持多种输出格式, 虽然 VC 号称是使用的 COFF 格式, 但实际上是有区别的. NASM 除了输出 COFF 格式外, 另外也支持 VC 的目标文件格式, 叫做 win32. 我在 Editplus 的 user tools 里设置的是
-i I:/nasm/include/ -f win32 -o $(FileNameNoExt).obj $(FileName)注: 这里的 -i 后是头文件的路径 将编译出来的 obj 加入 VC 的 project 里就可以了. 一般来说, 目标文件有三个段, 分别是 text/data/bss 段.
这里, 用 global 声明了一个可被 C 调用的函数 func (C 函数都有一个下划线前缀) _func: 是这个函数的入口. 在对应的 C 代码里想使用 func 这个函数, 还需要用 extern 声明 func 是外部函数. 如果在 C++ 里使用则还需要将函数说明成 C 调用方式, 方法是用 extern "C" { } 说明
[bits 32]
[section .text]
global _func
_func:
; 这里写func 函数的代码
[section .data]
[section .bss]
参数的传入和处理
函 数的参数处理和其调用方式有关, C 的缺省调用方式是 _cdel 调用方式, C++ 的非静态成员函数采用的是 _thiscall, 关于各种调用方式的处理堆栈的方法, 在 MSDN 里可以查的到. 写 inline asm 可能很少涉及这些, 但是这里却必须搞清楚.
用纯汇编写 win32 程序等
写 WIN32 下的 win32/pe EXE, 我建议使用 Borland 的 obj 格式, 这是因为导入 DLL 中的 API 比较方便. 但是需要注意一些要点:
[section .text class=code use32]
[section .data class=data use32]
[section .bss class=bss use32]
%define __SECT__
你的代码将从 WinMain 这里开始运行.
[section .text]
..start
WinMain:
push dword 0
call GetModuleHandle ; 获得 hInstance
push dword 0 ; 退出码
call ExitProcess
import GetModuleHandle kernel32.dll GetModuleHandleA
extern GetModuleHandle
另外 ExitProcess 也是这样:
import ExitProcess kernel32.dll
extern ExitProcess
善于使用 NASM 强大的宏指令
这本是一个应该很丰富的 Section, 但是时间有限, 只举一个简单的例子, 定义下面这样一个宏:
然后, 比如你需要调用 MessageBox, 就可以这样写
%imacro callapi 1-*
%define %%api %1
%rotate -1
%rep %0-1
%rotate -1
push dword %2
%endrep
call %%api
%endmacro