NASM网际编译器手册(三)
第二章 运行NASM
2.1NASM命令行参数
为了汇编一个文件,你必须用一个命令行来实现:
nasm -f [-o ]
例子:
nasm -f elf myfile.asm
将文件myfile.asm汇编成一个elf目标文件myfile.o.
nasm -f bin myfile.asm -o myfile.com
将文件myfile.asm汇编成一个二进制文件myfile.com.
为了生成一个列表文件,用参数-l来给出一个列表文件名,例如:
nasm -f coff myfile.asm -l myfile.lst
为了得到更多关于NASM指令用法请用命令:
nasm -h
这也可以列出有效的输出文件格式,及它们是什么.如果你用Linux,但不确信你的系统是a.out还是ELF,
用命令file nasm
(在你安装NASM一进制文件的哪个目录),如果它显示如下信息:
nasm:ELF 32-bit LSB executable i386 (386 and up) Version 1
哪么你的系统为ELF,当你想生成Linux目标文件时应该用-f elf。
如果它显示:nasm:Linux/i386 demand-paged executable(QMAGIC)
或相似的一些信息,你的系统应该是a.out的.你应该用use -f aout来代替(Linux a.out系统被认为淘汰了,
现在很小见)
象一些Unix编译器,NASM是默认的除非它出错;你瘵不能看到它输出任何信息,除非它显示错误信息。
2.1.1 -o参数 指定输出文件名
NASM正常情况下将为你指定输出文件名;它对目标文件格式是独立的。对于微软的目标格式文件(obj和
win32),它将从你的源文件移走asm扩展名(或者你用-NASM来忽略)并加上obj扩展名.对于Unix目标文件格式
(aout.coff,elf和as86),它将为rdf加上.o,它将用.rdf,而对于二进制文件它只是简单的移去扩展名.
所以myfile.asm文件将生成名为myfile的输出文件.如果输出文件存在,NASM将覆盖它,除非它有相同名字
的输入文件, 这种情况它将组出一个警告并用nasm.out来代替输出文件名。
这种替换行为是不允许的,NASM提供-o命令行参数来允许你指定你想要的输出文件名。
你可以在-o后边加空格或不加空格,然后在加文件名,如:
nasm -f bin program.asm -o program.com
nasm -f bin driver.asm -odriver.sys
2.1.2 -f参数:指定输出文件格式
如果你不用-f参数指定文件格式,NASM将为你指定一种格式。在NASM的发布版本中,默认的为bin;
如果你编译你自己的NASM拷贝,你可以在编译时重新定义OF_DEFAULT并且选择你想默认的文件名。
象 -o参数一样,在-f 和输出文件格式之间的空格是可选的。所以-f elf 和-felf都是有效。
一个完整的输出文件格式列表可能过命令nasm -h来得到。
2.1.3 -l参数:生成列表文件
如果你用NASM的-l参数,后面加文件名(通常中间加),NASM将生成一个源码列表文件。生成的代码和
地址被列在左边,还有真正的源码,并带有多行宏的扩展。(除了指定在要求在源码列表不扩展:见
第4.2.9节)在右边。例如nasm -f elf myfile.asm -l myfile.lst
2.1.4 -E参数:将错误信息输出到文件中
在MS-DOS下将程序的错误信息输入到一个文件中是比较困难(虽然这有方法),因此NASM通常将警告信息
和错误信息显示在屏幕上,如果你想捕获这些信息并存入到编辑器中将是比较困难的事.
NASM因此提供了-E参数然后加上文件名将会使错误输出到文件中而不是标准错误设备上。因此你可以通过
命令typing nasm -E myfile.err -f obj myfile.asm 重新将错误信息输出到文件中。
2.1.5 -s参数:将错误输出到标准输出上
-s参数将错误信息重新定位到标准输出面不是标准错误上,因此它能在MS-DOS下运行。为了汇编文件
myfile.asm并且将它通过管道输出到其它程序中,你可以通命令:
nasm -s -f obj myfile.asm |more
关于-E参数的详细信息请看第2.1.4节
2.1.6 -i参数:头文件设置路径
当NASM在源文件中查看%include 定向符号时(详细信息见第4.5节),它将搜索在命令行中指定的目录
而不只是当前目录中文件。你也可以从一个宏库文件中包含文件,例如,用命令:
nasm -ic:\\macrolib\\ -f obj myfile.asm
(通常在-i和路径名中的空格是可选的)
NASM ,在源码级兼容方面比较感兴趣,不用理解不同操作系统间文件名的转换。你提供的字符串做为-i参数
将写到头文件的名字。因此在上面的例子中加反斜杠是需要的。在Unix下,在前面加上一个斜杠也是需要的。
(你可以利用这点,如果你真想做的话,那么参数-ifoo将引起%include "bar.i"来搜索文件foobar.i...)
如果你想要定义一个标准头文件搜索路径,在Unix系统上象/usr/include,你应该在NASM的环境变量中用
一个或多个-i定向符。(见第2.1.13节)
为了与许多的C编译器的Makefile相兼容,这个参数也可以指定为-I。
2.1.7 -p参数:预包含头文件
NASM允许你通过-p参数指定文件预包含到你的源文件中,所以命令:
nasm myfile.asm -p myinc.inc
和运行nasm myfile.asm 然后在文件的开始置换%include "myinc.in"等价。
和-I,-D,-U参数一样,这个操作也可以写成-P。
2.1.8 -d 参数:预定义宏
-p参数给出了一个在源文件开始处可选择%include 定向符的方法,-d参数给出了可选择%define定向符的
方法。你可以将代码 nasm myfile.asm -dFOO=100写成%define FOO 100D 在文件开始时,你可以关掉宏
的值如:-dFOO 等价与%define FOO。这个定向符的表格对汇编时的参数选择有用, 如%ifdef
例如-dDEBUG。
为了和许多C编译器的Makefile文件兼容,这个参数可以写成-D.
2.1.9 -u参数:取消一个宏的定义
-u参数在已经预定义一个宏的情况下取消一个宏的定义。并且自动取消前面命令行中的-p或-d参数。
例如:命令行参数:nasm myfile.asm -dFOO=100 -uFOO
的作用是在程序中取消以前定义的FOO宏。这对于在Makefile中重载参数比较有用。
为了许多C编译器的Makefile兼容,这个参数可以指定为-U。
2.1.10 -e参数:只进行预处理
NASM 允许在它运行时进行预处理到某一点。用-e参数(不需参数)将会使NASM在它的输入文件中进行预处理,
扩展所有宏参数,移去注释和预处理符,并且在标准输出上显示结果文件(也可以用-o参数将结果存到
文件中)。
这个参数不能应用于那些对独立符号值进行预处理的操作如:%assign tablesize($-tablestart)
将在只进行预处理模式中引起一个错误。
2.1.11 -a参数:根本不进行预处理
如果NASM被做为一个编译器的后端使用,为了节约时间和提高速度,它可能假设编译器已经进行了预处理,
而完全不需要预处理。-a参数不需要参数,NASM将肢用一个什么都不做的stub预处理来替换这个参数。
2.1.12 -w参数:允许/禁止汇编警告
NASM在汇编码方案的过程中将检测很多条件,来提醒用户,但是一个错误不致于生成一个文件是这些条件
做为链接错误来报错并且在消息的前面有‘warning’这个词,Warning 不会阻止NASM生成文件并返回给系统
一个成功的状态。
有些情况可以忽略:它们只是想提醒用户。因而NASM允许用户用-w命令行参数来允许可禁止指定类别的
警告。警告类别可以用名字来描述,例如:orphan-labels,你可以用-w+orphan-labels来允许这个级别的
警告用-w-orphan-labels来禁止它.
可用的警告信息有:
macro-params 包含所有错误号参数执行的多行宏警告信息.这个警告类别默认是允许的;关于如
何禁止它的例子见第4.2.1节.
orphan-labels 包含警告哪些没有指令但用冒号定义的标号的源码行.NASM默认情况下没有对这种
条件进行规定;如果你想进一步了解它见第3.1节的例子.
number-overflow 包含不适用于32位的数字信息(例如:打入太多个f而产生0x7ffffffff的错误)
这个警告默认情况是允许的.
2.1.13 NASM的环境变量
如果你定义了一个叫NASM的环境变量,程序将会把它解释为为一个外部命令行参数,在真正的命令行以前
进行处理.你可以用这个来为头文件定义标准的搜索路径.通过在NASM变量中用-i操作.
这个变量的值用空格分开,以致于-s -ic:\\nasmlib 将做为2个单独的操作.然而,这意味着变量
-dNAME="my name"不会做你想要做的事.因为它将在空格处分开并且NASM命令行处理器将会得到两个无意义
的字 -dNAME="my and name".
为了可以这样做,NASM提供了一个操作,如果你在变量的前面字符加一个不为负号的字符,NASM将会这个符号
做为分隔符,所以设置变量 !-s!-ic:\\nasmlib 等价于-s-ic:\\nasmlib,但!-dNAME="my name"将会工作.
2.2 MASM用户快速入门
如果你用MASM写过程序或用与MASM相兼容的TASM或a86,这节试着说明MASM与NASM在语法上的主要区别.
如果你没有用过MASM,哪么请跳过这一节.
2.2.1 NASM是大小写区分的
一个简单的区别是NASM是区分大小写的,你调用标号foo,Foo或FOO是不一样的。如果你编译过DOS或OS/2的
.OBJ文件,你可以执行UPERCASE 指令(文档见第6.2节)来保证向其它模块输出的所有符号都为大写。
但在这里NASM只在标号间对它们进行区分。
2.2.2 NASM 需要用方括号引用内存
NASM在语法上被设计为尽量简单。一个实用的设计目标为用户看一行NASM代码时就告诉它产生的操作码是
什么。你不能在MASM中这样做:如果你定义如下符号
foo equ 1
bar dw 2
那么下面两行代码
mov ax,foo
mov ax,bar
将产生完全不同的操作码,尽管他们有相同的语法。NASM避免这种由有相同语法而产生不同情况的内存
引用。这个规则不难实现:对于任何访问内存内容的地方用方括号,而任何访问变量地址的则不用。
所以一条指令:mov ax,foo将会总是参考编译时的内容,无论它是一个EQU还是一个变量的地址;
访问一个变量的内容时,你必须用mov ax,[bar]
这也意味着NASM不需要MASM的OFFSET关键字,所以MASM的mov ax,offset bar与NASM的mov ax,bar意义
相同。如果你试着在NASM得到大量的MASM代码,你将会用%idefine offset来生成物大量的OFFSET预处理
关键字做为无意义的工作。
这会在a86中产生一些迷惑,用一个冒号结束一个标号的定义和一个变量区分引起a86采用NASM形式的语义。
所以在a86中,mov ax,var有另外的意义决定于var是否被定义成变量;dw 0(一个标号)或者 var dw 0(
一个WORD尺寸的变量)。NASM比较起来是很简单的:任何符号都是一个标号。
NASM在简单性方面很出色,也不支持象MASM复杂的语法格式和它的拷贝,如:mov ax,[table+bx],同样的
mov ax,es:[di]是错的而mov ax,[es:di]是对的。
2.2.3 NASM不存储变量类型
NASM在设计时,就选择了不记忆你定义的变量类型。然而看到var dw 0 MASM将记下你定义了一个字长的变量,
然后在指令mov var,2前 向这个尺寸填入一个值,而NASM则故意不记这个符号除非它开始时,所以你必须用
这样的指令:mov word [var],2.
由于这个原因,NASM不支持LODS,MOVS,STOS,SCAS,CMPS,INS或OUTS指令,但支持表格处理如:LODSB,MOVSW和
SCASD,这些指令明确指定了处理字串的单位。
2.2.4 NASM不用ASSUME.
做为NASM简单性的一部分,它不支持ASSUME关键字,NASM不保留你将什么值放入段寄存器的动作.并且也
永远不会自动生成一个段重载前缀。
2.2.5 NASM不支持内存模型
NASM不支持任何提供16位内存模型的操作符。程序员必须保留哪个函数为远调用哪个函数为近调用。
对于放入RET指令的正确表格也是负责的。(RETN或RETF;NASM接受RET做为RETN表格的一个替换);另外,
程序员也必须自己处理CALL FAR指令对于当外部调用函数时,并且也必须保留外部变量是far 还是near的
信息。
2.2.6 浮点数的区别
NASM 用不同的名字引用浮点寄存器;MASM叫它们为ST(0),ST(1),a86叫它们0,1;NASM叫它们st0,st1;
2.2.7 其它区别
由于历史原因,NASM用关键字TWORD,而MASM和其它编译器用TBYTE。
NASM同MASM一样不定义未初始化的内存;为了读一个64字节的保留内容。MASM程序员用stack db 64 dup(?),
NASM则用stack resb 64,为了有限的兼容,NASM对待问号做为一个有效字符,你可以用? equ 0然后写
dw ?将会有效。然而DUP是不被支持的。
除了以上说明这些,宏和定向符完全和MASM不同,详细信息请见第四章和第五章。