用 NASM 编写代码


本文主要为有汇编基础, 而习惯了用 VC 的 inline ASM 写代码的朋友们而作.

为什么要使用 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 段. 一个简单的程序框架是这样的:

[bits 32]

[section .text]
global _func
_func:
; 这里写func 函数的代码

[section .data]

[section .bss]
这里, 用 global 声明了一个可被 C 调用的函数 func (C 函数都有一个下划线前缀) _func: 是这个函数的入口. 在对应的 C 代码里想使用 func 这个函数, 还需要用 extern 声明 func 是外部函数. 如果在 C++ 里使用则还需要将函数说明成 C 调用方式, 方法是用 extern "C" { } 说明

参数的传入和处理

函数的参数处理和其调用方式有关, C 的缺省调用方式是 _cdel 调用方式, C++ 的非静态成员函数采用的是 _thiscall, 关于各种调用方式的处理堆栈的方法, 在 MSDN 里可以查的到. 写 inline asm 可能很少涉及这些, 但是这里却必须搞清楚.

用纯汇编写 win32 程序等

写 WIN32 下的 win32/pe EXE, 我建议使用 Borland 的 obj 格式, 这是因为导入 DLL 中的 API 比较方便. 但是需要注意一些要点:

善于使用 NASM 强大的宏指令

这本是一个应该很丰富的 Section, 但是时间有限, 只举一个简单的例子, 定义下面这样一个宏:


%imacro callapi 1-*
%define %%api %1
	%rotate -1

	%rep %0-1 
	%rotate -1 
		push dword %2 
	%endrep 

	call %%api
%endmacro
然后, 比如你需要调用 MessageBox, 就可以这样写
callapi MessageBox,[hWnd],lpText,lpCap,0
另外我自己还做了许多诸如处理函数参数, 保护堆栈等等方便使用的宏, 这里就不一一列出来了. 希望我引个头, 能让更多的朋友喜欢上 NASM